clean up and add some logging (#70)

This commit is contained in:
Jacek Sieka 2019-01-25 11:35:22 -06:00 committed by GitHub
parent a0712e691a
commit e0a257406e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 66 deletions

View File

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

View File

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

View File

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

View File

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