More persistance
This commit is contained in:
parent
dda24619e8
commit
917c48d9a5
|
@ -4,12 +4,12 @@ import
|
||||||
spec/[datatypes, digest, crypto],
|
spec/[datatypes, digest, crypto],
|
||||||
eth_trie/db, ssz
|
eth_trie/db, ssz
|
||||||
|
|
||||||
|
const STATE_STORAGE_PERIOD = 1000 # Save states once per this number of slots. TODO: Find a good number.
|
||||||
|
|
||||||
type
|
type
|
||||||
BeaconChainDB* = ref object
|
BeaconChainDB* = ref object
|
||||||
backend: TrieDatabaseRef
|
backend: TrieDatabaseRef
|
||||||
|
|
||||||
BeaconStateRef* = ref BeaconState
|
|
||||||
|
|
||||||
DbKey = object
|
DbKey = object
|
||||||
data: array[33, byte]
|
data: array[33, byte]
|
||||||
dataEndPos: uint8
|
dataEndPos: uint8
|
||||||
|
@ -17,6 +17,9 @@ type
|
||||||
DbKeyKind = enum
|
DbKeyKind = enum
|
||||||
kLastFinalizedState
|
kLastFinalizedState
|
||||||
kHashToBlock
|
kHashToBlock
|
||||||
|
kSlotToBlockHash
|
||||||
|
kSlotToState
|
||||||
|
kHashToValidatorRegistryChangeLog
|
||||||
|
|
||||||
|
|
||||||
template toOpenArray*(k: DbKey): openarray[byte] =
|
template toOpenArray*(k: DbKey): openarray[byte] =
|
||||||
|
@ -31,23 +34,67 @@ proc hashToBlockKey(h: Eth2Digest): DbKey =
|
||||||
result.data[1 .. ^1] = h.data
|
result.data[1 .. ^1] = h.data
|
||||||
result.dataEndPos = 32
|
result.dataEndPos = 32
|
||||||
|
|
||||||
|
proc slotToBlockHashKey(s: uint64): DbKey =
|
||||||
|
result.data[0] = byte ord(kSlotToBlockHash)
|
||||||
|
copyMem(addr result.data[1], unsafeAddr(s), sizeof(s))
|
||||||
|
result.dataEndPos = uint8 sizeof(s)
|
||||||
|
|
||||||
|
proc slotToStateKey(s: uint64): DbKey =
|
||||||
|
result.data[0] = byte ord(kSlotToState)
|
||||||
|
copyMem(addr result.data[1], unsafeAddr(s), sizeof(s))
|
||||||
|
result.dataEndPos = uint8 sizeof(s)
|
||||||
|
|
||||||
|
proc hashToValidatorRegistryChangeLogKey(deltaChainTip: Eth2Digest): DbKey =
|
||||||
|
result.data[0] = byte ord(kHashToValidatorRegistryChangeLog)
|
||||||
|
result.data[1 .. ^1] = deltaChainTip.data
|
||||||
|
result.dataEndPos = 32
|
||||||
|
|
||||||
proc init*(T: type BeaconChainDB, backend: TrieDatabaseRef): BeaconChainDB =
|
proc init*(T: type BeaconChainDB, backend: TrieDatabaseRef): BeaconChainDB =
|
||||||
new result
|
new result
|
||||||
result.backend = backend
|
result.backend = backend
|
||||||
|
|
||||||
proc lastFinalizedState*(db: BeaconChainDB): BeaconStateRef =
|
proc lastFinalizedState*(db: BeaconChainDB): BeaconState =
|
||||||
try:
|
|
||||||
let res = db.backend.get(lastFinalizedStateKey().toOpenArray)
|
let res = db.backend.get(lastFinalizedStateKey().toOpenArray)
|
||||||
if res.len != 0:
|
if res.len == 0:
|
||||||
result.new
|
raise newException(Exception, "Internal error: Database has no finalized state")
|
||||||
result[] = ssz.deserialize(res, BeaconState).get
|
ssz.deserialize(res, BeaconState).get
|
||||||
except:
|
|
||||||
error "Failed to load the latest finalized state",
|
proc isInitialized*(db: BeaconChainDB): bool =
|
||||||
err = getCurrentExceptionMsg()
|
db.backend.get(lastFinalizedStateKey().toOpenArray).len != 0
|
||||||
return nil
|
|
||||||
|
proc persistState*(db: BeaconChainDB, s: BeaconState) =
|
||||||
|
if s.slot != GENESIS_SLOT:
|
||||||
|
# TODO: Verify incoming state slot is higher than lastFinalizedState one
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
# Make sure we have no states
|
||||||
|
assert(not db.isInitialized)
|
||||||
|
|
||||||
|
var prevState: BeaconState
|
||||||
|
if s.slot != GENESIS_SLOT:
|
||||||
|
prevState = db.lastFinalizedState()
|
||||||
|
if prevState.validator_registry_delta_chain_tip != s.validator_registry_delta_chain_tip:
|
||||||
|
# Validator registry has changed in the incoming state.
|
||||||
|
# TODO: Save the changelog.
|
||||||
|
discard
|
||||||
|
|
||||||
|
let serializedState = ssz.serialize(s)
|
||||||
|
db.backend.put(lastFinalizedStateKey().toOpenArray, serializedState)
|
||||||
|
|
||||||
|
if s.slot mod STATE_STORAGE_PERIOD == 0:
|
||||||
|
# Save slot to state mapping
|
||||||
|
db.backend.put(slotToStateKey(s.slot).toOpenArray, serializedState)
|
||||||
|
|
||||||
proc persistBlock*(db: BeaconChainDB, s: BeaconState, b: BeaconBlock) =
|
proc persistBlock*(db: BeaconChainDB, s: BeaconState, b: BeaconBlock) =
|
||||||
db.backend.put(lastFinalizedStateKey().toOpenArray, ssz.serialize(s))
|
var prevState = db.lastFinalizedState()
|
||||||
|
|
||||||
|
db.persistState(s)
|
||||||
|
|
||||||
|
let blockHash = b.hash_tree_root_final
|
||||||
|
db.backend.put(hashToBlockKey(blockHash).toOpenArray, ssz.serialize(b))
|
||||||
|
db.backend.put(slotToBlockHashKey(b.slot).toOpenArray, blockHash.data)
|
||||||
|
|
||||||
|
# proc getValidatorChangeLog*(deltaChainTip: Eth2Digest)
|
||||||
|
|
||||||
proc getBlock*(db: BeaconChainDB, hash: Eth2Digest, output: var BeaconBlock): bool =
|
proc getBlock*(db: BeaconChainDB, hash: Eth2Digest, output: var BeaconBlock): bool =
|
||||||
let res = db.backend.get(hashToBlockKey(hash).toOpenArray)
|
let res = db.backend.get(hashToBlockKey(hash).toOpenArray)
|
||||||
|
|
|
@ -49,6 +49,12 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): T =
|
||||||
|
|
||||||
let trieDB = trieDB newChainDb(string conf.dataDir)
|
let trieDB = trieDB newChainDb(string conf.dataDir)
|
||||||
result.db = BeaconChainDB.init(trieDB)
|
result.db = BeaconChainDB.init(trieDB)
|
||||||
|
|
||||||
|
if not result.db.isInitialized:
|
||||||
|
# Use stateSnapshot as genesis
|
||||||
|
info "Initializing DB"
|
||||||
|
result.db.persistState(result.config.stateSnapshot.get)
|
||||||
|
|
||||||
result.keys = ensureNetworkKeys(string conf.dataDir)
|
result.keys = ensureNetworkKeys(string conf.dataDir)
|
||||||
|
|
||||||
var address: Address
|
var address: Address
|
||||||
|
@ -81,14 +87,10 @@ proc connectToNetwork(node: BeaconNode) {.async.} =
|
||||||
|
|
||||||
proc sync*(node: BeaconNode): Future[bool] {.async.} =
|
proc sync*(node: BeaconNode): Future[bool] {.async.} =
|
||||||
let persistedState = node.db.lastFinalizedState()
|
let persistedState = node.db.lastFinalizedState()
|
||||||
if persistedState.isNil or
|
if persistedState.slotDistanceFromNow() > WEAK_SUBJECTVITY_PERIOD.int64:
|
||||||
persistedState[].slotDistanceFromNow() > WEAK_SUBJECTVITY_PERIOD.int64:
|
|
||||||
if node.config.stateSnapshot.isSome:
|
|
||||||
node.beaconState = node.config.stateSnapshot.get
|
|
||||||
else:
|
|
||||||
node.beaconState = await obtainTrustedStateSnapshot(node.db)
|
node.beaconState = await obtainTrustedStateSnapshot(node.db)
|
||||||
else:
|
else:
|
||||||
node.beaconState = persistedState[]
|
node.beaconState = persistedState
|
||||||
var targetSlot = toSlot timeSinceGenesis(node.beaconState)
|
var targetSlot = toSlot timeSinceGenesis(node.beaconState)
|
||||||
|
|
||||||
let t = now()
|
let t = now()
|
||||||
|
@ -354,6 +356,7 @@ proc processBlocks*(node: BeaconNode) =
|
||||||
node.headBlock = newBlock
|
node.headBlock = newBlock
|
||||||
node.headBlockRoot = newBlockRoot
|
node.headBlockRoot = newBlockRoot
|
||||||
node.beaconState = state
|
node.beaconState = state
|
||||||
|
node.db.persistBlock(node.beaconState, newBlock)
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
#
|
#
|
||||||
|
|
|
@ -13,7 +13,7 @@ proc initRandao*(bytes: openarray[byte]): Randao =
|
||||||
raise newException(Exception, "Wrong randao size")
|
raise newException(Exception, "Wrong randao size")
|
||||||
var s: Eth2Digest
|
var s: Eth2Digest
|
||||||
s.data[0 .. ^1] = bytes
|
s.data[0 .. ^1] = bytes
|
||||||
initRandao(bytes)
|
initRandao(s)
|
||||||
|
|
||||||
proc initialCommitment*(r: Randao): Eth2Digest =
|
proc initialCommitment*(r: Randao): Eth2Digest =
|
||||||
repeatHash(r.seed, MaxRandaoLevels)
|
repeatHash(r.seed, MaxRandaoLevels)
|
||||||
|
|
|
@ -16,7 +16,11 @@ type
|
||||||
p2pProtocol BeaconSync(version = 1,
|
p2pProtocol BeaconSync(version = 1,
|
||||||
shortName = "bcs"):
|
shortName = "bcs"):
|
||||||
requestResponse:
|
requestResponse:
|
||||||
proc getValidatorChangeLog(peer: Peer, changeLogHead: Eth2Digest)
|
proc getValidatorChangeLog(peer: Peer, changeLogHead: Eth2Digest) =
|
||||||
|
var bb: BeaconBlock
|
||||||
|
var bs: BeaconState
|
||||||
|
# TODO: get the changelog from the DB.
|
||||||
|
await peer.validatorChangeLog(reqId, bb, bs, [], [], @[])
|
||||||
|
|
||||||
proc validatorChangeLog(peer: Peer,
|
proc validatorChangeLog(peer: Peer,
|
||||||
signedBlock: BeaconBlock,
|
signedBlock: BeaconBlock,
|
||||||
|
@ -25,11 +29,6 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
removed: openarray[uint32],
|
removed: openarray[uint32],
|
||||||
order: seq[byte])
|
order: seq[byte])
|
||||||
|
|
||||||
template `++`(x: var int): int =
|
|
||||||
let y = x
|
|
||||||
inc x
|
|
||||||
y
|
|
||||||
|
|
||||||
type
|
type
|
||||||
# A bit shorter names for convenience
|
# A bit shorter names for convenience
|
||||||
ChangeLog = BeaconSync.validatorChangeLog
|
ChangeLog = BeaconSync.validatorChangeLog
|
||||||
|
|
|
@ -27,7 +27,7 @@ proc obtainTrustedStateSnapshot*(db: BeaconChainDB): Future[BeaconState] {.async
|
||||||
#
|
#
|
||||||
# 5. Check that the state snapshot hash is correct and save it in the DB.
|
# 5. Check that the state snapshot hash is correct and save it in the DB.
|
||||||
|
|
||||||
discard
|
assert(false, "Not implemented")
|
||||||
|
|
||||||
proc createStateSnapshot*(startup: ChainStartupData, outFile: string) =
|
proc createStateSnapshot*(startup: ChainStartupData, outFile: string) =
|
||||||
let initialState = get_initial_beacon_state(startup.validatorDeposits,
|
let initialState = get_initial_beacon_state(startup.validatorDeposits,
|
||||||
|
|
Loading…
Reference in New Issue