clean up and add some logging (#70)
This commit is contained in:
parent
a0712e691a
commit
e0a257406e
|
@ -29,6 +29,8 @@ const
|
|||
topicAttestations = "ethereum/2.1/beacon_chain/attestations"
|
||||
|
||||
func shortHash(x: auto): string = ($x)[0..7]
|
||||
func shortValidatorKey(node: BeaconNode, validatorIdx: int): string =
|
||||
($node.beaconState.validator_registry[validatorIdx].pubkey)[0..7]
|
||||
|
||||
proc ensureNetworkKeys*(dataDir: string): KeyPair =
|
||||
# TODO:
|
||||
|
@ -131,6 +133,8 @@ proc addLocalValidators*(node: BeaconNode) =
|
|||
if idx == -1:
|
||||
warn "Validator not in registry", pubKey
|
||||
else:
|
||||
debug "Attaching validator", validator = shortValidatorKey(node, idx),
|
||||
idx, pubKey
|
||||
node.attachedValidators.addLocalValidator(idx, pubKey, privKey, randao)
|
||||
|
||||
info "Local validators attached ", count = node.attachedValidators.count
|
||||
|
@ -177,7 +181,7 @@ proc makeAttestation(node: BeaconNode,
|
|||
|
||||
info "Attestation sent", slot = slot,
|
||||
shard = shard,
|
||||
validator = validator.idx,
|
||||
validator = shortValidatorKey(node, validator.idx),
|
||||
signature = shortHash(validatorSignature)
|
||||
|
||||
proc proposeBlock(node: BeaconNode,
|
||||
|
@ -189,10 +193,13 @@ proc proposeBlock(node: BeaconNode,
|
|||
|
||||
var state = node.beaconState
|
||||
|
||||
for s in node.beaconState.slot + 1 ..< slot:
|
||||
info "Skipping block", slot = s
|
||||
let ok = updateState(state, node.headBlockRoot, none[BeaconBlock](), {})
|
||||
doAssert ok
|
||||
if node.beaconState.slot + 1 < slot:
|
||||
info "Proposing block after slot gap",
|
||||
slot,
|
||||
stateSlot = node.beaconState.slot
|
||||
for s in node.beaconState.slot + 1 ..< slot:
|
||||
let ok = updateState(state, node.headBlockRoot, none[BeaconBlock](), {})
|
||||
doAssert ok
|
||||
|
||||
let
|
||||
randaoCommitment = node.beaconState.validator_registry[validator.idx].randao_commitment
|
||||
|
@ -225,7 +232,7 @@ proc proposeBlock(node: BeaconNode,
|
|||
|
||||
info "Block proposed", slot = slot,
|
||||
stateRoot = shortHash(newBlock.state_root),
|
||||
validator = validator.idx
|
||||
validator = shortValidatorKey(node, validator.idx)
|
||||
|
||||
proc scheduleBlockProposal(node: BeaconNode,
|
||||
slot: uint64,
|
||||
|
@ -236,7 +243,16 @@ proc scheduleBlockProposal(node: BeaconNode,
|
|||
# internal `doAssert` starting to fail.
|
||||
doAssert validator != nil
|
||||
|
||||
let at = node.beaconState.slotStart(slot)
|
||||
|
||||
info "Scheduling block proposal",
|
||||
validator = shortValidatorKey(node, validator.idx),
|
||||
slot,
|
||||
fromNow = (at - fastEpochTime()) div 1000
|
||||
|
||||
addTimer(node.beaconState.slotStart(slot)) do (x: pointer) {.gcsafe.}:
|
||||
# TODO timers are generally not accurate / guaranteed to fire at the right
|
||||
# time - need to guard here against early / late firings
|
||||
doAssert validator != nil
|
||||
asyncCheck proposeBlock(node, validator, slot.uint64)
|
||||
|
||||
|
@ -292,30 +308,40 @@ proc scheduleEpochActions(node: BeaconNode, epoch: uint64) =
|
|||
scheduleAttestation(node, validator, slot, shard.shard, shard.committee.len, i)
|
||||
|
||||
node.lastScheduledEpoch = epoch
|
||||
let nextEpoch = epoch + 1
|
||||
let
|
||||
nextEpoch = epoch + 1
|
||||
at = node.beaconState.slotMiddle(nextEpoch * EPOCH_LENGTH)
|
||||
|
||||
addTimer(node.beaconState.slotMiddle(nextEpoch * EPOCH_LENGTH)) do (p: pointer):
|
||||
info "Scheduling next epoch update",
|
||||
fromNow = (at - fastEpochTime()) div 1000,
|
||||
nextEpoch
|
||||
|
||||
addTimer(at) do (p: pointer):
|
||||
if node.lastScheduledEpoch != nextEpoch:
|
||||
node.scheduleEpochActions(nextEpoch)
|
||||
|
||||
proc processBlocks*(node: BeaconNode) =
|
||||
node.network.subscribe(topicBeaconBlocks) do (newBlock: BeaconBlock):
|
||||
let stateSlot = node.beaconState.slot
|
||||
info "Block received", slot = newBlock.slot,
|
||||
stateRoot = shortHash(newBlock.state_root),
|
||||
currentStateSlot = node.beaconState.slot
|
||||
stateSlot
|
||||
|
||||
# TODO: This should be replaced with the real fork-choice rule
|
||||
if newBlock.slot <= node.beaconState.slot:
|
||||
if newBlock.slot <= stateSlot:
|
||||
debug "Ignoring block"
|
||||
return
|
||||
|
||||
let newBlockRoot = hash_tree_root_final(newBlock)
|
||||
|
||||
var state = node.beaconState
|
||||
for slot in node.beaconState.slot + 1 ..< newBlock.slot:
|
||||
info "Skipping block", slot
|
||||
let ok = updateState(state, node.headBlockRoot, none[BeaconBlock](), {})
|
||||
doAssert ok
|
||||
if stateSlot + 1 < newBlock.slot:
|
||||
info "Advancing state past slot gap",
|
||||
blockSlot = newBlock.slot,
|
||||
stateSlot
|
||||
for slot in stateSlot + 1 ..< newBlock.slot:
|
||||
let ok = updateState(state, node.headBlockRoot, none[BeaconBlock](), {})
|
||||
doAssert ok
|
||||
|
||||
let ok = updateState(state, node.headBlockRoot, some(newBlock), {})
|
||||
if not ok:
|
||||
|
@ -340,17 +366,21 @@ proc processBlocks*(node: BeaconNode) =
|
|||
node.scheduleEpochActions(epoch)
|
||||
|
||||
node.network.subscribe(topicAttestations) do (a: Attestation):
|
||||
let participants = get_attestation_participants(
|
||||
node.beaconState, a.data, a.participation_bitfield).
|
||||
mapIt(shortValidatorKey(node, it))
|
||||
|
||||
info "Attestation received", slot = a.data.slot,
|
||||
shard = a.data.shard,
|
||||
signature = shortHash(a.aggregate_signature)
|
||||
signature = shortHash(a.aggregate_signature),
|
||||
participants
|
||||
|
||||
node.attestationPool.add(a, node.beaconState)
|
||||
|
||||
dynamicLogScope(node = node.config.tcpPort - 50000):
|
||||
let epoch = node.beaconState.timeSinceGenesis().toSlot div EPOCH_LENGTH
|
||||
node.scheduleEpochActions(epoch)
|
||||
let epoch = node.beaconState.timeSinceGenesis().toSlot div EPOCH_LENGTH
|
||||
node.scheduleEpochActions(epoch)
|
||||
|
||||
runForever()
|
||||
runForever()
|
||||
|
||||
var gPidFile: string
|
||||
proc createPidFile(filename: string) =
|
||||
|
@ -370,15 +400,20 @@ when isMainModule:
|
|||
quit 0
|
||||
|
||||
of noCommand:
|
||||
waitFor syncrhronizeClock()
|
||||
waitFor synchronizeClock()
|
||||
createPidFile(config.dataDir.string / "beacon_node.pid")
|
||||
|
||||
var node = BeaconNode.init config
|
||||
waitFor node.connectToNetwork()
|
||||
|
||||
if not waitFor node.sync():
|
||||
quit 1
|
||||
dynamicLogScope(node = node.config.tcpPort - 50000):
|
||||
waitFor node.connectToNetwork()
|
||||
|
||||
node.addLocalValidators()
|
||||
node.processBlocks()
|
||||
if not waitFor node.sync():
|
||||
quit 1
|
||||
|
||||
info "Starting beacon node",
|
||||
slotsSinceFinalization = node.beaconState.slotDistanceFromNow(),
|
||||
stateSlot = node.beaconState.slot
|
||||
|
||||
node.addLocalValidators()
|
||||
node.processBlocks()
|
||||
|
|
|
@ -64,7 +64,7 @@ func verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
|
|||
proposal_hash.data, blck.signature,
|
||||
get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))
|
||||
|
||||
func processRandao(
|
||||
proc processRandao(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
## When a validator signs up, they will include a hash number together with
|
||||
## the deposit - the randao_commitment. The commitment is formed by hashing
|
||||
|
@ -86,8 +86,12 @@ func processRandao(
|
|||
|
||||
if skipValidation notin flags:
|
||||
# Check that proposer commit and reveal match
|
||||
if repeat_hash(blck.randao_reveal, proposer.randao_layers) !=
|
||||
proposer.randao_commitment:
|
||||
let expected = repeat_hash(blck.randao_reveal, proposer.randao_layers)
|
||||
if expected != proposer.randao_commitment:
|
||||
notice "Randao reveal mismatch", reveal = blck.randao_reveal,
|
||||
layers = proposer.randao_layers,
|
||||
commitment = proposer.randao_commitment,
|
||||
expected
|
||||
return false
|
||||
|
||||
# Update state and proposer now that we're alright
|
||||
|
@ -130,8 +134,8 @@ proc processProposerSlashings(
|
|||
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposer-slashings-1
|
||||
|
||||
if len(blck.body.proposer_slashings) > MAX_PROPOSER_SLASHINGS:
|
||||
warn("PropSlash: too many!",
|
||||
proposer_slashings = len(blck.body.proposer_slashings))
|
||||
notice "PropSlash: too many!",
|
||||
proposer_slashings = len(blck.body.proposer_slashings)
|
||||
return false
|
||||
|
||||
for proposer_slashing in blck.body.proposer_slashings:
|
||||
|
@ -144,7 +148,7 @@ proc processProposerSlashings(
|
|||
get_domain(
|
||||
state.fork_data, proposer_slashing.proposal_data_1.slot,
|
||||
DOMAIN_PROPOSAL)):
|
||||
warn("PropSlash: invalid signature 1")
|
||||
notice "PropSlash: invalid signature 1"
|
||||
return false
|
||||
if not bls_verify(
|
||||
proposer.pubkey,
|
||||
|
@ -153,26 +157,26 @@ proc processProposerSlashings(
|
|||
get_domain(
|
||||
state.fork_data, proposer_slashing.proposal_data_2.slot,
|
||||
DOMAIN_PROPOSAL)):
|
||||
warn("PropSlash: invalid signature 2")
|
||||
notice "PropSlash: invalid signature 2"
|
||||
return false
|
||||
|
||||
if not (proposer_slashing.proposal_data_1.slot ==
|
||||
proposer_slashing.proposal_data_2.slot):
|
||||
warn("PropSlash: slot mismatch")
|
||||
notice "PropSlash: slot mismatch"
|
||||
return false
|
||||
|
||||
if not (proposer_slashing.proposal_data_1.shard ==
|
||||
proposer_slashing.proposal_data_2.shard):
|
||||
warn("PropSlash: shard mismatch")
|
||||
notice "PropSlash: shard mismatch"
|
||||
return false
|
||||
|
||||
if not (proposer_slashing.proposal_data_1.block_root ==
|
||||
proposer_slashing.proposal_data_2.block_root):
|
||||
warn("PropSlash: block root mismatch")
|
||||
notice "PropSlash: block root mismatch"
|
||||
return false
|
||||
|
||||
if not (proposer.penalized_slot > state.slot):
|
||||
warn("PropSlash: penalized slot")
|
||||
notice "PropSlash: penalized slot"
|
||||
return false
|
||||
|
||||
penalizeValidator(state, proposer_slashing.proposer_index)
|
||||
|
@ -202,7 +206,7 @@ proc indices(vote: SlashableVoteData): seq[Uint24] =
|
|||
proc processCasperSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
||||
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#casper-slashings-1
|
||||
if len(blck.body.casper_slashings) > MAX_CASPER_SLASHINGS:
|
||||
warn("CaspSlash: too many!")
|
||||
notice "CaspSlash: too many!"
|
||||
return false
|
||||
|
||||
for casper_slashing in blck.body.casper_slashings:
|
||||
|
@ -214,25 +218,25 @@ proc processCasperSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
|||
indices(slashable_vote_data_1).filterIt(it in indices2)
|
||||
|
||||
if not (slashable_vote_data_1.data != slashable_vote_data_2.data):
|
||||
warn("CaspSlash: invalid data")
|
||||
notice "CaspSlash: invalid data"
|
||||
return false
|
||||
|
||||
if not (len(intersection) >= 1):
|
||||
warn("CaspSlash: no intersection")
|
||||
notice "CaspSlash: no intersection"
|
||||
return false
|
||||
|
||||
if not (
|
||||
is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data) or
|
||||
is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)):
|
||||
warn("CaspSlash: surround or double vote check failed")
|
||||
notice "CaspSlash: surround or double vote check failed"
|
||||
return false
|
||||
|
||||
if not verify_slashable_vote_data(state, slashable_vote_data_1):
|
||||
warn("CaspSlash: invalid votes 1")
|
||||
notice "CaspSlash: invalid votes 1"
|
||||
return false
|
||||
|
||||
if not verify_slashable_vote_data(state, slashable_vote_data_2):
|
||||
warn("CaspSlash: invalid votes 2")
|
||||
notice "CaspSlash: invalid votes 2"
|
||||
return false
|
||||
|
||||
for i in intersection:
|
||||
|
@ -252,7 +256,7 @@ proc processAttestations(
|
|||
##
|
||||
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1
|
||||
if blck.body.attestations.len > MAX_ATTESTATIONS:
|
||||
warn("Attestation: too many!", attestations = blck.body.attestations.len)
|
||||
notice "Attestation: too many!", attestations = blck.body.attestations.len
|
||||
return false
|
||||
|
||||
if not blck.body.attestations.allIt(checkAttestation(state, it, flags)):
|
||||
|
@ -283,7 +287,7 @@ proc processExits(
|
|||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1
|
||||
if len(blck.body.exits) > MAX_EXITS:
|
||||
warn("Exit: too many!")
|
||||
notice "Exit: too many!"
|
||||
return false
|
||||
|
||||
for exit in blck.body.exits:
|
||||
|
@ -293,15 +297,15 @@ proc processExits(
|
|||
if not bls_verify(
|
||||
validator.pubkey, ZERO_HASH.data, exit.signature,
|
||||
get_domain(state.fork_data, exit.slot, DOMAIN_EXIT)):
|
||||
warn("Exit: invalid signature")
|
||||
notice "Exit: invalid signature"
|
||||
return false
|
||||
|
||||
if not (validator.exit_slot > state.slot + ENTRY_EXIT_DELAY):
|
||||
warn("Exit: exit/entry too close")
|
||||
notice "Exit: exit/entry too close"
|
||||
return false
|
||||
|
||||
if not (state.slot >= exit.slot):
|
||||
warn("Exit: bad slot")
|
||||
notice "Exit: bad slot"
|
||||
return false
|
||||
|
||||
initiate_validator_exit(state, exit.validator_index)
|
||||
|
@ -347,16 +351,21 @@ proc processBlock(
|
|||
# TODO probably better to do all verification first, then apply state changes
|
||||
|
||||
if not (blck.slot == state.slot):
|
||||
warn "Unexpected block slot number"
|
||||
notice "Unexpected block slot number",
|
||||
blockSlot = blck.slot,
|
||||
stateSlot = state.slot
|
||||
return false
|
||||
|
||||
# Spec does not have this check explicitly, but requires that this condition
|
||||
# holds - so we give verify it as well - this would happen naturally if
|
||||
# `blck.parent_root` was used in `processSlot` - but that doesn't cut it for
|
||||
# blockless slot processing.
|
||||
if not (blck.parent_root ==
|
||||
state.latest_block_roots[(state.slot - 1) mod LATEST_BLOCK_ROOTS_LENGTH]):
|
||||
warn("Unexpected parent root")
|
||||
let stateParentRoot =
|
||||
state.latest_block_roots[(state.slot - 1) mod LATEST_BLOCK_ROOTS_LENGTH]
|
||||
if not (blck.parent_root == stateParentRoot):
|
||||
notice "Unexpected parent root",
|
||||
blockParentRoot = blck.parent_root,
|
||||
stateParentRoot
|
||||
return false
|
||||
|
||||
if skipValidation notin flags:
|
||||
|
@ -365,11 +374,10 @@ proc processBlock(
|
|||
# type that omits some fields - this way, the compiler would guarantee
|
||||
# that we don't try to access fields that don't have a value yet
|
||||
if not verifyProposerSignature(state, blck):
|
||||
warn("Proposer signature not valid")
|
||||
notice "Proposer signature not valid"
|
||||
return false
|
||||
|
||||
if not processRandao(state, blck, flags):
|
||||
warn("Randao reveal failed")
|
||||
return false
|
||||
|
||||
processDepositRoot(state, blck)
|
||||
|
@ -727,8 +735,8 @@ func processEpoch(state: var BeaconState) =
|
|||
proc verifyStateRoot(state: BeaconState, blck: BeaconBlock): bool =
|
||||
let state_root = hash_tree_root_final(state)
|
||||
if state_root != blck.state_root:
|
||||
warn("Block: root verification failed",
|
||||
block_state_root = blck.state_root, state_root)
|
||||
notice "Block: root verification failed",
|
||||
block_state_root = blck.state_root, state_root
|
||||
false
|
||||
else:
|
||||
true
|
||||
|
|
|
@ -11,6 +11,7 @@ var
|
|||
|
||||
template now*: auto = fastEpochTime()
|
||||
|
||||
# TODO underflow when genesis has not yet happened!
|
||||
proc timeSinceGenesis*(s: BeaconState): Timestamp =
|
||||
Timestamp(int64(fastEpochTime() - s.genesis_time * 1000) -
|
||||
detectedClockDrift)
|
||||
|
@ -39,7 +40,7 @@ proc slotDistanceFromNow*(s: BeaconState): int64 =
|
|||
## Returns how many slots have passed since a particular BeaconState was finalized
|
||||
int64(s.timeSinceGenesis() div (SLOT_DURATION * 1000)) - int64(s.finalized_slot)
|
||||
|
||||
proc syncrhronizeClock*() {.async.} =
|
||||
proc synchronizeClock*() {.async.} =
|
||||
## This should determine the offset of the local clock against a global
|
||||
## trusted time (e.g. it can be obtained from multiple time servers).
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
set -eu
|
||||
|
||||
# Kill children on ctrl-c
|
||||
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
|
||||
|
||||
# Set a default value for the env vars usually supplied by nimbus Makefile
|
||||
: ${SKIP_BUILDS:=""}
|
||||
: ${BUILD_OUTPUTS_DIR:="./build"}
|
||||
|
@ -24,7 +27,7 @@ BEACON_NODE_BIN=$BUILD_OUTPUTS_DIR/beacon_node
|
|||
VALIDATOR_KEYGEN_BIN=$BUILD_OUTPUTS_DIR/validator_keygen
|
||||
|
||||
if [[ -z "$SKIP_BUILDS" ]]; then
|
||||
nim c -o:"$VALIDATOR_KEYGEN_BIN" beacon_chain/validator_keygen
|
||||
nim c -o:"$VALIDATOR_KEYGEN_BIN" -d:release beacon_chain/validator_keygen
|
||||
nim c -o:"$BEACON_NODE_BIN" beacon_chain/beacon_node
|
||||
fi
|
||||
|
||||
|
@ -76,12 +79,4 @@ for i in $(seq 0 9); do
|
|||
$BOOTSTRAP_NODES_FLAG &
|
||||
done
|
||||
|
||||
trap ctrl_c INT
|
||||
|
||||
function ctrl_c() {
|
||||
killall beacon_node
|
||||
exit 0
|
||||
}
|
||||
|
||||
sleep 100000
|
||||
|
||||
wait # Stop when all nodes have gone down
|
||||
|
|
Loading…
Reference in New Issue