From 9f55b4646c34484db88aa754715d0365d4f8f5ab Mon Sep 17 00:00:00 2001 From: Dustin Brody Date: Fri, 22 Mar 2019 18:33:12 +0000 Subject: [PATCH] More 0.5.1 spec updates (#195) * rm gone-in-0.5.0 Proposal, verifyBlockSignature, and slot check which moved to spec function processBlockHeader * mark get_attestation_participants and get_epoch_committee_count as 0.5.1; finish updating processAttestations to 0.5.1; add kludgy workaround for bug relating to get_winning_roots_etc using crosslink_data_root as index when we have that as ZERO_HASH for all leading to it confusing attesters on different shards; rm BeaconState.latest_attestations, which splits into previous_epoch_attestations and current_epoch_attestations * Fix CI due to removed latest_attestations field --- beacon_chain/beacon_node.nim | 9 +-- beacon_chain/spec/beaconstate.nim | 44 +++++++---- beacon_chain/spec/datatypes.nim | 18 +---- beacon_chain/spec/helpers.nim | 2 +- beacon_chain/state_transition.nim | 120 ++++++++++++++---------------- beacon_chain/validator_pool.nim | 8 +- research/serialized_sizes.nim | 24 +++--- tests/test_state_transition.nim | 2 +- tests/testutil.nim | 22 ++---- 9 files changed, 114 insertions(+), 135 deletions(-) diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index 64d8b4f65..4cef4f8d0 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -428,13 +428,8 @@ proc proposeBlock(node: BeaconNode, newBlock.state_root = node.state.root - let proposal = Proposal( - slot: slot.uint64, - block_root: Eth2Digest(data: signed_root(newBlock)), - signature: ValidatorSig(), - ) newBlock.signature = - await validator.signBlockProposal(node.state.data.fork, proposal) + await validator.signBlockProposal(node.state.data.fork, newBlock) # TODO what are we waiting for here? broadcast should never block, and never # fail... @@ -442,7 +437,7 @@ proc proposeBlock(node: BeaconNode, info "Block proposed", blck = shortLog(newBlock), - blockRoot = shortLog(proposal.block_root), + blockRoot = shortLog(Eth2Digest(data: signed_root(newBlock))), validator = shortValidatorKey(node, validator.idx), idx = validator.idx diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index b8614e419..419f0ea0a 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -334,7 +334,7 @@ func get_block_root*(state: BeaconState, doAssert slot < state.slot state.latest_block_roots[slot mod SLOTS_PER_HISTORICAL_ROOT] -# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#get_attestation_participants +# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#get_attestation_participants func get_attestation_participants*(state: BeaconState, attestation_data: AttestationData, bitfield: BitField): seq[ValidatorIndex] = @@ -350,10 +350,10 @@ func get_attestation_participants*(state: BeaconState, # TODO Linear search through shard list? borderline ok, it's a small list # TODO iterator candidate - ## Return the participant indices at for the ``attestation_data`` and - ## ``bitfield``. + # Find the committee in the list with the desired shard let crosslink_committees = get_crosslink_committees_at_slot( state, attestation_data.slot) + doAssert anyIt( crosslink_committees, it[1] == attestation_data.shard) @@ -442,8 +442,7 @@ func update_validator_registry*(state: var BeaconState) = state.validator_registry_update_epoch = current_epoch -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#attestations -# with last half or so still not fully converted from 0.4.0 (tag, remove when done) +# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#attestations proc checkAttestation*( state: var BeaconState, attestation: Attestation, flags: UpdateFlags): bool = ## Check that an attestation follows the rules of being included in the state @@ -499,34 +498,50 @@ proc checkAttestation*( state_slot = humaneSlotNum(state.slot)) return - if not (state.latest_crosslinks[attestation.data.shard] in [ - attestation.data.previous_crosslink, - Crosslink( - crosslink_data_root: attestation.data.crosslink_data_root, - epoch: slot_to_epoch(attestation.data.slot))]): + # Check that the crosslink data is valid + let acceptable_crosslink_data = @[ + # Case 1: Latest crosslink matches the one in the state + attestation.data.previous_crosslink, + + # Case 2: State has already been updated, state's latest crosslink matches + # the crosslink the attestation is trying to create + Crosslink( + crosslink_data_root: attestation.data.crosslink_data_root, + epoch: slot_to_epoch(attestation.data.slot) + ) + ] + if not (state.latest_crosslinks[attestation.data.shard] in + acceptable_crosslink_data): warn("Unexpected crosslink shard", state_latest_crosslinks_attestation_data_shard = state.latest_crosslinks[attestation.data.shard], attestation_data_previous_crosslink = attestation.data.previous_crosslink, epoch = humaneEpochNum(slot_to_epoch(attestation.data.slot)), - crosslink_data_root = attestation.data.crosslink_data_root) + actual_epoch = slot_to_epoch(attestation.data.slot), + crosslink_data_root = attestation.data.crosslink_data_root, + acceptable_crosslink_data = acceptable_crosslink_data) return - doAssert allIt(attestation.custody_bitfield.bits, it == 0) #TO BE REMOVED IN PHASE 1 + # Attestation must be nonempty! doAssert anyIt(attestation.aggregation_bitfield.bits, it != 0) + # Custody must be empty (to be removed in phase 1) + doAssert allIt(attestation.custody_bitfield.bits, it == 0) + + # Get the committee for the specific shard that this attestation is for let crosslink_committee = mapIt( filterIt(get_crosslink_committees_at_slot(state, attestation.data.slot), it.shard == attestation.data.shard), it.committee)[0] + # Custody bitfield must be a subset of the attestation bitfield doAssert allIt(0 ..< len(crosslink_committee), if not get_bitfield_bit(attestation.aggregation_bitfield, it): - # Should always be true in phase 0, because of above assertion not get_bitfield_bit(attestation.custody_bitfield, it) else: true) + # Verify aggregate signature let participants = get_attestation_participants( state, attestation.data, attestation.aggregation_bitfield) @@ -557,7 +572,7 @@ proc checkAttestation*( DOMAIN_ATTESTATION), ) - # To be removed in Phase1: + # Crosslink data root is zero (to be removed in phase 1) if attestation.data.crosslink_data_root != ZERO_HASH: warn("Invalid crosslink data root") return @@ -573,7 +588,6 @@ func prepare_validator_for_withdrawal*(state: var BeaconState, index: ValidatorI validator.withdrawable_epoch = get_current_epoch(state) + MIN_VALIDATOR_WITHDRAWABILITY_DELAY -# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/validator/0_beacon-chain-validator.md#attestations-1 proc makeAttestationData*( state: BeaconState, shard: uint64, beacon_block_root: Eth2Digest): AttestationData = diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 261bc7e0c..fd692522c 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -46,7 +46,7 @@ type Epoch* = distinct uint64 const - SPEC_VERSION* = "0.5.0" ## \ + SPEC_VERSION* = "0.5.1" ## \ ## Spec version we're aiming to be compatible with, right now ## TODO: improve this scheme once we can negotiate versions in protocol @@ -360,18 +360,7 @@ type voluntary_exits*: seq[VoluntaryExit] transfers*: seq[Transfer] - # https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#proposal - Proposal* = object - slot*: uint64 ##\ - ## Slot number - - block_root*: Eth2Digest ##\ - ## Block root - - signature*: ValidatorSig ##\ - ## Signature - - # https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#beaconstate + # https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#beaconstate BeaconState* = object slot*: Slot genesis_time*: uint64 @@ -423,9 +412,6 @@ type ## `latest_block_header.state_root == ZERO_HASH` temporarily historical_roots*: seq[Eth2Digest] - # TOOD remove, gone in 0.5 - latest_attestations*: seq[PendingAttestation] - # Ethereum 1.0 chain data latest_eth1_data*: Eth1Data eth1_data_votes*: seq[Eth1DataVote] diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index 69e91bea5..33967f02a 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -132,7 +132,7 @@ func get_active_validator_indices*(validators: openArray[Validator], epoch: Epoc if is_active_validator(val, epoch): result.add idx.ValidatorIndex -# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#get_epoch_committee_count +# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#get_epoch_committee_count func get_epoch_committee_count*(active_validator_count: int): uint64 = clamp( active_validator_count div SLOTS_PER_EPOCH div TARGET_COMMITTEE_SIZE, diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index 5b35e18b2..35fbc9721 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -39,25 +39,6 @@ func flatten[T](v: openArray[seq[T]]): seq[T] = # TODO not in nim - doh. for x in v: result.add x -# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#block-signature -func verifyBlockSignature(state: BeaconState, blck: BeaconBlock): bool = - ## When creating a block, the proposer will sign a version of the block that - ## doesn't contain the data (chicken and egg), then add the signature to that - ## block. Here, we check that the signature is correct by repeating the same - ## process. - let - proposer = - state.validator_registry[get_beacon_proposer_index(state, state.slot)] - proposal = Proposal( - slot: blck.slot.uint64, - block_root: Eth2Digest(data: signed_root(blck)), - signature: blck.signature) - bls_verify( - proposer.pubkey, - signed_root(proposal), - proposal.signature, - get_domain(state.fork, get_current_epoch(state), DOMAIN_BEACON_BLOCK)) - # https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#block-header proc processBlockHeader( state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool = @@ -70,13 +51,16 @@ proc processBlockHeader( ## https://github.com/ethereum/eth2.0-specs/pull/816/files remove when ## switched to 0.5.1 - if not (blck.previous_block_root.data == - signed_root(state.latest_block_header)): - notice "Block header: previous block root mismatch", - previous_block_root = blck.previous_block_root, - latest_block_header = state.latest_block_header, - latest_block_header_root = hash_tree_root_final(state.latest_block_header) - return false + when false: + ## TODO Re-enable when it works; currently, some code in processBlock + ## also checks this invariant in a different way. tag as 0.4.0. + if not (blck.previous_block_root.data == + signed_root(state.latest_block_header)): + notice "Block header: previous block root mismatch", + previous_block_root = blck.previous_block_root, + latest_block_header = state.latest_block_header, + latest_block_header_root = hash_tree_root_final(state.latest_block_header) + return false state.latest_block_header = get_temporary_block_header(blck) @@ -277,7 +261,7 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool = true -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#attestations +# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#attestations proc processAttestations( state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool = ## Each block includes a number of attestations that the proposer chose. Each @@ -294,18 +278,23 @@ proc processAttestations( return false # All checks passed - update state - state.latest_attestations.add blck.body.attestations.mapIt( - PendingAttestation( - data: it.data, - aggregation_bitfield: it.aggregation_bitfield, - custody_bitfield: it.custody_bitfield, - inclusion_slot: state.slot, + # Apply the attestations + for attestation in blck.body.attestations: + let pending_attestation = PendingAttestation( + data: attestation.data, + aggregation_bitfield: attestation.aggregation_bitfield, + custody_bitfield: attestation.custody_bitfield, + inclusion_slot: state.slot ) - ) + + if slot_to_epoch(attestation.data.slot) == get_current_epoch(state): + state.current_epoch_attestations.add(pending_attestation) + else: + state.previous_epoch_attestations.add(pending_attestation) true -# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#deposits-1 +# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#deposits-1 func processDeposits(state: var BeaconState, blck: BeaconBlock): bool = true @@ -484,18 +473,11 @@ proc processBlock( # TODO when there's a failure, we should reset the state! # TODO probably better to do all verification first, then apply state changes - # https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#slot-1 - if not (blck.slot == state.slot): - notice "Unexpected block slot number", - blockSlot = humaneSlotNum(blck.slot), - stateSlot = humaneSlotNum(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.previous_block_root` was used in `processSlot` - but that doesn't cut it for # blockless slot processing. - # TODO compare with check in processBlockHeader, might be redundant + # TODO compare with processBlockHeader check, might be redundant and to be removed let stateParentRoot = state.latest_block_roots[(state.slot - 1) mod SLOTS_PER_HISTORICAL_ROOT] if not (blck.previous_block_root == stateParentRoot): @@ -504,18 +486,9 @@ proc processBlock( stateParentRoot return false - # TODO Technically, we could make processBlock take a generic type instead - # of BeaconBlock - we would then have an intermediate `ProposedBlock` - # 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 processBlockHeader(state, blck, flags): - # notice "Block header not valid", slot = humaneSlotNum(state.slot) - # return false - # TODO this starts requiring refactoring blockpool, etc - if skipValidation notin flags: - if not verifyBlockSignature(state, blck): - notice "Block signature not valid", slot = humaneSlotNum(state.slot) - return false + if not processBlockHeader(state, blck, flags): + notice "Block header not valid", slot = humaneSlotNum(state.slot) + return false if not processRandao(state, blck, flags): return false @@ -542,7 +515,7 @@ proc processBlock( true -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#helper-functions-1 +# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#helper-functions-1 func get_current_total_balance(state: BeaconState): Gwei = return get_total_balance( state, @@ -615,7 +588,9 @@ func get_winning_root_and_participants(state: BeaconState, shard: Shard): return (ZERO_HASH, @[]) func get_attestations_for(root: Eth2Digest): seq[PendingAttestation] = - filterIt(valid_attestations, it.data.crosslink_data_root == root) + filterIt( + valid_attestations, + it.data.crosslink_data_root == root) ## Winning crosslink root is the root with the most votes for it, ties broken ## in favor of lexicographically higher hash @@ -641,7 +616,7 @@ func inclusion_slots(state: BeaconState): auto = result = initTable[ValidatorIndex, Slot]() let previous_epoch_attestations = - state.latest_attestations.filterIt( + state.previous_epoch_attestations.filterIt( get_previous_epoch(state) == slot_to_epoch(it.data.slot)) ## TODO switch previous_epoch_attestations to state.foo, @@ -659,7 +634,7 @@ func inclusion_distances(state: BeaconState): auto = result = initTable[ValidatorIndex, Slot]() let previous_epoch_attestations = - state.latest_attestations.filterIt( + state.previous_epoch_attestations.filterIt( get_previous_epoch(state) == slot_to_epoch(it.data.slot)) ## TODO switch previous_epoch_attestations to state.foo, @@ -741,13 +716,20 @@ func update_justification_and_finalization(state: var BeaconState) = state.finalized_root = get_block_root(state, get_epoch_start_slot(new_finalized_epoch)) -# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#crosslinks +# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#crosslinks func process_crosslinks(state: var BeaconState) = let current_epoch = get_current_epoch(state) previous_epoch = current_epoch - 1 next_epoch = current_epoch + 1 - for slot in get_epoch_start_slot(previous_epoch).uint64 ..< + ## TODO is it actually correct to be setting state.latest_crosslinks[shard] + ## to something pre-GENESIS_EPOCH, ever? I guess the intent is if there are + ## a quorum of participants for get_epoch_start_slot(previous_epoch), when + ## state.slot == GENESIS_SLOT, then there will be participants for a quorum + ## in the current-epoch (i.e. genesis epoch) version of that shard? + #for slot in get_epoch_start_slot(previous_epoch).uint64 ..< + for slot in max( + GENESIS_SLOT.uint64, get_epoch_start_slot(previous_epoch).uint64) ..< get_epoch_start_slot(next_epoch).uint64: for cas in get_crosslink_committees_at_slot(state, slot): let @@ -756,7 +738,11 @@ func process_crosslinks(state: var BeaconState) = get_winning_root_and_participants(state, shard) participating_balance = get_total_balance(state, participants) total_balance = get_total_balance(state, crosslink_committee) + if 3'u64 * participating_balance >= 2'u64 * total_balance: + # Check not from spec; seems kludgy + doAssert slot >= GENESIS_SLOT + state.latest_crosslinks[shard] = Crosslink( epoch: slot_to_epoch(slot), crosslink_data_root: winning_root @@ -1117,14 +1103,20 @@ proc advanceState*( ## We now define the state transition function. At a high level the state ## transition is made up of four parts: - # 1. State caching, which happens at the start of every slot. + ## 1. State caching, which happens at the start of every slot. + ## The state caching, caches the state root of the previous slot cacheState(state) - ## (2) The per-epoch transitions, which happens at the start of the first + ## 2. The per-epoch transitions, which happens at the start of the first ## slot of every epoch. + ## The per-epoch transitions focus on the validator registry, including + ## adjusting balances and activating and exiting validators, as well as + ## processing crosslinks and managing block justification/finalization. processEpoch(state) - # (3) The per-slot transitions, which happens at every slot. + ## 3. The per-slot transitions, which happens at every slot. + ## The per-slot transitions focus on the slot counter and block roots + ## records updates. processSlot(state, previous_block_root) proc updateState*( diff --git a/beacon_chain/validator_pool.nim b/beacon_chain/validator_pool.nim index ede42c3d5..810c518f1 100644 --- a/beacon_chain/validator_pool.nim +++ b/beacon_chain/validator_pool.nim @@ -25,13 +25,11 @@ proc getValidator*(pool: ValidatorPool, pool.validators.getOrDefault(validatorKey) proc signBlockProposal*(v: AttachedValidator, fork: Fork, - proposal: Proposal): Future[ValidatorSig] {.async.} = + blck: BeaconBlock): Future[ValidatorSig] {.async.} = if v.kind == inProcess: await sleepAsync(1) - let proposalRoot = hash_tree_root_final(proposal) - - result = bls_sign(v.privKey, signed_root(proposal), - get_domain(fork, slot_to_epoch(proposal.slot), DOMAIN_BEACON_BLOCK)) + result = bls_sign(v.privKey, signed_root(blck), + get_domain(fork, slot_to_epoch(blck.slot), DOMAIN_BEACON_BLOCK)) else: # TODO: # send RPC diff --git a/research/serialized_sizes.nim b/research/serialized_sizes.nim index ba9330695..953f85a39 100644 --- a/research/serialized_sizes.nim +++ b/research/serialized_sizes.nim @@ -10,16 +10,20 @@ proc stateSize(deposits: int, maxContent = false) = deposits, {skipValidation}), 0, Eth1Data(), {skipValidation}) if maxContent: - # TODO verify this is correct, but generally we collect up to two epochs - # of attestations, and each block has a cap on the number of - # attestations it may hold, so we'll just add so many of them - state.latest_attestations.setLen(MAX_ATTESTATIONS * SLOTS_PER_EPOCH * 2) - let - crosslink_committees = get_crosslink_committees_at_slot(state, 0.Slot) - validatorsPerCommittee = - len(crosslink_committees[0].committee) # close enough.. - for a in state.latest_attestations.mitems(): - a.aggregation_bitfield = BitField.init(validatorsPerCommittee) + # TODO: state.latest_attestations was removed + # in https://github.com/status-im/nim-beacon-chain/pull/195 + raise newException(ValueError, "Not supported at the moment") + + # # TODO verify this is correct, but generally we collect up to two epochs + # # of attestations, and each block has a cap on the number of + # # attestations it may hold, so we'll just add so many of them + # state.latest_attestations.setLen(MAX_ATTESTATIONS * SLOTS_PER_EPOCH * 2) + # let + # crosslink_committees = get_crosslink_committees_at_slot(state, 0.Slot) + # validatorsPerCommittee = + # len(crosslink_committees[0].committee) # close enough.. + # for a in state.latest_attestations.mitems(): + # a.aggregation_bitfield = BitField.init(validatorsPerCommittee) echo "Validators: ", deposits, ", total: ", SSZ.encode(state).len dispatch(stateSize) diff --git a/tests/test_state_transition.nim b/tests/test_state_transition.nim index c530f9aa4..be79aa26f 100644 --- a/tests/test_state_transition.nim +++ b/tests/test_state_transition.nim @@ -101,7 +101,7 @@ suite "Block processing": discard updateState(state, previous_block_root, new_block, {}) check: - state.latest_attestations.len == 1 + state.current_epoch_attestations.len == 1 while state.slot < 191: advanceState(state, previous_block_root) diff --git a/tests/testutil.nim b/tests/testutil.nim index f3d2e5a36..bd4b13bfb 100644 --- a/tests/testutil.nim +++ b/tests/testutil.nim @@ -118,31 +118,21 @@ proc addBlock*( # can set the state root in order to be able to create a valid signature new_block.state_root = Eth2Digest(data: hash_tree_root(state)) - let - proposerPrivkey = hackPrivKey(proposer) - - # Once we've collected all the state data, we sign the block data along with - # some book-keeping values - signed_data = Proposal( - slot: new_block.slot.uint64, - block_root: Eth2Digest(data: signed_root(new_block)), - signature: ValidatorSig(), - ) - proposal_hash = signed_root(signed_data) - + let proposerPrivkey = hackPrivKey(proposer) doAssert proposerPrivkey.pubKey() == proposer.pubkey, "signature key should be derived from private key! - wrong privkey?" if skipValidation notin flags: + let block_root = signed_root(new_block) # We have a signature - put it in the block and we should be done! new_block.signature = - bls_sign(proposerPrivkey, proposal_hash, - get_domain(state.fork, slot_to_epoch(state.slot), DOMAIN_BEACON_BLOCK)) + bls_sign(proposerPrivkey, block_root, + get_domain(state.fork, slot_to_epoch(new_block.slot), DOMAIN_BEACON_BLOCK)) doAssert bls_verify( proposer.pubkey, - proposal_hash, new_block.signature, - get_domain(state.fork, slot_to_epoch(state.slot), DOMAIN_BEACON_BLOCK)), + block_root, new_block.signature, + get_domain(state.fork, slot_to_epoch(new_block.slot), DOMAIN_BEACON_BLOCK)), "we just signed this message - it should pass verification!" new_block