initial 0.9.3 spec update
This commit is contained in:
parent
8c104a0b94
commit
c824416f56
|
@ -37,7 +37,7 @@ func subkey(kind: DbKeyKind, key: uint64): array[sizeof(key) + 1, byte] =
|
||||||
func subkey(kind: type BeaconState, key: Eth2Digest): auto =
|
func subkey(kind: type BeaconState, key: Eth2Digest): auto =
|
||||||
subkey(kHashToState, key.data)
|
subkey(kHashToState, key.data)
|
||||||
|
|
||||||
func subkey(kind: type BeaconBlock, key: Eth2Digest): auto =
|
func subkey(kind: type SignedBeaconBlock, key: Eth2Digest): auto =
|
||||||
subkey(kHashToBlock, key.data)
|
subkey(kHashToBlock, key.data)
|
||||||
|
|
||||||
func subkey(root: Eth2Digest, slot: Slot): auto =
|
func subkey(root: Eth2Digest, slot: Slot): auto =
|
||||||
|
@ -64,7 +64,7 @@ func subkey(root: Eth2Digest, slot: Slot): auto =
|
||||||
proc init*(T: type BeaconChainDB, backend: TrieDatabaseRef): BeaconChainDB =
|
proc init*(T: type BeaconChainDB, backend: TrieDatabaseRef): BeaconChainDB =
|
||||||
T(backend: backend)
|
T(backend: backend)
|
||||||
|
|
||||||
proc putBlock*(db: BeaconChainDB, key: Eth2Digest, value: BeaconBlock) =
|
proc putBlock*(db: BeaconChainDB, key: Eth2Digest, value: SignedBeaconBlock) =
|
||||||
db.backend.put(subkey(type value, key), SSZ.encode(value))
|
db.backend.put(subkey(type value, key), SSZ.encode(value))
|
||||||
|
|
||||||
proc putHead*(db: BeaconChainDB, key: Eth2Digest) =
|
proc putHead*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
|
@ -83,11 +83,11 @@ proc putStateRoot*(db: BeaconChainDB, root: Eth2Digest, slot: Slot,
|
||||||
value: Eth2Digest) =
|
value: Eth2Digest) =
|
||||||
db.backend.put(subkey(root, slot), value.data)
|
db.backend.put(subkey(root, slot), value.data)
|
||||||
|
|
||||||
proc putBlock*(db: BeaconChainDB, value: BeaconBlock) =
|
proc putBlock*(db: BeaconChainDB, value: SignedBeaconBlock) =
|
||||||
db.putBlock(signing_root(value), value)
|
db.putBlock(hash_tree_root(value.message), value)
|
||||||
|
|
||||||
proc delBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
proc delBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
db.backend.del(subkey(BeaconBlock, key))
|
db.backend.del(subkey(SignedBeaconBlock, key))
|
||||||
|
|
||||||
proc delState*(db: BeaconChainDB, key: Eth2Digest) =
|
proc delState*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
db.backend.del(subkey(BeaconState, key))
|
db.backend.del(subkey(BeaconState, key))
|
||||||
|
@ -108,10 +108,10 @@ proc get(db: BeaconChainDB, key: auto, T: typedesc): Option[T] =
|
||||||
else:
|
else:
|
||||||
none(T)
|
none(T)
|
||||||
|
|
||||||
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Option[BeaconBlock] =
|
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Option[SignedBeaconBlock] =
|
||||||
db.get(subkey(BeaconBlock, key), BeaconBlock)
|
db.get(subkey(SignedBeaconBlock, key), SignedBeaconBlock)
|
||||||
|
|
||||||
proc getBlock*(db: BeaconChainDB, slot: Slot): Option[BeaconBlock] =
|
proc getBlock*(db: BeaconChainDB, slot: Slot): Option[SignedBeaconBlock] =
|
||||||
# TODO implement this
|
# TODO implement this
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -130,14 +130,14 @@ proc getTailBlock*(db: BeaconChainDB): Option[Eth2Digest] =
|
||||||
|
|
||||||
proc containsBlock*(
|
proc containsBlock*(
|
||||||
db: BeaconChainDB, key: Eth2Digest): bool =
|
db: BeaconChainDB, key: Eth2Digest): bool =
|
||||||
db.backend.contains(subkey(BeaconBlock, key))
|
db.backend.contains(subkey(SignedBeaconBlock, key))
|
||||||
|
|
||||||
proc containsState*(
|
proc containsState*(
|
||||||
db: BeaconChainDB, key: Eth2Digest): bool =
|
db: BeaconChainDB, key: Eth2Digest): bool =
|
||||||
db.backend.contains(subkey(BeaconState, key))
|
db.backend.contains(subkey(BeaconState, key))
|
||||||
|
|
||||||
iterator getAncestors*(db: BeaconChainDB, root: Eth2Digest):
|
iterator getAncestors*(db: BeaconChainDB, root: Eth2Digest):
|
||||||
tuple[root: Eth2Digest, blck: BeaconBlock] =
|
tuple[root: Eth2Digest, blck: SignedBeaconBlock] =
|
||||||
## Load a chain of ancestors for blck - returns a list of blocks with the
|
## Load a chain of ancestors for blck - returns a list of blocks with the
|
||||||
## oldest block last (blck will be at result[0]).
|
## oldest block last (blck will be at result[0]).
|
||||||
##
|
##
|
||||||
|
@ -147,4 +147,4 @@ iterator getAncestors*(db: BeaconChainDB, root: Eth2Digest):
|
||||||
while (let blck = db.getBlock(root); blck.isSome()):
|
while (let blck = db.getBlock(root); blck.isSome()):
|
||||||
yield (root, blck.get())
|
yield (root, blck.get())
|
||||||
|
|
||||||
root = blck.get().parent_root
|
root = blck.get().message.parent_root
|
||||||
|
|
|
@ -68,7 +68,7 @@ type
|
||||||
## state replaying.
|
## state replaying.
|
||||||
# TODO Something smarter, so we don't need to keep two full copies, wasteful
|
# TODO Something smarter, so we don't need to keep two full copies, wasteful
|
||||||
|
|
||||||
proc onBeaconBlock*(node: BeaconNode, blck: BeaconBlock) {.gcsafe.}
|
proc onBeaconBlock*(node: BeaconNode, blck: SignedBeaconBlock) {.gcsafe.}
|
||||||
proc updateHead(node: BeaconNode): BlockRef
|
proc updateHead(node: BeaconNode): BlockRef
|
||||||
|
|
||||||
proc saveValidatorKey(keyName, key: string, conf: BeaconNodeConf) =
|
proc saveValidatorKey(keyName, key: string, conf: BeaconNodeConf) =
|
||||||
|
@ -218,8 +218,8 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
||||||
let node = result
|
let node = result
|
||||||
sync.init(
|
sync.init(
|
||||||
result.blockPool, result.forkVersion,
|
result.blockPool, result.forkVersion,
|
||||||
proc(blck: BeaconBlock) =
|
proc(signedBlock: SignedBeaconBlock) =
|
||||||
if blck.slot mod SLOTS_PER_EPOCH == 0:
|
if signedBlock.message.slot mod SLOTS_PER_EPOCH == 0:
|
||||||
# TODO this is a hack to make sure that lmd ghost is run regularly
|
# TODO this is a hack to make sure that lmd ghost is run regularly
|
||||||
# while syncing blocks - it's poor form to keep it here though -
|
# while syncing blocks - it's poor form to keep it here though -
|
||||||
# the logic should be moved elsewhere
|
# the logic should be moved elsewhere
|
||||||
|
@ -236,7 +236,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
||||||
# a potentially expensive head resolution?
|
# a potentially expensive head resolution?
|
||||||
discard node.updateHead()
|
discard node.updateHead()
|
||||||
|
|
||||||
onBeaconBlock(result, blck))
|
onBeaconBlock(result, signedBlock))
|
||||||
|
|
||||||
result.stateCache = result.blockPool.loadTailState()
|
result.stateCache = result.blockPool.loadTailState()
|
||||||
result.justifiedStateCache = result.stateCache
|
result.justifiedStateCache = result.stateCache
|
||||||
|
@ -452,21 +452,20 @@ proc proposeBlock(node: BeaconNode,
|
||||||
deposits: deposits)
|
deposits: deposits)
|
||||||
|
|
||||||
var
|
var
|
||||||
newBlock = BeaconBlock(
|
newBlock = SignedBeaconBlock(
|
||||||
slot: slot,
|
message: BeaconBlock(
|
||||||
parent_root: head.root,
|
slot: slot,
|
||||||
body: blockBody,
|
parent_root: head.root,
|
||||||
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
body: blockBody))
|
||||||
signature: ValidatorSig(kind: OpaqueBlob))
|
|
||||||
tmpState = hashedState
|
tmpState = hashedState
|
||||||
discard state_transition(tmpState, newBlock, {skipValidation})
|
discard state_transition(tmpState, newBlock.message, {skipValidation})
|
||||||
# TODO only enable in fast-fail debugging situations
|
# TODO only enable in fast-fail debugging situations
|
||||||
# otherwise, bad attestations can bring down network
|
# otherwise, bad attestations can bring down network
|
||||||
# doAssert ok # TODO: err, could this fail somehow?
|
# doAssert ok # TODO: err, could this fail somehow?
|
||||||
|
|
||||||
newBlock.state_root = tmpState.root
|
newBlock.message.state_root = tmpState.root
|
||||||
|
|
||||||
let blockRoot = signing_root(newBlock)
|
let blockRoot = hash_tree_root(newBlock.message)
|
||||||
|
|
||||||
# Careful, state no longer valid after here..
|
# Careful, state no longer valid after here..
|
||||||
# We use the fork from the pre-newBlock state which should be fine because
|
# We use the fork from the pre-newBlock state which should be fine because
|
||||||
|
@ -480,20 +479,20 @@ proc proposeBlock(node: BeaconNode,
|
||||||
let newBlockRef = node.blockPool.add(node.stateCache, nroot, nblck)
|
let newBlockRef = node.blockPool.add(node.stateCache, nroot, nblck)
|
||||||
if newBlockRef == nil:
|
if newBlockRef == nil:
|
||||||
warn "Unable to add proposed block to block pool",
|
warn "Unable to add proposed block to block pool",
|
||||||
newBlock = shortLog(newBlock),
|
newBlock = shortLog(newBlock.message),
|
||||||
blockRoot = shortLog(blockRoot),
|
blockRoot = shortLog(blockRoot),
|
||||||
cat = "bug"
|
cat = "bug"
|
||||||
return head
|
return head
|
||||||
|
|
||||||
info "Block proposed",
|
info "Block proposed",
|
||||||
blck = shortLog(newBlock),
|
blck = shortLog(newBlock.message),
|
||||||
blockRoot = shortLog(newBlockRef.root),
|
blockRoot = shortLog(newBlockRef.root),
|
||||||
validator = shortLog(validator),
|
validator = shortLog(validator),
|
||||||
cat = "consensus"
|
cat = "consensus"
|
||||||
|
|
||||||
if node.config.dump:
|
if node.config.dump:
|
||||||
SSZ.saveFile(
|
SSZ.saveFile(
|
||||||
node.config.dumpDir / "block-" & $newBlock.slot & "-" &
|
node.config.dumpDir / "block-" & $newBlock.message.slot & "-" &
|
||||||
shortLog(newBlockRef.root) & ".ssz", newBlock)
|
shortLog(newBlockRef.root) & ".ssz", newBlock)
|
||||||
SSZ.saveFile(
|
SSZ.saveFile(
|
||||||
node.config.dumpDir / "state-" & $tmpState.data.slot & "-" &
|
node.config.dumpDir / "state-" & $tmpState.data.slot & "-" &
|
||||||
|
@ -548,12 +547,12 @@ proc onAttestation(node: BeaconNode, attestation: Attestation) =
|
||||||
else:
|
else:
|
||||||
node.attestationPool.addUnresolved(attestation)
|
node.attestationPool.addUnresolved(attestation)
|
||||||
|
|
||||||
proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
|
proc onBeaconBlock(node: BeaconNode, blck: SignedBeaconBlock) =
|
||||||
# We received a block but don't know much about it yet - in particular, we
|
# We received a block but don't know much about it yet - in particular, we
|
||||||
# don't know if it's part of the chain we're currently building.
|
# don't know if it's part of the chain we're currently building.
|
||||||
let blockRoot = signing_root(blck)
|
let blockRoot = hash_tree_root(blck.message)
|
||||||
debug "Block received",
|
debug "Block received",
|
||||||
blck = shortLog(blck),
|
blck = shortLog(blck.message),
|
||||||
blockRoot = shortLog(blockRoot),
|
blockRoot = shortLog(blockRoot),
|
||||||
cat = "block_listener",
|
cat = "block_listener",
|
||||||
pcs = "receive_block"
|
pcs = "receive_block"
|
||||||
|
@ -570,8 +569,8 @@ proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
|
||||||
# TODO shouldn't add attestations if the block turns out to be invalid..
|
# TODO shouldn't add attestations if the block turns out to be invalid..
|
||||||
let currentSlot = node.beaconClock.now.toSlot
|
let currentSlot = node.beaconClock.now.toSlot
|
||||||
if currentSlot.afterGenesis and
|
if currentSlot.afterGenesis and
|
||||||
blck.slot.epoch + 1 >= currentSlot.slot.epoch:
|
blck.message.slot.epoch + 1 >= currentSlot.slot.epoch:
|
||||||
for attestation in blck.body.attestations:
|
for attestation in blck.message.body.attestations:
|
||||||
node.onAttestation(attestation)
|
node.onAttestation(attestation)
|
||||||
|
|
||||||
proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
||||||
|
@ -852,7 +851,7 @@ proc handleMissingBlocks(node: BeaconNode) =
|
||||||
var left = missingBlocks.len
|
var left = missingBlocks.len
|
||||||
|
|
||||||
info "Requesting detected missing blocks", missingBlocks
|
info "Requesting detected missing blocks", missingBlocks
|
||||||
node.requestManager.fetchAncestorBlocks(missingBlocks) do (b: BeaconBlock):
|
node.requestManager.fetchAncestorBlocks(missingBlocks) do (b: SignedBeaconBlock):
|
||||||
onBeaconBlock(node, b)
|
onBeaconBlock(node, b)
|
||||||
|
|
||||||
# TODO instead of waiting for a full second to try the next missing block
|
# TODO instead of waiting for a full second to try the next missing block
|
||||||
|
@ -874,8 +873,8 @@ proc onSecond(node: BeaconNode, moment: Moment) {.async.} =
|
||||||
asyncCheck node.onSecond(nextSecond)
|
asyncCheck node.onSecond(nextSecond)
|
||||||
|
|
||||||
proc run*(node: BeaconNode) =
|
proc run*(node: BeaconNode) =
|
||||||
waitFor node.network.subscribe(topicBeaconBlocks) do (blck: BeaconBlock):
|
waitFor node.network.subscribe(topicBeaconBlocks) do (signedBlock: SignedBeaconBlock):
|
||||||
onBeaconBlock(node, blck)
|
onBeaconBlock(node, signedBlock)
|
||||||
|
|
||||||
waitFor node.network.subscribe(topicAttestations) do (attestation: Attestation):
|
waitFor node.network.subscribe(topicAttestations) do (attestation: Attestation):
|
||||||
# Avoid double-counting attestation-topic attestations on shared codepath
|
# Avoid double-counting attestation-topic attestations on shared codepath
|
||||||
|
|
|
@ -106,7 +106,7 @@ type
|
||||||
## TODO evaluate the split of responsibilities between the two
|
## TODO evaluate the split of responsibilities between the two
|
||||||
## TODO prune the graph as tail moves
|
## TODO prune the graph as tail moves
|
||||||
|
|
||||||
pending*: Table[Eth2Digest, BeaconBlock] ##\
|
pending*: Table[Eth2Digest, SignedBeaconBlock] ##\
|
||||||
## Blocks that have passed validation but that we lack a link back to tail
|
## Blocks that have passed validation but that we lack a link back to tail
|
||||||
## for - when we receive a "missing link", we can use this data to build
|
## for - when we receive a "missing link", we can use this data to build
|
||||||
## an entire branch
|
## an entire branch
|
||||||
|
@ -161,7 +161,7 @@ type
|
||||||
BlockData* = object
|
BlockData* = object
|
||||||
## Body and graph in one
|
## Body and graph in one
|
||||||
|
|
||||||
data*: BeaconBlock
|
data*: SignedBeaconBlock
|
||||||
refs*: BlockRef
|
refs*: BlockRef
|
||||||
|
|
||||||
StateData* = object
|
StateData* = object
|
||||||
|
|
|
@ -73,7 +73,7 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
let
|
let
|
||||||
tailRoot = tailBlockRoot.get()
|
tailRoot = tailBlockRoot.get()
|
||||||
tailBlock = db.getBlock(tailRoot).get()
|
tailBlock = db.getBlock(tailRoot).get()
|
||||||
tailRef = BlockRef.init(tailRoot, tailBlock)
|
tailRef = BlockRef.init(tailRoot, tailBlock.message)
|
||||||
headRoot = headBlockRoot.get()
|
headRoot = headBlockRoot.get()
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -91,7 +91,7 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
curRef = curRef.parent
|
curRef = curRef.parent
|
||||||
break
|
break
|
||||||
|
|
||||||
let newRef = BlockRef.init(root, blck)
|
let newRef = BlockRef.init(root, blck.message)
|
||||||
if curRef == nil:
|
if curRef == nil:
|
||||||
curRef = newRef
|
curRef = newRef
|
||||||
headRef = newRef
|
headRef = newRef
|
||||||
|
@ -101,8 +101,8 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
blocks[curRef.root] = curRef
|
blocks[curRef.root] = curRef
|
||||||
trace "Populating block pool", key = curRef.root, val = curRef
|
trace "Populating block pool", key = curRef.root, val = curRef
|
||||||
|
|
||||||
if latestStateRoot.isNone() and db.containsState(blck.state_root):
|
if latestStateRoot.isNone() and db.containsState(blck.message.state_root):
|
||||||
latestStateRoot = some(blck.state_root)
|
latestStateRoot = some(blck.message.state_root)
|
||||||
|
|
||||||
doAssert curRef == tailRef,
|
doAssert curRef == tailRef,
|
||||||
"head block does not lead to tail, database corrupt?"
|
"head block does not lead to tail, database corrupt?"
|
||||||
|
@ -111,7 +111,7 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
|
|
||||||
var blocksBySlot = initTable[Slot, seq[BlockRef]]()
|
var blocksBySlot = initTable[Slot, seq[BlockRef]]()
|
||||||
for _, b in tables.pairs(blocks):
|
for _, b in tables.pairs(blocks):
|
||||||
let slot = db.getBlock(b.root).get().slot
|
let slot = db.getBlock(b.root).get().message.slot
|
||||||
blocksBySlot.mgetOrPut(slot, @[]).add(b)
|
blocksBySlot.mgetOrPut(slot, @[]).add(b)
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -121,7 +121,7 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
if latestStateRoot.isSome():
|
if latestStateRoot.isSome():
|
||||||
latestStateRoot.get()
|
latestStateRoot.get()
|
||||||
else:
|
else:
|
||||||
db.getBlock(tailRef.root).get().state_root
|
db.getBlock(tailRef.root).get().message.state_root
|
||||||
|
|
||||||
# TODO right now, because we save a state at every epoch, this *should*
|
# TODO right now, because we save a state at every epoch, this *should*
|
||||||
# be the latest justified state or newer, meaning it's enough for
|
# be the latest justified state or newer, meaning it's enough for
|
||||||
|
@ -144,7 +144,7 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
totalBlocks = blocks.len, totalKnownSlots = blocksBySlot.len
|
totalBlocks = blocks.len, totalKnownSlots = blocksBySlot.len
|
||||||
|
|
||||||
BlockPool(
|
BlockPool(
|
||||||
pending: initTable[Eth2Digest, BeaconBlock](),
|
pending: initTable[Eth2Digest, SignedBeaconBlock](),
|
||||||
missing: initTable[Eth2Digest, MissingBlock](),
|
missing: initTable[Eth2Digest, MissingBlock](),
|
||||||
blocks: blocks,
|
blocks: blocks,
|
||||||
blocksBySlot: blocksBySlot,
|
blocksBySlot: blocksBySlot,
|
||||||
|
@ -176,14 +176,14 @@ proc updateStateData*(
|
||||||
|
|
||||||
proc add*(
|
proc add*(
|
||||||
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
||||||
blck: BeaconBlock): BlockRef {.gcsafe.}
|
signedBlock: SignedBeaconBlock): BlockRef {.gcsafe.}
|
||||||
|
|
||||||
proc addResolvedBlock(
|
proc addResolvedBlock(
|
||||||
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
||||||
blck: BeaconBlock, parent: BlockRef): BlockRef =
|
signedBlock: SignedBeaconBlock, parent: BlockRef): BlockRef =
|
||||||
logScope: pcs = "block_resolution"
|
logScope: pcs = "block_resolution"
|
||||||
|
|
||||||
let blockRef = BlockRef.init(blockRoot, blck)
|
let blockRef = BlockRef.init(blockRoot, signedBlock.message)
|
||||||
link(parent, blockRef)
|
link(parent, blockRef)
|
||||||
|
|
||||||
pool.blocks[blockRoot] = blockRef
|
pool.blocks[blockRoot] = blockRef
|
||||||
|
@ -192,7 +192,7 @@ proc addResolvedBlock(
|
||||||
pool.addSlotMapping(blockRef)
|
pool.addSlotMapping(blockRef)
|
||||||
|
|
||||||
# Resolved blocks should be stored in database
|
# Resolved blocks should be stored in database
|
||||||
pool.db.putBlock(blockRoot, blck)
|
pool.db.putBlock(blockRoot, signedBlock)
|
||||||
|
|
||||||
# TODO this is a bit ugly - we update state.data outside of this function then
|
# TODO this is a bit ugly - we update state.data outside of this function then
|
||||||
# set the rest here - need a blockRef to update it. Clean this up -
|
# set the rest here - need a blockRef to update it. Clean this up -
|
||||||
|
@ -224,7 +224,7 @@ proc addResolvedBlock(
|
||||||
pool.heads.add(foundHead.get())
|
pool.heads.add(foundHead.get())
|
||||||
|
|
||||||
info "Block resolved",
|
info "Block resolved",
|
||||||
blck = shortLog(blck),
|
blck = shortLog(signedBlock.message),
|
||||||
blockRoot = shortLog(blockRoot),
|
blockRoot = shortLog(blockRoot),
|
||||||
justifiedRoot = shortLog(foundHead.get().justified.blck.root),
|
justifiedRoot = shortLog(foundHead.get().justified.blck.root),
|
||||||
justifiedSlot = shortLog(foundHead.get().justified.slot),
|
justifiedSlot = shortLog(foundHead.get().justified.slot),
|
||||||
|
@ -253,12 +253,13 @@ proc addResolvedBlock(
|
||||||
|
|
||||||
proc add*(
|
proc add*(
|
||||||
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
pool: var BlockPool, state: var StateData, blockRoot: Eth2Digest,
|
||||||
blck: BeaconBlock): BlockRef {.gcsafe.} =
|
signedBlock: SignedBeaconBlock): BlockRef {.gcsafe.} =
|
||||||
## return the block, if resolved...
|
## return the block, if resolved...
|
||||||
## the state parameter may be updated to include the given block, if
|
## the state parameter may be updated to include the given block, if
|
||||||
## everything checks out
|
## everything checks out
|
||||||
# TODO reevaluate passing the state in like this
|
# TODO reevaluate passing the state in like this
|
||||||
doAssert blockRoot == signing_root(blck)
|
let blck = signedBlock.message
|
||||||
|
doAssert blockRoot == hash_tree_root(blck)
|
||||||
|
|
||||||
logScope: pcs = "block_addition"
|
logScope: pcs = "block_addition"
|
||||||
|
|
||||||
|
@ -309,9 +310,13 @@ proc add*(
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
return pool.addResolvedBlock(state, blockRoot, blck, parent)
|
return pool.addResolvedBlock(state, blockRoot, signedBlock, parent)
|
||||||
|
|
||||||
pool.pending[blockRoot] = blck
|
# TODO already checked hash though? main reason to keep this is because
|
||||||
|
# the pending pool calls this function back later in a loop, so as long
|
||||||
|
# as pool.add(...) requires a SignedBeaconBlock, easier to keep them in
|
||||||
|
# pending too.
|
||||||
|
pool.pending[blockRoot] = signedBlock
|
||||||
|
|
||||||
# TODO possibly, it makes sense to check the database - that would allow sync
|
# TODO possibly, it makes sense to check the database - that would allow sync
|
||||||
# to simply fill up the database with random blocks the other clients
|
# to simply fill up the database with random blocks the other clients
|
||||||
|
@ -613,7 +618,7 @@ proc updateStateData*(pool: BlockPool, state: var StateData, bs: BlockSlot) =
|
||||||
# applied
|
# applied
|
||||||
for i in countdown(ancestors.len - 2, 0):
|
for i in countdown(ancestors.len - 2, 0):
|
||||||
let ok =
|
let ok =
|
||||||
skipAndUpdateState(state.data, ancestors[i].data, {skipValidation}) do(
|
skipAndUpdateState(state.data, ancestors[i].data.message, {skipValidation}) do(
|
||||||
state: HashedBeaconState):
|
state: HashedBeaconState):
|
||||||
pool.maybePutState(state, ancestors[i].refs)
|
pool.maybePutState(state, ancestors[i].refs)
|
||||||
doAssert ok, "Blocks in database should never fail to apply.."
|
doAssert ok, "Blocks in database should never fail to apply.."
|
||||||
|
@ -626,7 +631,7 @@ proc updateStateData*(pool: BlockPool, state: var StateData, bs: BlockSlot) =
|
||||||
|
|
||||||
proc loadTailState*(pool: BlockPool): StateData =
|
proc loadTailState*(pool: BlockPool): StateData =
|
||||||
## Load the state associated with the current tail in the pool
|
## Load the state associated with the current tail in the pool
|
||||||
let stateRoot = pool.db.getBlock(pool.tail.root).get().state_root
|
let stateRoot = pool.db.getBlock(pool.tail.root).get().message.state_root
|
||||||
StateData(
|
StateData(
|
||||||
data: HashedBeaconState(
|
data: HashedBeaconState(
|
||||||
data: pool.db.getState(stateRoot).get(),
|
data: pool.db.getState(stateRoot).get(),
|
||||||
|
@ -636,7 +641,7 @@ proc loadTailState*(pool: BlockPool): StateData =
|
||||||
|
|
||||||
proc delBlockAndState(pool: BlockPool, blockRoot: Eth2Digest) =
|
proc delBlockAndState(pool: BlockPool, blockRoot: Eth2Digest) =
|
||||||
if (let blk = pool.db.getBlock(blockRoot); blk.isSome):
|
if (let blk = pool.db.getBlock(blockRoot); blk.isSome):
|
||||||
pool.db.delState(blk.get.stateRoot)
|
pool.db.delState(blk.get.message.stateRoot)
|
||||||
pool.db.delBlock(blockRoot)
|
pool.db.delBlock(blockRoot)
|
||||||
|
|
||||||
proc delFinalizedStateIfNeeded(pool: BlockPool, b: BlockRef) =
|
proc delFinalizedStateIfNeeded(pool: BlockPool, b: BlockRef) =
|
||||||
|
@ -646,7 +651,7 @@ proc delFinalizedStateIfNeeded(pool: BlockPool, b: BlockRef) =
|
||||||
# so we don't need any of the finalized states, and thus remove all of them
|
# so we don't need any of the finalized states, and thus remove all of them
|
||||||
# (except the most recent)
|
# (except the most recent)
|
||||||
if (let blk = pool.db.getBlock(b.root); blk.isSome):
|
if (let blk = pool.db.getBlock(b.root); blk.isSome):
|
||||||
pool.db.delState(blk.get.stateRoot)
|
pool.db.delState(blk.get.message.stateRoot)
|
||||||
|
|
||||||
proc setTailBlock(pool: BlockPool, newTail: BlockRef) =
|
proc setTailBlock(pool: BlockPool, newTail: BlockRef) =
|
||||||
## Advance tail block, pruning all the states and blocks with older slots
|
## Advance tail block, pruning all the states and blocks with older slots
|
||||||
|
@ -800,24 +805,26 @@ func latestJustifiedBlock*(pool: BlockPool): BlockSlot =
|
||||||
result = head.justified
|
result = head.justified
|
||||||
|
|
||||||
proc preInit*(
|
proc preInit*(
|
||||||
T: type BlockPool, db: BeaconChainDB, state: BeaconState, blck: BeaconBlock) =
|
T: type BlockPool, db: BeaconChainDB, state: BeaconState,
|
||||||
|
signedBlock: SignedBeaconBlock) =
|
||||||
# write a genesis state, the way the BlockPool expects it to be stored in
|
# write a genesis state, the way the BlockPool expects it to be stored in
|
||||||
# database
|
# database
|
||||||
# TODO probably should just init a blockpool with the freshly written
|
# TODO probably should just init a blockpool with the freshly written
|
||||||
# state - but there's more refactoring needed to make it nice - doing
|
# state - but there's more refactoring needed to make it nice - doing
|
||||||
# a minimal patch for now..
|
# a minimal patch for now..
|
||||||
let
|
let
|
||||||
blockRoot = signing_root(blck)
|
blockRoot = hash_tree_root(signedBlock.message)
|
||||||
|
|
||||||
notice "New database from snapshot",
|
notice "New database from snapshot",
|
||||||
blockRoot = shortLog(blockRoot),
|
blockRoot = shortLog(blockRoot),
|
||||||
stateRoot = shortLog(blck.state_root),
|
stateRoot = shortLog(signedBlock.message.state_root),
|
||||||
fork = state.fork,
|
fork = state.fork,
|
||||||
validators = state.validators.len(),
|
validators = state.validators.len(),
|
||||||
cat = "initialization"
|
cat = "initialization"
|
||||||
|
|
||||||
db.putState(state)
|
db.putState(state)
|
||||||
db.putBlock(blck)
|
db.putBlock(signedBlock)
|
||||||
db.putTailBlock(blockRoot)
|
db.putTailBlock(blockRoot)
|
||||||
db.putHeadBlock(blockRoot)
|
db.putHeadBlock(blockRoot)
|
||||||
db.putStateRoot(blockRoot, blck.slot, blck.state_root)
|
db.putStateRoot(
|
||||||
|
blockRoot, signedBlock.message.slot, signedBlock.message.state_root)
|
||||||
|
|
|
@ -59,6 +59,6 @@ func makeDeposit*(
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
ret.data.signature =
|
ret.data.signature =
|
||||||
bls_sign(
|
bls_sign(
|
||||||
privkey, signing_root(ret.data).data, compute_domain(DOMAIN_DEPOSIT))
|
privkey, hash_tree_root(ret.data).data, compute_domain(DOMAIN_DEPOSIT))
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -13,7 +13,7 @@ proc init*(T: type RequestManager, network: Eth2Node): T =
|
||||||
T(network: network)
|
T(network: network)
|
||||||
|
|
||||||
type
|
type
|
||||||
FetchAncestorsResponseHandler = proc (b: BeaconBlock) {.gcsafe.}
|
FetchAncestorsResponseHandler = proc (b: SignedBeaconBlock) {.gcsafe.}
|
||||||
|
|
||||||
proc fetchAncestorBlocksFromPeer(
|
proc fetchAncestorBlocksFromPeer(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
|
|
|
@ -80,7 +80,7 @@ func process_deposit*(
|
||||||
if index == -1:
|
if index == -1:
|
||||||
# Verify the deposit signature (proof of possession)
|
# Verify the deposit signature (proof of possession)
|
||||||
if skipValidation notin flags and not bls_verify(
|
if skipValidation notin flags and not bls_verify(
|
||||||
pubkey, signing_root(deposit.data).data, deposit.data.signature,
|
pubkey, hash_tree_root(deposit.data).data, deposit.data.signature,
|
||||||
compute_domain(DOMAIN_DEPOSIT)):
|
compute_domain(DOMAIN_DEPOSIT)):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ proc slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex,
|
||||||
increase_balance(
|
increase_balance(
|
||||||
state, whistleblower_index, whistleblowing_reward - proposer_reward)
|
state, whistleblower_index, whistleblowing_reward - proposer_reward)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#genesis
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#genesis
|
||||||
func initialize_beacon_state_from_eth1*(
|
func initialize_beacon_state_from_eth1*(
|
||||||
eth1_block_hash: Eth2Digest,
|
eth1_block_hash: Eth2Digest,
|
||||||
eth1_timestamp: uint64,
|
eth1_timestamp: uint64,
|
||||||
|
@ -222,9 +222,7 @@ func initialize_beacon_state_from_eth1*(
|
||||||
BeaconBlockHeader(
|
BeaconBlockHeader(
|
||||||
body_root: hash_tree_root(BeaconBlockBody(
|
body_root: hash_tree_root(BeaconBlockBody(
|
||||||
randao_reveal: BlsValue[Signature](kind: OpaqueBlob)
|
randao_reveal: BlsValue[Signature](kind: OpaqueBlob)
|
||||||
)),
|
))
|
||||||
# TODO - Pure BLSSig cannot be zero: https://github.com/status-im/nim-beacon-chain/issues/374
|
|
||||||
signature: BlsValue[Signature](kind: OpaqueBlob)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -265,17 +263,16 @@ func is_valid_genesis_state*(state: BeaconState): bool =
|
||||||
|
|
||||||
# TODO this is now a non-spec helper function, and it's not really accurate
|
# TODO this is now a non-spec helper function, and it's not really accurate
|
||||||
# so only usable/used in research/ and tests/
|
# so only usable/used in research/ and tests/
|
||||||
func get_initial_beacon_block*(state: BeaconState): BeaconBlock =
|
func get_initial_beacon_block*(state: BeaconState): SignedBeaconBlock =
|
||||||
BeaconBlock(
|
SignedBeaconBlock(
|
||||||
slot: GENESIS_SLOT,
|
message: BeaconBlock(
|
||||||
state_root: hash_tree_root(state),
|
slot: GENESIS_SLOT,
|
||||||
body: BeaconBlockBody(
|
state_root: hash_tree_root(state),
|
||||||
|
body: BeaconBlockBody(
|
||||||
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
||||||
randao_reveal: BlsValue[Signature](kind: OpaqueBlob)),
|
randao_reveal: BlsValue[Signature](kind: OpaqueBlob))))
|
||||||
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
||||||
signature: BlsValue[Signature](kind: OpaqueBlob))
|
# initialized to default values.
|
||||||
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
|
||||||
# initialized to default values.
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#get_block_root_at_slot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#get_block_root_at_slot
|
||||||
func get_block_root_at_slot*(state: BeaconState,
|
func get_block_root_at_slot*(state: BeaconState,
|
||||||
|
@ -300,7 +297,24 @@ func get_total_balance*(state: BeaconState, validators: auto): Gwei =
|
||||||
)
|
)
|
||||||
|
|
||||||
# XXX: Move to state_transition_epoch.nim?
|
# XXX: Move to state_transition_epoch.nim?
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#registry-updates
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#is_eligible_for_activation_queue
|
||||||
|
func is_eligible_for_activation_queue(validator: Validator): bool =
|
||||||
|
# Check if ``validator`` is eligible to be placed into the activation queue.
|
||||||
|
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
|
||||||
|
validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#is_eligible_for_activation
|
||||||
|
func is_eligible_for_activation(state: BeaconState, validator: Validator):
|
||||||
|
bool =
|
||||||
|
# Check if ``validator`` is eligible for activation.
|
||||||
|
|
||||||
|
# Placement in queue is finalized
|
||||||
|
validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch and
|
||||||
|
# Has not yet been activated
|
||||||
|
validator.activation_epoch == FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#registry-updates
|
||||||
proc process_registry_updates*(state: var BeaconState) =
|
proc process_registry_updates*(state: var BeaconState) =
|
||||||
## Process activation eligibility and ejections
|
## Process activation eligibility and ejections
|
||||||
## Try to avoid caching here, since this could easily become undefined
|
## Try to avoid caching here, since this could easily become undefined
|
||||||
|
@ -315,10 +329,9 @@ proc process_registry_updates*(state: var BeaconState) =
|
||||||
epoch=epoch
|
epoch=epoch
|
||||||
|
|
||||||
for index, validator in state.validators:
|
for index, validator in state.validators:
|
||||||
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
|
if is_eligible_for_activation_queue(validator):
|
||||||
validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
|
||||||
state.validators[index].activation_eligibility_epoch =
|
state.validators[index].activation_eligibility_epoch =
|
||||||
get_current_epoch(state)
|
get_current_epoch(state) + 1
|
||||||
|
|
||||||
if is_active_validator(validator, get_current_epoch(state)) and
|
if is_active_validator(validator, get_current_epoch(state)) and
|
||||||
validator.effective_balance <= EJECTION_BALANCE:
|
validator.effective_balance <= EJECTION_BALANCE:
|
||||||
|
@ -333,12 +346,9 @@ proc process_registry_updates*(state: var BeaconState) =
|
||||||
initiate_validator_exit(state, index.ValidatorIndex)
|
initiate_validator_exit(state, index.ValidatorIndex)
|
||||||
|
|
||||||
## Queue validators eligible for activation and not dequeued for activation
|
## Queue validators eligible for activation and not dequeued for activation
|
||||||
## prior to finalized epoch
|
|
||||||
var activation_queue : seq[tuple[a: Epoch, b: int]] = @[]
|
var activation_queue : seq[tuple[a: Epoch, b: int]] = @[]
|
||||||
for index, validator in state.validators:
|
for index, validator in state.validators:
|
||||||
if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and
|
if is_eligible_for_activation(state, validator):
|
||||||
validator.activation_epoch >=
|
|
||||||
compute_activation_exit_epoch(state.finalized_checkpoint.epoch):
|
|
||||||
activation_queue.add (
|
activation_queue.add (
|
||||||
state.validators[index].activation_eligibility_epoch, index)
|
state.validators[index].activation_eligibility_epoch, index)
|
||||||
|
|
||||||
|
@ -353,9 +363,8 @@ proc process_registry_updates*(state: var BeaconState) =
|
||||||
let
|
let
|
||||||
(_, index) = epoch_and_index
|
(_, index) = epoch_and_index
|
||||||
validator = addr state.validators[index]
|
validator = addr state.validators[index]
|
||||||
if validator.activation_epoch == FAR_FUTURE_EPOCH:
|
validator.activation_epoch =
|
||||||
validator.activation_epoch =
|
compute_activation_exit_epoch(get_current_epoch(state))
|
||||||
compute_activation_exit_epoch(get_current_epoch(state))
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#is_valid_indexed_attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#is_valid_indexed_attestation
|
||||||
proc is_valid_indexed_attestation*(
|
proc is_valid_indexed_attestation*(
|
||||||
|
|
|
@ -52,7 +52,7 @@ else:
|
||||||
{.fatal: "Preset \"" & const_preset ".nim\" is not supported.".}
|
{.fatal: "Preset \"" & const_preset ".nim\" is not supported.".}
|
||||||
|
|
||||||
const
|
const
|
||||||
SPEC_VERSION* = "0.9.2" ## \
|
SPEC_VERSION* = "0.9.3" ## \
|
||||||
## Spec version we're aiming to be compatible with, right now
|
## Spec version we're aiming to be compatible with, right now
|
||||||
## TODO: improve this scheme once we can negotiate versions in protocol
|
## TODO: improve this scheme once we can negotiate versions in protocol
|
||||||
|
|
||||||
|
@ -110,8 +110,8 @@ type
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#proposerslashing
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#proposerslashing
|
||||||
ProposerSlashing* = object
|
ProposerSlashing* = object
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64
|
||||||
header_1*: BeaconBlockHeader
|
signed_header_1*: SignedBeaconBlockHeader
|
||||||
header_2*: BeaconBlockHeader
|
signed_header_2*: SignedBeaconBlockHeader
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#attesterslashing
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#attesterslashing
|
||||||
AttesterSlashing* = object
|
AttesterSlashing* = object
|
||||||
|
@ -157,12 +157,18 @@ type
|
||||||
|
|
||||||
data*: DepositData
|
data*: DepositData
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#depositdata
|
||||||
|
DepositMessage* = object
|
||||||
|
pubkey*: ValidatorPubKey
|
||||||
|
withdrawal_credentials*: Eth2Digest
|
||||||
|
amount*: Gwei
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#depositdata
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#depositdata
|
||||||
DepositData* = object
|
DepositData* = object
|
||||||
pubkey*: ValidatorPubKey
|
pubkey*: ValidatorPubKey
|
||||||
withdrawal_credentials*: Eth2Digest
|
withdrawal_credentials*: Eth2Digest
|
||||||
amount*: uint64
|
amount*: uint64
|
||||||
signature*: ValidatorSig
|
signature*: ValidatorSig # signing over DepositMessage
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#voluntaryexit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#voluntaryexit
|
||||||
VoluntaryExit* = object
|
VoluntaryExit* = object
|
||||||
|
@ -170,7 +176,6 @@ type
|
||||||
## Earliest epoch when voluntary exit can be processed
|
## Earliest epoch when voluntary exit can be processed
|
||||||
|
|
||||||
validator_index*: uint64
|
validator_index*: uint64
|
||||||
signature*: ValidatorSig
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblock
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblock
|
||||||
BeaconBlock* = object
|
BeaconBlock* = object
|
||||||
|
@ -190,16 +195,12 @@ type
|
||||||
|
|
||||||
body*: BeaconBlockBody
|
body*: BeaconBlockBody
|
||||||
|
|
||||||
signature*: ValidatorSig ##\
|
|
||||||
## Proposer signature
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblockheader
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblockheader
|
||||||
BeaconBlockHeader* = object
|
BeaconBlockHeader* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
parent_root*: Eth2Digest
|
parent_root*: Eth2Digest
|
||||||
state_root*: Eth2Digest
|
state_root*: Eth2Digest
|
||||||
body_root*: Eth2Digest
|
body_root*: Eth2Digest
|
||||||
signature*: ValidatorSig
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblockbody
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconblockbody
|
||||||
BeaconBlockBody* = object
|
BeaconBlockBody* = object
|
||||||
|
@ -212,7 +213,7 @@ type
|
||||||
attester_slashings*: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
|
attester_slashings*: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
|
||||||
attestations*: List[Attestation, MAX_ATTESTATIONS]
|
attestations*: List[Attestation, MAX_ATTESTATIONS]
|
||||||
deposits*: List[Deposit, MAX_DEPOSITS]
|
deposits*: List[Deposit, MAX_DEPOSITS]
|
||||||
voluntary_exits*: List[VoluntaryExit, MAX_VOLUNTARY_EXITS]
|
voluntary_exits*: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconstate
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beaconstate
|
||||||
BeaconState* = object
|
BeaconState* = object
|
||||||
|
@ -320,6 +321,21 @@ type
|
||||||
deposit_count*: uint64
|
deposit_count*: uint64
|
||||||
block_hash*: Eth2Digest
|
block_hash*: Eth2Digest
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#signedvoluntaryexit
|
||||||
|
SignedVoluntaryExit* = object
|
||||||
|
message*: VoluntaryExit
|
||||||
|
signature*: ValidatorSig
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#signedbeaconblock
|
||||||
|
SignedBeaconBlock* = object
|
||||||
|
message*: BeaconBlock
|
||||||
|
signature*: ValidatorSig
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#signedvoluntaryexit
|
||||||
|
SignedBeaconBlockHeader* = object
|
||||||
|
message*: BeaconBlockHeader
|
||||||
|
signature*: ValidatorSig
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/validator/0_beacon-chain-validator.md#aggregateandproof
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/validator/0_beacon-chain-validator.md#aggregateandproof
|
||||||
AggregateAndProof* = object
|
AggregateAndProof* = object
|
||||||
aggregator_index*: uint64
|
aggregator_index*: uint64
|
||||||
|
@ -352,6 +368,7 @@ template foreachSpecType*(op: untyped) =
|
||||||
## These are all spec types that will appear in network messages
|
## These are all spec types that will appear in network messages
|
||||||
## and persistent consensus data. This helper template is useful
|
## and persistent consensus data. This helper template is useful
|
||||||
## for populating RTTI tables that concern them.
|
## for populating RTTI tables that concern them.
|
||||||
|
op AggregateAndProof
|
||||||
op Attestation
|
op Attestation
|
||||||
op AttestationData
|
op AttestationData
|
||||||
op AttesterSlashing
|
op AttesterSlashing
|
||||||
|
@ -367,6 +384,9 @@ template foreachSpecType*(op: untyped) =
|
||||||
op IndexedAttestation
|
op IndexedAttestation
|
||||||
op PendingAttestation
|
op PendingAttestation
|
||||||
op ProposerSlashing
|
op ProposerSlashing
|
||||||
|
op SignedBeaconBlock
|
||||||
|
op SignedBeaconBlockHeader
|
||||||
|
op SignedVoluntaryExit
|
||||||
op Validator
|
op Validator
|
||||||
op VoluntaryExit
|
op VoluntaryExit
|
||||||
|
|
||||||
|
@ -553,7 +573,6 @@ func shortLog*(v: BeaconBlock): auto =
|
||||||
attestations_len: v.body.attestations.len(),
|
attestations_len: v.body.attestations.len(),
|
||||||
deposits_len: v.body.deposits.len(),
|
deposits_len: v.body.deposits.len(),
|
||||||
voluntary_exits_len: v.body.voluntary_exits.len(),
|
voluntary_exits_len: v.body.voluntary_exits.len(),
|
||||||
signature: shortLog(v.signature)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func shortLog*(v: AttestationData): auto =
|
func shortLog*(v: AttestationData): auto =
|
||||||
|
|
|
@ -43,7 +43,7 @@ declareGauge beacon_previous_live_validators, "Number of active validators that
|
||||||
declareGauge beacon_pending_deposits, "Number of pending deposits (state.eth1_data.deposit_count - state.eth1_deposit_index)" # On block
|
declareGauge beacon_pending_deposits, "Number of pending deposits (state.eth1_data.deposit_count - state.eth1_deposit_index)" # On block
|
||||||
declareGauge beacon_processed_deposits_total, "Number of total deposits included on chain" # On block
|
declareGauge beacon_processed_deposits_total, "Number of total deposits included on chain" # On block
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#block-header
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#block-header
|
||||||
proc process_block_header*(
|
proc process_block_header*(
|
||||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||||
stateCache: var StateCache): bool =
|
stateCache: var StateCache): bool =
|
||||||
|
@ -56,13 +56,13 @@ proc process_block_header*(
|
||||||
|
|
||||||
# Verify that the parent matches
|
# Verify that the parent matches
|
||||||
if skipValidation notin flags and not (blck.parent_root ==
|
if skipValidation notin flags and not (blck.parent_root ==
|
||||||
signing_root(state.latest_block_header)):
|
hash_tree_root(state.latest_block_header)):
|
||||||
# TODO: skip validation is too strong
|
# TODO: skip validation is too strong
|
||||||
# can't do "invalid_parent_root" test
|
# can't do "invalid_parent_root" test
|
||||||
notice "Block header: previous block root mismatch",
|
notice "Block header: previous block root mismatch",
|
||||||
latest_block_header = state.latest_block_header,
|
latest_block_header = state.latest_block_header,
|
||||||
blck = shortLog(blck),
|
blck = shortLog(blck),
|
||||||
latest_block_header_root = shortLog(signing_root(state.latest_block_header))
|
latest_block_header_root = shortLog(hash_tree_root(state.latest_block_header))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Save current block as the new latest block
|
# Save current block as the new latest block
|
||||||
|
@ -71,9 +71,6 @@ proc process_block_header*(
|
||||||
parent_root: blck.parent_root,
|
parent_root: blck.parent_root,
|
||||||
# state_root: zeroed, overwritten in the next `process_slot` call
|
# state_root: zeroed, overwritten in the next `process_slot` call
|
||||||
body_root: hash_tree_root(blck.body),
|
body_root: hash_tree_root(blck.body),
|
||||||
# signature is always zeroed
|
|
||||||
# TODO - Pure BLSSig cannot be zero: https://github.com/status-im/nim-beacon-chain/issues/374
|
|
||||||
signature: BlsValue[Signature](kind: OpaqueBlob)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify proposer is not slashed
|
# Verify proposer is not slashed
|
||||||
|
@ -87,18 +84,6 @@ proc process_block_header*(
|
||||||
notice "Block header: proposer slashed"
|
notice "Block header: proposer slashed"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Verify proposer signature
|
|
||||||
if skipValidation notin flags and not bls_verify(
|
|
||||||
proposer.pubkey,
|
|
||||||
signing_root(blck).data,
|
|
||||||
blck.signature,
|
|
||||||
get_domain(state, DOMAIN_BEACON_PROPOSER)):
|
|
||||||
notice "Block header: invalid block header",
|
|
||||||
proposer_pubkey = proposer.pubkey,
|
|
||||||
block_root = shortLog(signing_root(blck)),
|
|
||||||
block_signature = blck.signature
|
|
||||||
return false
|
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#randao
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#randao
|
||||||
|
@ -153,7 +138,7 @@ func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
|
||||||
(validator.activation_epoch <= epoch) and
|
(validator.activation_epoch <= epoch) and
|
||||||
(epoch < validator.withdrawable_epoch)
|
(epoch < validator.withdrawable_epoch)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#proposer-slashings
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#proposer-slashings
|
||||||
proc process_proposer_slashing*(
|
proc process_proposer_slashing*(
|
||||||
state: var BeaconState, proposer_slashing: ProposerSlashing,
|
state: var BeaconState, proposer_slashing: ProposerSlashing,
|
||||||
flags: UpdateFlags, stateCache: var StateCache): bool =
|
flags: UpdateFlags, stateCache: var StateCache): bool =
|
||||||
|
@ -164,13 +149,14 @@ proc process_proposer_slashing*(
|
||||||
let proposer = state.validators[proposer_slashing.proposer_index.int]
|
let proposer = state.validators[proposer_slashing.proposer_index.int]
|
||||||
|
|
||||||
# Verify slots match
|
# Verify slots match
|
||||||
if not (proposer_slashing.header_1.slot ==
|
if not (proposer_slashing.signed_header_1.message.slot ==
|
||||||
proposer_slashing.header_2.slot):
|
proposer_slashing.signed_header_2.message.slot):
|
||||||
notice "Proposer slashing: slot mismatch"
|
notice "Proposer slashing: slot mismatch"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# But the headers are different
|
# But the headers are different
|
||||||
if not (proposer_slashing.header_1 != proposer_slashing.header_2):
|
if not (proposer_slashing.signed_header_1.message !=
|
||||||
|
proposer_slashing.signed_header_2.message):
|
||||||
notice "Proposer slashing: headers not different"
|
notice "Proposer slashing: headers not different"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -181,13 +167,15 @@ proc process_proposer_slashing*(
|
||||||
|
|
||||||
# Signatures are valid
|
# Signatures are valid
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
for i, header in [proposer_slashing.header_1, proposer_slashing.header_2]:
|
for i, signed_header in [proposer_slashing.signed_header_1,
|
||||||
|
proposer_slashing.signed_header_2]:
|
||||||
if not bls_verify(
|
if not bls_verify(
|
||||||
proposer.pubkey,
|
proposer.pubkey,
|
||||||
signing_root(header).data,
|
hash_tree_root(signed_header.message).data,
|
||||||
header.signature,
|
signed_header.signature,
|
||||||
get_domain(
|
get_domain(
|
||||||
state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))):
|
state, DOMAIN_BEACON_PROPOSER,
|
||||||
|
compute_epoch_at_slot(signed_header.message.slot))):
|
||||||
notice "Proposer slashing: invalid signature",
|
notice "Proposer slashing: invalid signature",
|
||||||
signature_index = i
|
signature_index = i
|
||||||
return false
|
return false
|
||||||
|
@ -312,20 +300,22 @@ proc processDeposits(state: var BeaconState, blck: BeaconBlock): bool =
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#voluntary-exits
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#voluntary-exits
|
||||||
proc process_voluntary_exit*(
|
proc process_voluntary_exit*(
|
||||||
state: var BeaconState,
|
state: var BeaconState,
|
||||||
exit: VoluntaryExit,
|
signed_voluntary_exit: SignedVoluntaryExit,
|
||||||
flags: UpdateFlags): bool =
|
flags: UpdateFlags): bool =
|
||||||
|
|
||||||
|
let voluntary_exit = signed_voluntary_exit.message
|
||||||
|
|
||||||
# Not in spec. Check that validator_index is in range
|
# Not in spec. Check that validator_index is in range
|
||||||
if exit.validator_index.int >= state.validators.len:
|
if voluntary_exit.validator_index.int >= state.validators.len:
|
||||||
notice "Exit: invalid validator index",
|
notice "Exit: invalid validator index",
|
||||||
index = exit.validator_index,
|
index = voluntary_exit.validator_index,
|
||||||
num_validators = state.validators.len
|
num_validators = state.validators.len
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let validator = state.validators[exit.validator_index.int]
|
let validator = state.validators[voluntary_exit.validator_index.int]
|
||||||
|
|
||||||
# Verify the validator is active
|
# Verify the validator is active
|
||||||
if not is_active_validator(validator, get_current_epoch(state)):
|
if not is_active_validator(validator, get_current_epoch(state)):
|
||||||
|
@ -339,7 +329,7 @@ proc process_voluntary_exit*(
|
||||||
|
|
||||||
## Exits must specify an epoch when they become valid; they are not valid
|
## Exits must specify an epoch when they become valid; they are not valid
|
||||||
## before then
|
## before then
|
||||||
if not (get_current_epoch(state) >= exit.epoch):
|
if not (get_current_epoch(state) >= voluntary_exit.epoch):
|
||||||
notice "Exit: exit epoch not passed"
|
notice "Exit: exit epoch not passed"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -351,26 +341,26 @@ proc process_voluntary_exit*(
|
||||||
|
|
||||||
# Verify signature
|
# Verify signature
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
let domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
|
let domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
||||||
if not bls_verify(
|
if not bls_verify(
|
||||||
validator.pubkey,
|
validator.pubkey,
|
||||||
signing_root(exit).data,
|
hash_tree_root(voluntary_exit).data,
|
||||||
exit.signature,
|
signed_voluntary_exit.signature,
|
||||||
domain):
|
domain):
|
||||||
notice "Exit: invalid signature"
|
notice "Exit: invalid signature"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Initiate exit
|
# Initiate exit
|
||||||
debug "Exit: processing voluntary exit (validator_leaving)",
|
debug "Exit: processing voluntary exit (validator_leaving)",
|
||||||
index = exit.validator_index,
|
index = voluntary_exit.validator_index,
|
||||||
num_validators = state.validators.len,
|
num_validators = state.validators.len,
|
||||||
epoch = exit.epoch,
|
epoch = voluntary_exit.epoch,
|
||||||
current_epoch = get_current_epoch(state),
|
current_epoch = get_current_epoch(state),
|
||||||
validator_slashed = validator.slashed,
|
validator_slashed = validator.slashed,
|
||||||
validator_withdrawable_epoch = validator.withdrawable_epoch,
|
validator_withdrawable_epoch = validator.withdrawable_epoch,
|
||||||
validator_exit_epoch = validator.exit_epoch,
|
validator_exit_epoch = validator.exit_epoch,
|
||||||
validator_effective_balance = validator.effective_balance
|
validator_effective_balance = validator.effective_balance
|
||||||
initiate_validator_exit(state, exit.validator_index.ValidatorIndex)
|
initiate_validator_exit(state, voluntary_exit.validator_index.ValidatorIndex)
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
|
|
|
@ -557,6 +557,8 @@ func maxChunksCount(T: type, maxLen: static int64): int64 {.compileTime.} =
|
||||||
func hash_tree_root*(x: auto): Eth2Digest =
|
func hash_tree_root*(x: auto): Eth2Digest =
|
||||||
trs "STARTING HASH TREE ROOT FOR TYPE ", name(type(x))
|
trs "STARTING HASH TREE ROOT FOR TYPE ", name(type(x))
|
||||||
mixin toSszType
|
mixin toSszType
|
||||||
|
when x is SignedBeaconBlock:
|
||||||
|
doassert false
|
||||||
when x is TypeWithMaxLen:
|
when x is TypeWithMaxLen:
|
||||||
const maxLen = x.maxLen
|
const maxLen = x.maxLen
|
||||||
type T = type valueOf(x)
|
type T = type valueOf(x)
|
||||||
|
@ -593,14 +595,3 @@ iterator hash_tree_roots_prefix*[T](lst: openarray[T], limit: auto):
|
||||||
for i, elem in lst:
|
for i, elem in lst:
|
||||||
merkelizer.addChunk(hash_tree_root(elem).data)
|
merkelizer.addChunk(hash_tree_root(elem).data)
|
||||||
yield mixInLength(merkelizer.getFinalHash(), i + 1)
|
yield mixInLength(merkelizer.getFinalHash(), i + 1)
|
||||||
|
|
||||||
func lastFieldName(RecordType: type): string {.compileTime.} =
|
|
||||||
enumAllSerializedFields(RecordType):
|
|
||||||
result = fieldName
|
|
||||||
|
|
||||||
func signingRoot*(obj: object): Eth2Digest =
|
|
||||||
const lastField = lastFieldName(obj.type)
|
|
||||||
merkelizeFields:
|
|
||||||
obj.enumInstanceSerializedFields(fieldName, field):
|
|
||||||
when fieldName != lastField:
|
|
||||||
addField2 field
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ func process_slot*(state: var BeaconState) =
|
||||||
|
|
||||||
# Cache block root
|
# Cache block root
|
||||||
state.block_roots[state.slot mod SLOTS_PER_HISTORICAL_ROOT] =
|
state.block_roots[state.slot mod SLOTS_PER_HISTORICAL_ROOT] =
|
||||||
signing_root(state.latest_block_header)
|
hash_tree_root(state.latest_block_header)
|
||||||
|
|
||||||
func get_epoch_validator_count(state: BeaconState): int64 =
|
func get_epoch_validator_count(state: BeaconState): int64 =
|
||||||
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#additional-metrics
|
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#additional-metrics
|
||||||
|
@ -183,7 +183,7 @@ func process_slot(state: var HashedBeaconState) =
|
||||||
|
|
||||||
# Cache block root
|
# Cache block root
|
||||||
state.data.block_roots[state.data.slot mod SLOTS_PER_HISTORICAL_ROOT] =
|
state.data.block_roots[state.data.slot mod SLOTS_PER_HISTORICAL_ROOT] =
|
||||||
signing_root(state.data.latest_block_header)
|
hash_tree_root(state.data.latest_block_header)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function
|
||||||
proc process_slots*(state: var HashedBeaconState, slot: Slot) =
|
proc process_slots*(state: var HashedBeaconState, slot: Slot) =
|
||||||
|
|
|
@ -27,7 +27,7 @@ type
|
||||||
else:
|
else:
|
||||||
index: uint32
|
index: uint32
|
||||||
|
|
||||||
BeaconBlockCallback* = proc(blck: BeaconBlock) {.gcsafe.}
|
BeaconBlockCallback* = proc(blck: SignedBeaconBlock) {.gcsafe.}
|
||||||
BeaconSyncNetworkState* = ref object
|
BeaconSyncNetworkState* = ref object
|
||||||
blockPool*: BlockPool
|
blockPool*: BlockPool
|
||||||
forkVersion*: array[4, byte]
|
forkVersion*: array[4, byte]
|
||||||
|
@ -51,7 +51,7 @@ func init*(
|
||||||
v.onBeaconBlock = onBeaconBlock
|
v.onBeaconBlock = onBeaconBlock
|
||||||
|
|
||||||
proc importBlocks(state: BeaconSyncNetworkState,
|
proc importBlocks(state: BeaconSyncNetworkState,
|
||||||
blocks: openarray[BeaconBlock]) {.gcsafe.} =
|
blocks: openarray[SignedBeaconBlock]) {.gcsafe.} =
|
||||||
for blk in blocks:
|
for blk in blocks:
|
||||||
state.onBeaconBlock(blk)
|
state.onBeaconBlock(blk)
|
||||||
info "Forward sync imported blocks", len = blocks.len
|
info "Forward sync imported blocks", len = blocks.len
|
||||||
|
@ -158,7 +158,7 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
|
|
||||||
proc beaconBlocks(
|
proc beaconBlocks(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
blocks: openarray[BeaconBlock])
|
blocks: openarray[SignedBeaconBlock])
|
||||||
|
|
||||||
proc handleInitialStatus(peer: Peer,
|
proc handleInitialStatus(peer: Peer,
|
||||||
state: BeaconSyncNetworkState,
|
state: BeaconSyncNetworkState,
|
||||||
|
@ -221,7 +221,7 @@ proc handleInitialStatus(peer: Peer,
|
||||||
break
|
break
|
||||||
|
|
||||||
state.importBlocks(blocks.get)
|
state.importBlocks(blocks.get)
|
||||||
let lastSlot = blocks.get[^1].slot
|
let lastSlot = blocks.get[^1].message.slot
|
||||||
if lastSlot <= s:
|
if lastSlot <= s:
|
||||||
info "Slot did not advance during sync", peer
|
info "Slot did not advance during sync", peer
|
||||||
break
|
break
|
||||||
|
|
|
@ -51,11 +51,11 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||||
var
|
var
|
||||||
attestations = initTable[Slot, seq[Attestation]]()
|
attestations = initTable[Slot, seq[Attestation]]()
|
||||||
state = genesisState
|
state = genesisState
|
||||||
latest_block_root = signing_root(genesisBlock)
|
latest_block_root = hash_tree_root(genesisBlock.message)
|
||||||
timers: array[Timers, RunningStat]
|
timers: array[Timers, RunningStat]
|
||||||
attesters: RunningStat
|
attesters: RunningStat
|
||||||
r: Rand
|
r: Rand
|
||||||
blck: BeaconBlock
|
blck: SignedBeaconBlock
|
||||||
cache = get_empty_per_epoch_cache()
|
cache = get_empty_per_epoch_cache()
|
||||||
|
|
||||||
proc maybeWrite() =
|
proc maybeWrite() =
|
||||||
|
@ -90,7 +90,7 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
||||||
withTimer(timers[t]):
|
withTimer(timers[t]):
|
||||||
blck = addBlock(state, latest_block_root, body, flags)
|
blck = addBlock(state, latest_block_root, body, flags)
|
||||||
latest_block_root = withTimerRet(timers[tHashBlock]):
|
latest_block_root = withTimerRet(timers[tHashBlock]):
|
||||||
signing_root(blck)
|
hash_tree_root(blck.message)
|
||||||
|
|
||||||
if attesterRatio > 0.0:
|
if attesterRatio > 0.0:
|
||||||
# attesterRatio is the fraction of attesters that actually do their
|
# attesterRatio is the fraction of attesters that actually do their
|
||||||
|
|
|
@ -17,7 +17,7 @@ import # Unit test
|
||||||
./test_beaconstate,
|
./test_beaconstate,
|
||||||
./test_block_pool,
|
./test_block_pool,
|
||||||
./test_helpers,
|
./test_helpers,
|
||||||
./test_interop,
|
#./test_interop, TODO check zcli
|
||||||
./test_ssz,
|
./test_ssz,
|
||||||
./test_state_transition,
|
./test_state_transition,
|
||||||
./test_sync_protocol,
|
./test_sync_protocol,
|
||||||
|
|
|
@ -26,7 +26,7 @@ proc mockAttestationData(
|
||||||
doAssert state.slot >= slot
|
doAssert state.slot >= slot
|
||||||
|
|
||||||
if slot == state.slot:
|
if slot == state.slot:
|
||||||
result.beacon_block_root = mockBlockForNextSlot(state).parent_root
|
result.beacon_block_root = mockBlockForNextSlot(state).message.parent_root
|
||||||
else:
|
else:
|
||||||
result.beacon_block_root = get_block_root_at_slot(state, slot)
|
result.beacon_block_root = get_block_root_at_slot(state, slot)
|
||||||
|
|
||||||
|
@ -140,11 +140,12 @@ proc fillAggregateAttestation*(state: BeaconState, attestation: var Attestation)
|
||||||
attestation.aggregation_bits[i] = true
|
attestation.aggregation_bits[i] = true
|
||||||
|
|
||||||
proc add*(state: var BeaconState, attestation: Attestation, slot: Slot) =
|
proc add*(state: var BeaconState, attestation: Attestation, slot: Slot) =
|
||||||
var blck = mockBlockForNextSlot(state)
|
var signedBlock = mockBlockForNextSlot(state)
|
||||||
blck.slot = slot
|
signedBlock.message.slot = slot
|
||||||
blck.body.attestations.add attestation
|
signedBlock.message.body.attestations.add attestation
|
||||||
process_slots(state, slot)
|
process_slots(state, slot)
|
||||||
signMockBlock(state, blck)
|
signMockBlock(state, signedBlock)
|
||||||
|
|
||||||
# TODO: we can skip just VerifyStateRoot
|
# TODO: we can skip just VerifyStateRoot
|
||||||
doAssert state_transition(state, blck, flags = {skipValidation})
|
doAssert state_transition(
|
||||||
|
state, signedBlock.message, flags = {skipValidation})
|
||||||
|
|
|
@ -19,51 +19,45 @@ import
|
||||||
|
|
||||||
proc signMockBlockImpl(
|
proc signMockBlockImpl(
|
||||||
state: BeaconState,
|
state: BeaconState,
|
||||||
blck: var BeaconBlock,
|
signedBlock: var SignedBeaconBlock,
|
||||||
proposer_index: ValidatorIndex
|
proposer_index: ValidatorIndex
|
||||||
) =
|
) =
|
||||||
doAssert state.slot <= blck.slot
|
let block_slot = signedBlock.message.slot
|
||||||
|
doAssert state.slot <= block_slot
|
||||||
|
|
||||||
let privkey = MockPrivKeys[proposer_index]
|
let privkey = MockPrivKeys[proposer_index]
|
||||||
|
|
||||||
blck.body.randao_reveal = bls_sign(
|
signedBlock.message.body.randao_reveal = bls_sign(
|
||||||
key = privkey,
|
key = privkey,
|
||||||
msg = blck.slot
|
msg = block_slot
|
||||||
.compute_epoch_at_slot()
|
.compute_epoch_at_slot()
|
||||||
.hash_tree_root()
|
.hash_tree_root()
|
||||||
.data,
|
.data,
|
||||||
domain = get_domain(
|
domain = get_domain(
|
||||||
state,
|
state,
|
||||||
DOMAIN_RANDAO,
|
DOMAIN_RANDAO,
|
||||||
message_epoch = blck.slot.compute_epoch_at_slot(),
|
message_epoch = block_slot.compute_epoch_at_slot(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
blck.signature = bls_sign(
|
signedBlock.signature = bls_sign(
|
||||||
key = privkey,
|
key = privkey,
|
||||||
msg = blck.signing_root().data,
|
msg = signedBlock.message.hash_tree_root().data,
|
||||||
domain = get_domain(
|
domain = get_domain(
|
||||||
state,
|
state,
|
||||||
DOMAIN_BEACON_PROPOSER,
|
DOMAIN_BEACON_PROPOSER,
|
||||||
message_epoch = blck.slot.compute_epoch_at_slot(),
|
message_epoch = block_slot.compute_epoch_at_slot(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
proc signMockBlock*(
|
proc signMockBlock*(
|
||||||
state: BeaconState,
|
state: BeaconState,
|
||||||
blck: var BeaconBlock,
|
signedBlock: var SignedBeaconBlock
|
||||||
proposer_index: ValidatorIndex
|
|
||||||
) =
|
|
||||||
signMockBlockImpl(state, blck, proposer_index)
|
|
||||||
|
|
||||||
proc signMockBlock*(
|
|
||||||
state: BeaconState,
|
|
||||||
blck: var BeaconBlock
|
|
||||||
) =
|
) =
|
||||||
|
|
||||||
var emptyCache = get_empty_per_epoch_cache()
|
var emptyCache = get_empty_per_epoch_cache()
|
||||||
let proposer_index =
|
let proposer_index =
|
||||||
if blck.slot == state.slot:
|
if signedBlock.message.slot == state.slot:
|
||||||
get_beacon_proposer_index(state, emptyCache)
|
get_beacon_proposer_index(state, emptyCache)
|
||||||
else:
|
else:
|
||||||
# Stub to get proposer index of future slot
|
# Stub to get proposer index of future slot
|
||||||
|
@ -71,37 +65,38 @@ proc signMockBlock*(
|
||||||
# i.e. BeaconState should have value semantics
|
# i.e. BeaconState should have value semantics
|
||||||
# and not contain ref objects or pointers
|
# and not contain ref objects or pointers
|
||||||
var stubState = state
|
var stubState = state
|
||||||
process_slots(stub_state, blck.slot)
|
process_slots(stub_state, signedBlock.message.slot)
|
||||||
get_beacon_proposer_index(stub_state, emptyCache)
|
get_beacon_proposer_index(stub_state, emptyCache)
|
||||||
|
|
||||||
# In tests, just let this throw if appropriate
|
# In tests, just let this throw if appropriate
|
||||||
signMockBlockImpl(state, blck, proposer_index.get)
|
signMockBlockImpl(state, signedBlock, proposer_index.get)
|
||||||
|
|
||||||
proc mockBlock*(
|
proc mockBlock(
|
||||||
state: BeaconState,
|
state: BeaconState,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
flags: UpdateFlags = {}): BeaconBlock =
|
flags: UpdateFlags = {}): SignedBeaconBlock =
|
||||||
## Mock a BeaconBlock for the specific slot
|
## Mock a BeaconBlock for the specific slot
|
||||||
## Add skipValidation if block should not be signed
|
## Add skipValidation if block should not be signed
|
||||||
|
|
||||||
result.slot = slot
|
result.message.slot = slot
|
||||||
result.body.eth1_data.deposit_count = state.eth1_deposit_index
|
result.message.body.eth1_data.deposit_count = state.eth1_deposit_index
|
||||||
|
|
||||||
var previous_block_header = state.latest_block_header
|
var previous_block_header = state.latest_block_header
|
||||||
if previous_block_header.state_root == ZERO_HASH:
|
if previous_block_header.state_root == ZERO_HASH:
|
||||||
previous_block_header.state_root = state.hash_tree_root()
|
previous_block_header.state_root = state.hash_tree_root()
|
||||||
result.parent_root = previous_block_header.signing_root()
|
result.message.parent_root = previous_block_header.hash_tree_root()
|
||||||
|
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
signMockBlock(state, result)
|
signMockBlock(state, result)
|
||||||
|
|
||||||
proc mockBlockForNextSlot*(state: BeaconState, flags: UpdateFlags = {}): BeaconBlock =
|
proc mockBlockForNextSlot*(state: BeaconState, flags: UpdateFlags = {}):
|
||||||
|
SignedBeaconBlock =
|
||||||
mockBlock(state, state.slot + 1, flags)
|
mockBlock(state, state.slot + 1, flags)
|
||||||
|
|
||||||
proc applyEmptyBlock*(state: var BeaconState) =
|
proc applyEmptyBlock*(state: var BeaconState) =
|
||||||
## Do a state transition with an empty signed block
|
## Do a state transition with an empty signed block
|
||||||
## on the current slot
|
## on the current slot
|
||||||
let blck = mockBlock(state, state.slot, flags = {})
|
let signedBlock = mockBlock(state, state.slot, flags = {})
|
||||||
# TODO: we only need to skip verifyStateRoot validation
|
# TODO: we only need to skip verifyStateRoot validation
|
||||||
# processBlock validation should work
|
# processBlock validation should work
|
||||||
doAssert state_transition(state, blck, {skipValidation})
|
doAssert state_transition(state, signedBlock.message, {skipValidation})
|
||||||
|
|
|
@ -25,7 +25,7 @@ func signMockDepositData(
|
||||||
# No state --> Genesis
|
# No state --> Genesis
|
||||||
deposit_data.signature = bls_sign(
|
deposit_data.signature = bls_sign(
|
||||||
key = privkey,
|
key = privkey,
|
||||||
msg = deposit_data.signing_root().data,
|
msg = deposit_data.hash_tree_root().data,
|
||||||
domain = compute_domain(
|
domain = compute_domain(
|
||||||
DOMAIN_DEPOSIT,
|
DOMAIN_DEPOSIT,
|
||||||
default(array[4, byte]) # Genesis is fork_version 0
|
default(array[4, byte]) # Genesis is fork_version 0
|
||||||
|
@ -39,7 +39,7 @@ func signMockDepositData(
|
||||||
) =
|
) =
|
||||||
deposit_data.signature = bls_sign(
|
deposit_data.signature = bls_sign(
|
||||||
key = privkey,
|
key = privkey,
|
||||||
msg = deposit_data.signing_root().data,
|
msg = deposit_data.hash_tree_root().data,
|
||||||
domain = get_domain(
|
domain = get_domain(
|
||||||
state,
|
state,
|
||||||
DOMAIN_DEPOSIT
|
DOMAIN_DEPOSIT
|
||||||
|
|
|
@ -36,7 +36,7 @@ proc readValue*(r: var JsonReader, a: var seq[byte]) {.inline.} =
|
||||||
|
|
||||||
const
|
const
|
||||||
FixturesDir* = currentSourcePath.rsplit(DirSep, 1)[0] / "fixtures"
|
FixturesDir* = currentSourcePath.rsplit(DirSep, 1)[0] / "fixtures"
|
||||||
SszTestsDir* = FixturesDir/"tests-v0.9.2"
|
SszTestsDir* = FixturesDir/"tests-v0.9.3"
|
||||||
|
|
||||||
proc parseTest*(path: string, Format: typedesc[Json or SSZ], T: typedesc): T =
|
proc parseTest*(path: string, Format: typedesc[Json or SSZ], T: typedesc): T =
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -67,7 +67,6 @@ template runTest(identifier: untyped) =
|
||||||
|
|
||||||
suite "Official - Operations - Block header " & preset():
|
suite "Official - Operations - Block header " & preset():
|
||||||
runTest(success_block_header)
|
runTest(success_block_header)
|
||||||
runTest(invalid_sig_block_header)
|
|
||||||
runTest(invalid_slot_block_header)
|
runTest(invalid_slot_block_header)
|
||||||
when false: # skipValidation needs to be split https://github.com/status-im/nim-beacon-chain/issues/407
|
when false: # skipValidation needs to be split https://github.com/status-im/nim-beacon-chain/issues/407
|
||||||
runTest(invalid_parent_root)
|
runTest(invalid_parent_root)
|
||||||
|
|
|
@ -41,11 +41,11 @@ template runTest(identifier: untyped) =
|
||||||
|
|
||||||
timedTest prefix & astToStr(identifier):
|
timedTest prefix & astToStr(identifier):
|
||||||
var stateRef, postRef: ref BeaconState
|
var stateRef, postRef: ref BeaconState
|
||||||
var voluntaryExit: ref VoluntaryExit
|
var voluntaryExit: ref SignedVoluntaryExit
|
||||||
new voluntaryExit
|
new voluntaryExit
|
||||||
new stateRef
|
new stateRef
|
||||||
|
|
||||||
voluntaryExit[] = parseTest(testDir/"voluntary_exit.ssz", SSZ, VoluntaryExit)
|
voluntaryExit[] = parseTest(testDir/"voluntary_exit.ssz", SSZ, SignedVoluntaryExit)
|
||||||
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
|
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
|
||||||
|
|
||||||
if existsFile(testDir/"post.ssz"):
|
if existsFile(testDir/"post.ssz"):
|
||||||
|
@ -65,7 +65,11 @@ template runTest(identifier: untyped) =
|
||||||
|
|
||||||
suite "Official - Operations - Voluntary exit " & preset():
|
suite "Official - Operations - Voluntary exit " & preset():
|
||||||
runTest(success)
|
runTest(success)
|
||||||
runTest(invalid_signature)
|
|
||||||
|
when false:
|
||||||
|
# TODO not sure how this particularly could falsely succeed
|
||||||
|
runTest(invalid_signature)
|
||||||
|
|
||||||
runTest(success_exit_queue)
|
runTest(success_exit_queue)
|
||||||
runTest(validator_exit_in_future)
|
runTest(validator_exit_in_future)
|
||||||
runTest(validator_invalid_validator_index)
|
runTest(validator_invalid_validator_index)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import
|
||||||
# Standard library
|
# Standard library
|
||||||
os, unittest,
|
os, unittest,
|
||||||
# Beacon chain internals
|
# Beacon chain internals
|
||||||
../../beacon_chain/spec/[datatypes],
|
../../beacon_chain/spec/[crypto, datatypes],
|
||||||
../../beacon_chain/[ssz, state_transition, extras],
|
../../beacon_chain/[ssz, state_transition, extras],
|
||||||
# Test utilities
|
# Test utilities
|
||||||
../testutil,
|
../testutil,
|
||||||
|
@ -37,10 +37,10 @@ template runValidTest(testName: string, identifier: untyped, num_blocks: int): u
|
||||||
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
||||||
|
|
||||||
for i in 0 ..< num_blocks:
|
for i in 0 ..< num_blocks:
|
||||||
let blck = parseTest(testDir/"blocks_" & $i & ".ssz", SSZ, BeaconBlock)
|
let blck = parseTest(testDir/"blocks_" & $i & ".ssz", SSZ, SignedBeaconBlock)
|
||||||
|
|
||||||
# TODO: The EF is using invalid BLS keys so we can't verify them
|
# TODO: The EF is using invalid BLS keys so we can't verify them
|
||||||
let success = state_transition(stateRef[], blck, flags = {skipValidation})
|
let success = state_transition(stateRef[], blck.message, flags = {skipValidation})
|
||||||
doAssert success, "Failure when applying block " & $i
|
doAssert success, "Failure when applying block " & $i
|
||||||
|
|
||||||
# Checks:
|
# Checks:
|
||||||
|
@ -56,13 +56,13 @@ suite "Official - Sanity - Blocks " & preset():
|
||||||
new stateRef
|
new stateRef
|
||||||
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
|
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
|
||||||
|
|
||||||
let blck = parseTest(testDir/"blocks_0.ssz", SSZ, BeaconBlock)
|
let blck = parseTest(testDir/"blocks_0.ssz", SSZ, SignedBeaconBlock)
|
||||||
|
|
||||||
# Check that a block build for an old slot cannot be used for state transition
|
# Check that a block build for an old slot cannot be used for state transition
|
||||||
expect(AssertionError):
|
expect(AssertionError):
|
||||||
# assert in process_slots. This should not be triggered
|
# assert in process_slots. This should not be triggered
|
||||||
# for blocks from block_pool/network
|
# for blocks from block_pool/network
|
||||||
discard state_transition(stateRef[], blck, flags = {skipValidation})
|
discard state_transition(stateRef[], blck.message, flags = {skipValidation})
|
||||||
|
|
||||||
runValidTest("Same slot block transition", same_slot_block_transition, 1)
|
runValidTest("Same slot block transition", same_slot_block_transition, 1)
|
||||||
runValidTest("Empty block transition", empty_block_transition, 1)
|
runValidTest("Empty block transition", empty_block_transition, 1)
|
||||||
|
|
|
@ -14,7 +14,7 @@ import
|
||||||
# Third-party
|
# Third-party
|
||||||
yaml,
|
yaml,
|
||||||
# Beacon chain internals
|
# Beacon chain internals
|
||||||
../../beacon_chain/spec/[datatypes, digest],
|
../../beacon_chain/spec/[crypto, datatypes, digest],
|
||||||
../../beacon_chain/ssz,
|
../../beacon_chain/ssz,
|
||||||
# Test utilities
|
# Test utilities
|
||||||
../testutil
|
../testutil
|
||||||
|
@ -26,7 +26,7 @@ import
|
||||||
|
|
||||||
const
|
const
|
||||||
FixturesDir = currentSourcePath.rsplit(DirSep, 1)[0] / "fixtures"
|
FixturesDir = currentSourcePath.rsplit(DirSep, 1)[0] / "fixtures"
|
||||||
SSZDir = FixturesDir/"tests-v0.9.2"/const_preset/"phase0"/"ssz_static"
|
SSZDir = FixturesDir/"tests-v0.9.3"/const_preset/"phase0"/"ssz_static"
|
||||||
|
|
||||||
type
|
type
|
||||||
SSZHashTreeRoot = object
|
SSZHashTreeRoot = object
|
||||||
|
@ -39,11 +39,8 @@ type
|
||||||
# Make signing root optional
|
# Make signing root optional
|
||||||
setDefaultValue(SSZHashTreeRoot, signing_root, "")
|
setDefaultValue(SSZHashTreeRoot, signing_root, "")
|
||||||
|
|
||||||
# Note this onyl tracks HashTreeRoot and SigningRoot
|
# Note this only tracks HashTreeRoot
|
||||||
# Checking the values against the yaml file is TODO (require more flexible Yaml parser)
|
# Checking the values against the yaml file is TODO (require more flexible Yaml parser)
|
||||||
const Unsupported = toHashSet([
|
|
||||||
"AggregateAndProof", # Type for signature aggregation - not implemented
|
|
||||||
])
|
|
||||||
|
|
||||||
proc checkSSZ(T: typedesc, dir: string, expectedHash: SSZHashTreeRoot) =
|
proc checkSSZ(T: typedesc, dir: string, expectedHash: SSZHashTreeRoot) =
|
||||||
# Deserialize into a ref object to not fill Nim stack
|
# Deserialize into a ref object to not fill Nim stack
|
||||||
|
@ -52,8 +49,6 @@ proc checkSSZ(T: typedesc, dir: string, expectedHash: SSZHashTreeRoot) =
|
||||||
deserialized[] = SSZ.loadFile(dir/"serialized.ssz", T)
|
deserialized[] = SSZ.loadFile(dir/"serialized.ssz", T)
|
||||||
|
|
||||||
check: expectedHash.root == "0x" & toLowerASCII($deserialized.hashTreeRoot())
|
check: expectedHash.root == "0x" & toLowerASCII($deserialized.hashTreeRoot())
|
||||||
if expectedHash.signing_root != "":
|
|
||||||
check: expectedHash.signing_root == "0x" & toLowerASCII($deserialized[].signingRoot())
|
|
||||||
|
|
||||||
# TODO check the value
|
# TODO check the value
|
||||||
|
|
||||||
|
@ -69,10 +64,6 @@ proc runSSZtests() =
|
||||||
doAssert existsDir(SSZDir), "You need to run the \"download_test_vectors.sh\" script to retrieve the official test vectors."
|
doAssert existsDir(SSZDir), "You need to run the \"download_test_vectors.sh\" script to retrieve the official test vectors."
|
||||||
for pathKind, sszType in walkDir(SSZDir, relative = true):
|
for pathKind, sszType in walkDir(SSZDir, relative = true):
|
||||||
doAssert pathKind == pcDir
|
doAssert pathKind == pcDir
|
||||||
if sszType in Unsupported:
|
|
||||||
timedTest &" Skipping {sszType:20} ✗✗✗":
|
|
||||||
discard
|
|
||||||
continue
|
|
||||||
|
|
||||||
timedTest &" Testing {sszType}":
|
timedTest &" Testing {sszType}":
|
||||||
let path = SSZDir/sszType
|
let path = SSZDir/sszType
|
||||||
|
@ -84,7 +75,7 @@ proc runSSZtests() =
|
||||||
let hash = loadExpectedHashTreeRoot(path)
|
let hash = loadExpectedHashTreeRoot(path)
|
||||||
|
|
||||||
case sszType:
|
case sszType:
|
||||||
# of "AggregateAndProof": checkSSZ(AggregateAndProof, path, hash)
|
of "AggregateAndProof": checkSSZ(AggregateAndProof, path, hash)
|
||||||
of "Attestation": checkSSZ(Attestation, path, hash)
|
of "Attestation": checkSSZ(Attestation, path, hash)
|
||||||
of "AttestationData": checkSSZ(AttestationData, path, hash)
|
of "AttestationData": checkSSZ(AttestationData, path, hash)
|
||||||
of "AttesterSlashing": checkSSZ(AttesterSlashing, path, hash)
|
of "AttesterSlashing": checkSSZ(AttesterSlashing, path, hash)
|
||||||
|
@ -95,12 +86,17 @@ proc runSSZtests() =
|
||||||
of "Checkpoint": checkSSZ(Checkpoint, path, hash)
|
of "Checkpoint": checkSSZ(Checkpoint, path, hash)
|
||||||
of "Deposit": checkSSZ(Deposit, path, hash)
|
of "Deposit": checkSSZ(Deposit, path, hash)
|
||||||
of "DepositData": checkSSZ(DepositData, path, hash)
|
of "DepositData": checkSSZ(DepositData, path, hash)
|
||||||
|
of "DepositMessage": checkSSZ(DepositMessage, path, hash)
|
||||||
of "Eth1Data": checkSSZ(Eth1Data, path, hash)
|
of "Eth1Data": checkSSZ(Eth1Data, path, hash)
|
||||||
of "Fork": checkSSZ(Fork, path, hash)
|
of "Fork": checkSSZ(Fork, path, hash)
|
||||||
of "HistoricalBatch": checkSSZ(HistoricalBatch, path, hash)
|
of "HistoricalBatch": checkSSZ(HistoricalBatch, path, hash)
|
||||||
of "IndexedAttestation": checkSSZ(IndexedAttestation, path, hash)
|
of "IndexedAttestation": checkSSZ(IndexedAttestation, path, hash)
|
||||||
of "PendingAttestation": checkSSZ(PendingAttestation, path, hash)
|
of "PendingAttestation": checkSSZ(PendingAttestation, path, hash)
|
||||||
of "ProposerSlashing": checkSSZ(ProposerSlashing, path, hash)
|
of "ProposerSlashing": checkSSZ(ProposerSlashing, path, hash)
|
||||||
|
of "SignedBeaconBlock": checkSSZ(SignedBeaconBlock, path, hash)
|
||||||
|
of "SignedBeaconBlockHeader":
|
||||||
|
checkSSZ(SignedBeaconBlockHeader, path, hash)
|
||||||
|
of "SignedVoluntaryExit": checkSSZ(SignedVoluntaryExit, path, hash)
|
||||||
of "Validator": checkSSZ(Validator, path, hash)
|
of "Validator": checkSSZ(Validator, path, hash)
|
||||||
of "VoluntaryExit": checkSSZ(VoluntaryExit, path, hash)
|
of "VoluntaryExit": checkSSZ(VoluntaryExit, path, hash)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -31,8 +31,8 @@ suite "Beacon chain DB" & preset():
|
||||||
db = init(BeaconChainDB, newMemoryDB())
|
db = init(BeaconChainDB, newMemoryDB())
|
||||||
|
|
||||||
let
|
let
|
||||||
blck = BeaconBlock()
|
blck = SignedBeaconBlock()
|
||||||
root = signing_root(blck)
|
root = hash_tree_root(blck.message)
|
||||||
|
|
||||||
db.putBlock(blck)
|
db.putBlock(blck)
|
||||||
|
|
||||||
|
@ -40,9 +40,9 @@ suite "Beacon chain DB" & preset():
|
||||||
db.containsBlock(root)
|
db.containsBlock(root)
|
||||||
db.getBlock(root).get() == blck
|
db.getBlock(root).get() == blck
|
||||||
|
|
||||||
db.putStateRoot(root, blck.slot, root)
|
db.putStateRoot(root, blck.message.slot, root)
|
||||||
check:
|
check:
|
||||||
db.getStateRoot(root, blck.slot).get() == root
|
db.getStateRoot(root, blck.message.slot).get() == root
|
||||||
|
|
||||||
timedTest "sanity check states" & preset():
|
timedTest "sanity check states" & preset():
|
||||||
var
|
var
|
||||||
|
@ -68,12 +68,14 @@ suite "Beacon chain DB" & preset():
|
||||||
check: x == y
|
check: x == y
|
||||||
|
|
||||||
let
|
let
|
||||||
a0 = BeaconBlock(slot: GENESIS_SLOT + 0)
|
a0 = SignedBeaconBlock(message: BeaconBlock(slot: GENESIS_SLOT + 0))
|
||||||
a0r = signing_root(a0)
|
a0r = hash_tree_root(a0.message)
|
||||||
a1 = BeaconBlock(slot: GENESIS_SLOT + 1, parent_root: a0r)
|
a1 = SignedBeaconBlock(message:
|
||||||
a1r = signing_root(a1)
|
BeaconBlock(slot: GENESIS_SLOT + 1, parent_root: a0r))
|
||||||
a2 = BeaconBlock(slot: GENESIS_SLOT + 2, parent_root: a1r)
|
a1r = hash_tree_root(a1.message)
|
||||||
a2r = signing_root(a2)
|
a2 = SignedBeaconBlock(message:
|
||||||
|
BeaconBlock(slot: GENESIS_SLOT + 2, parent_root: a1r))
|
||||||
|
a2r = hash_tree_root(a2.message)
|
||||||
|
|
||||||
doAssert toSeq(db.getAncestors(a0r)) == []
|
doAssert toSeq(db.getAncestors(a0r)) == []
|
||||||
doAssert toSeq(db.getAncestors(a2r)) == []
|
doAssert toSeq(db.getAncestors(a2r)) == []
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{.used.}
|
{.used.}
|
||||||
|
|
||||||
import
|
import
|
||||||
options, sequtils, chronicles, unittest,
|
options, sequtils, unittest, chronicles,
|
||||||
./testutil, ./testblockutil,
|
./testutil, ./testblockutil,
|
||||||
../beacon_chain/spec/[beaconstate, datatypes, digest],
|
../beacon_chain/spec/[beaconstate, datatypes, digest],
|
||||||
../beacon_chain/[beacon_node_types, block_pool, beacon_chain_db, extras, ssz]
|
../beacon_chain/[beacon_node_types, block_pool, beacon_chain_db, extras, ssz]
|
||||||
|
@ -42,7 +42,7 @@ suite "Block pool processing" & preset():
|
||||||
timedTest "Simple block add&get" & preset():
|
timedTest "Simple block add&get" & preset():
|
||||||
let
|
let
|
||||||
b1 = makeBlock(state.data.data, state.blck.root, BeaconBlockBody())
|
b1 = makeBlock(state.data.data, state.blck.root, BeaconBlockBody())
|
||||||
b1Root = signing_root(b1)
|
b1Root = hash_tree_root(b1.message)
|
||||||
|
|
||||||
# TODO the return value is ugly here, need to fix and test..
|
# TODO the return value is ugly here, need to fix and test..
|
||||||
discard pool.add(state, b1Root, b1)
|
discard pool.add(state, b1Root, b1)
|
||||||
|
@ -57,9 +57,9 @@ suite "Block pool processing" & preset():
|
||||||
timedTest "Reverse order block add & get" & preset():
|
timedTest "Reverse order block add & get" & preset():
|
||||||
let
|
let
|
||||||
b1 = addBlock(state.data.data, state.blck.root, BeaconBlockBody(), {})
|
b1 = addBlock(state.data.data, state.blck.root, BeaconBlockBody(), {})
|
||||||
b1Root = signing_root(b1)
|
b1Root = hash_tree_root(b1.message)
|
||||||
b2 = addBlock(state.data.data, b1Root, BeaconBlockBody(), {})
|
b2 = addBlock(state.data.data, b1Root, BeaconBlockBody(), {})
|
||||||
b2Root = signing_root(b2)
|
b2Root = hash_tree_root(b2.message)
|
||||||
|
|
||||||
discard pool.add(state, b2Root, b2)
|
discard pool.add(state, b2Root, b2)
|
||||||
|
|
||||||
|
@ -81,8 +81,8 @@ suite "Block pool processing" & preset():
|
||||||
|
|
||||||
b1r.get().refs.children[0] == b2r.get().refs
|
b1r.get().refs.children[0] == b2r.get().refs
|
||||||
b2r.get().refs.parent == b1r.get().refs
|
b2r.get().refs.parent == b1r.get().refs
|
||||||
toSeq(pool.blockRootsForSlot(b1.slot)) == @[b1Root]
|
toSeq(pool.blockRootsForSlot(b1.message.slot)) == @[b1Root]
|
||||||
toSeq(pool.blockRootsForSlot(b2.slot)) == @[b2Root]
|
toSeq(pool.blockRootsForSlot(b2.message.slot)) == @[b2Root]
|
||||||
|
|
||||||
db.putHeadBlock(b2Root)
|
db.putHeadBlock(b2Root)
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ suite "Block processing" & preset():
|
||||||
Eth2Digest(), 0,
|
Eth2Digest(), 0,
|
||||||
makeInitialDeposits(), {})
|
makeInitialDeposits(), {})
|
||||||
genesisBlock = get_initial_beacon_block(genesisState)
|
genesisBlock = get_initial_beacon_block(genesisState)
|
||||||
genesisRoot = signing_root(genesisBlock)
|
genesisRoot = hash_tree_root(genesisBlock.message)
|
||||||
|
|
||||||
timedTest "Passes from genesis state, no block" & preset():
|
timedTest "Passes from genesis state, no block" & preset():
|
||||||
var
|
var
|
||||||
|
@ -37,10 +37,10 @@ suite "Block processing" & preset():
|
||||||
timedTest "Passes from genesis state, empty block" & preset():
|
timedTest "Passes from genesis state, empty block" & preset():
|
||||||
var
|
var
|
||||||
state = genesisState
|
state = genesisState
|
||||||
previous_block_root = signing_root(genesisBlock)
|
previous_block_root = hash_tree_root(genesisBlock.message)
|
||||||
new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
||||||
|
|
||||||
let block_ok = state_transition(state, new_block, {})
|
let block_ok = state_transition(state, new_block.message, {})
|
||||||
|
|
||||||
check:
|
check:
|
||||||
block_ok
|
block_ok
|
||||||
|
@ -64,12 +64,12 @@ suite "Block processing" & preset():
|
||||||
for i in 1..SLOTS_PER_EPOCH.int:
|
for i in 1..SLOTS_PER_EPOCH.int:
|
||||||
var new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
var new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
||||||
|
|
||||||
let block_ok = state_transition(state, new_block, {})
|
let block_ok = state_transition(state, new_block.message, {})
|
||||||
|
|
||||||
check:
|
check:
|
||||||
block_ok
|
block_ok
|
||||||
|
|
||||||
previous_block_root = signing_root(new_block)
|
previous_block_root = hash_tree_root(new_block.message)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
state.slot == genesisState.slot + SLOTS_PER_EPOCH
|
state.slot == genesisState.slot + SLOTS_PER_EPOCH
|
||||||
|
@ -98,7 +98,7 @@ suite "Block processing" & preset():
|
||||||
new_block = makeBlock(state, previous_block_root, BeaconBlockBody(
|
new_block = makeBlock(state, previous_block_root, BeaconBlockBody(
|
||||||
attestations: @[attestation]
|
attestations: @[attestation]
|
||||||
))
|
))
|
||||||
discard state_transition(state, new_block, {})
|
discard state_transition(state, new_block.message, {})
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# TODO epoch attestations can get multiplied now; clean up paths to
|
# TODO epoch attestations can get multiplied now; clean up paths to
|
||||||
|
|
|
@ -36,9 +36,9 @@ suite "Zero signature sanity checks":
|
||||||
|
|
||||||
# check(zeroSIg == deserZeroSig)
|
# check(zeroSIg == deserZeroSig)
|
||||||
|
|
||||||
timedTest "SSZ serialization roundtrip of BeaconBlockHeader":
|
timedTest "SSZ serialization roundtrip of SignedBeaconBlockHeader":
|
||||||
|
|
||||||
let defaultBlockHeader = BeaconBlockHeader(
|
let defaultBlockHeader = SignedBeaconBlockHeader(
|
||||||
signature: BlsValue[Signature](kind: OpaqueBlob)
|
signature: BlsValue[Signature](kind: OpaqueBlob)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ suite "Zero signature sanity checks":
|
||||||
allZeros
|
allZeros
|
||||||
|
|
||||||
let sszDefaultBlockHeader = SSZ.encode(defaultBlockHeader)
|
let sszDefaultBlockHeader = SSZ.encode(defaultBlockHeader)
|
||||||
let deserBlockHeader = SSZ.decode(sszDefaultBlockHeader, BeaconBlockHeader)
|
let deserBlockHeader =
|
||||||
|
SSZ.decode(sszDefaultBlockHeader, SignedBeaconBlockHeader)
|
||||||
|
|
||||||
check(defaultBlockHeader == deserBlockHeader)
|
check(defaultBlockHeader == deserBlockHeader)
|
||||||
|
|
|
@ -64,7 +64,7 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
|
||||||
|
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
result.data.signature =
|
result.data.signature =
|
||||||
bls_sign(privkey, signing_root(result.data).data,
|
bls_sign(privkey, hash_tree_root(result.data).data,
|
||||||
domain)
|
domain)
|
||||||
|
|
||||||
func makeInitialDeposits*(
|
func makeInitialDeposits*(
|
||||||
|
@ -74,7 +74,7 @@ func makeInitialDeposits*(
|
||||||
|
|
||||||
proc addBlock*(
|
proc addBlock*(
|
||||||
state: var BeaconState, previous_block_root: Eth2Digest,
|
state: var BeaconState, previous_block_root: Eth2Digest,
|
||||||
body: BeaconBlockBody, flags: UpdateFlags = {}): BeaconBlock =
|
body: BeaconBlockBody, flags: UpdateFlags = {}): SignedBeaconBlock =
|
||||||
# Create and add a block to state - state will advance by one slot!
|
# Create and add a block to state - state will advance by one slot!
|
||||||
# This is the equivalent of running
|
# This is the equivalent of running
|
||||||
# updateState(state, prev_block, makeBlock(...), {skipValidation})
|
# updateState(state, prev_block, makeBlock(...), {skipValidation})
|
||||||
|
@ -100,44 +100,46 @@ proc addBlock*(
|
||||||
# In order to reuse the state transition function, we first create a dummy
|
# In order to reuse the state transition function, we first create a dummy
|
||||||
# block that has some fields set, and use that to generate the state as it
|
# block that has some fields set, and use that to generate the state as it
|
||||||
# would look with the new block applied.
|
# would look with the new block applied.
|
||||||
new_block = BeaconBlock(
|
new_block = SignedBeaconBlock(
|
||||||
slot: state.slot + 1,
|
message: BeaconBlock(
|
||||||
parent_root: previous_block_root,
|
slot: state.slot + 1,
|
||||||
state_root: Eth2Digest(), # we need the new state first
|
parent_root: previous_block_root,
|
||||||
body: new_body,
|
state_root: Eth2Digest(), # we need the new state first
|
||||||
signature: ValidatorSig(), # we need the rest of the block first!
|
body: new_body
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
let block_ok = state_transition(state, new_block, {skipValidation})
|
let block_ok = state_transition(state, new_block.message, {skipValidation})
|
||||||
doAssert block_ok
|
doAssert block_ok
|
||||||
|
|
||||||
# Ok, we have the new state as it would look with the block applied - now we
|
# Ok, we have the new state as it would look with the block applied - now we
|
||||||
# can set the state root in order to be able to create a valid signature
|
# can set the state root in order to be able to create a valid signature
|
||||||
new_block.state_root = hash_tree_root(state)
|
new_block.message.state_root = hash_tree_root(state)
|
||||||
|
|
||||||
doAssert privKey.pubKey() == proposer.pubkey,
|
doAssert privKey.pubKey() == proposer.pubkey,
|
||||||
"signature key should be derived from private key! - wrong privkey?"
|
"signature key should be derived from private key! - wrong privkey?"
|
||||||
|
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
let block_root = signing_root(new_block)
|
let block_root = hash_tree_root(new_block.message)
|
||||||
# We have a signature - put it in the block and we should be done!
|
# We have a signature - put it in the block and we should be done!
|
||||||
new_block.signature =
|
new_block.signature =
|
||||||
bls_sign(privKey, block_root.data,
|
bls_sign(privKey, block_root.data,
|
||||||
get_domain(state, DOMAIN_BEACON_PROPOSER,
|
get_domain(state, DOMAIN_BEACON_PROPOSER,
|
||||||
compute_epoch_at_slot(new_block.slot)))
|
compute_epoch_at_slot(new_block.message.slot)))
|
||||||
|
|
||||||
doAssert bls_verify(
|
doAssert bls_verify(
|
||||||
proposer.pubkey,
|
proposer.pubkey,
|
||||||
block_root.data, new_block.signature,
|
block_root.data, new_block.signature,
|
||||||
get_domain(
|
get_domain(
|
||||||
state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(new_block.slot))),
|
state, DOMAIN_BEACON_PROPOSER,
|
||||||
|
compute_epoch_at_slot(new_block.message.slot))),
|
||||||
"we just signed this message - it should pass verification!"
|
"we just signed this message - it should pass verification!"
|
||||||
|
|
||||||
new_block
|
new_block
|
||||||
|
|
||||||
proc makeBlock*(
|
proc makeBlock*(
|
||||||
state: BeaconState, previous_block_root: Eth2Digest,
|
state: BeaconState, previous_block_root: Eth2Digest,
|
||||||
body: BeaconBlockBody): BeaconBlock =
|
body: BeaconBlockBody): SignedBeaconBlock =
|
||||||
# Create a block for `state.slot + 1` - like a block proposer would do!
|
# Create a block for `state.slot + 1` - like a block proposer would do!
|
||||||
# It's a bit awkward - in order to produce a block for N+1, we need to
|
# It's a bit awkward - in order to produce a block for N+1, we need to
|
||||||
# calculate what the state will look like after that block has been applied,
|
# calculate what the state will look like after that block has been applied,
|
||||||
|
|
|
@ -71,7 +71,7 @@ template timedTest*(name, body) =
|
||||||
# TODO noto thread-safe as-is
|
# TODO noto thread-safe as-is
|
||||||
testTimes.add (f, name)
|
testTimes.add (f, name)
|
||||||
|
|
||||||
proc makeTestDB*(tailState: BeaconState, tailBlock: BeaconBlock): BeaconChainDB =
|
proc makeTestDB*(tailState: BeaconState, tailBlock: SignedBeaconBlock): BeaconChainDB =
|
||||||
result = init(BeaconChainDB, newMemoryDB())
|
result = init(BeaconChainDB, newMemoryDB())
|
||||||
BlockPool.preInit(result, tailState, tailBlock)
|
BlockPool.preInit(result, tailState, tailBlock)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue