More persistance

This commit is contained in:
Yuriy Glukhov 2019-01-25 16:17:35 +02:00 committed by zah
parent dda24619e8
commit 917c48d9a5
5 changed files with 77 additions and 28 deletions

View File

@ -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)

View File

@ -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:
# #

View File

@ -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)

View File

@ -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

View File

@ -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,