diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 51f8ef104..63b32facf 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -399,11 +399,11 @@ type data*: BeaconState root*: Eth2Digest # hash_tree_root(data) - # TODO remove some of these, or otherwise coordinate with EpochRef StateCache* = object shuffled_active_validator_indices*: Table[Epoch, seq[ValidatorIndex]] committee_count_cache*: Table[Epoch, uint64] + beacon_proposer_indices*: Table[Slot, Option[ValidatorIndex]] func shortValidatorKey*(state: BeaconState, validatorIdx: int): string = ($state.validators[validatorIdx].pubkey)[0..7] diff --git a/beacon_chain/spec/validator.nim b/beacon_chain/spec/validator.nim index ce6f1ad98..1137fffd0 100644 --- a/beacon_chain/spec/validator.nim +++ b/beacon_chain/spec/validator.nim @@ -196,6 +196,12 @@ func compute_proposer_index(state: BeaconState, indices: seq[ValidatorIndex], # https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#get_beacon_proposer_index func get_beacon_proposer_index*(state: BeaconState, cache: var StateCache, slot: Slot): Option[ValidatorIndex] = + try: + if slot in cache.beacon_proposer_indices: + return cache.beacon_proposer_indices[slot] + except KeyError: + raiseAssert("Cached entries are added before use") + # Return the beacon proposer index at the current slot. let epoch = get_current_epoch(state) @@ -215,14 +221,16 @@ func get_beacon_proposer_index*(state: BeaconState, cache: var StateCache, slot: indices = sorted(cache.shuffled_active_validator_indices[epoch], system.cmp) - compute_proposer_index(state, indices, seed) + cache.beacon_proposer_indices[slot] = + compute_proposer_index(state, indices, seed) + cache.beacon_proposer_indices[slot] except KeyError: raiseAssert("Cached entries are added before use") # https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#get_beacon_proposer_index -func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache): +func get_beacon_proposer_index*(state: BeaconState, cache: var StateCache): Option[ValidatorIndex] = - get_beacon_proposer_index(state, stateCache, state.slot) + get_beacon_proposer_index(state, cache, state.slot) # Not from spec # TODO: cache the results from this and reuse in subsequent calls to get_beacon_proposer_index diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index 3f6cb1c78..40e769b37 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -273,11 +273,11 @@ proc makeBeaconBlock*( graffiti: Eth2Digest, attestations: seq[Attestation], deposits: seq[Deposit], - rollback: RollbackHashedProc): Option[BeaconBlock] = + rollback: RollbackHashedProc, + cache: var StateCache): Option[BeaconBlock] = ## Create a block for the given state. The last block applied to it must be ## the one identified by parent_root and process_slots must be called up to ## the slot for which a block is to be created. - var cache = get_empty_per_epoch_cache() # To create a block, we'll first apply a partial block to the state, skipping # some validations. diff --git a/beacon_chain/validator_duties.nim b/beacon_chain/validator_duties.nim index d3a9e9957..12d3e0288 100644 --- a/beacon_chain/validator_duties.nim +++ b/beacon_chain/validator_duties.nim @@ -176,6 +176,7 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode, doAssert v.addr == addr poolPtr.tmpState.data poolPtr.tmpState = poolPtr.headState + var cache = get_empty_per_epoch_cache() let message = makeBeaconBlock( hashedState, validator_index, @@ -185,7 +186,8 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode, graffiti, node.attestationPool.getAttestationsForBlock(state), deposits, - restore) + restore, + cache) if message.isSome(): # TODO this restore is needed because otherwise tmpState will be internally diff --git a/ncli/ncli_query.nim b/ncli/ncli_query.nim index a148cf452..e7833a21a 100644 --- a/ncli/ncli_query.nim +++ b/ncli/ncli_query.nim @@ -1,8 +1,7 @@ import confutils, os, strutils, json_serialization, stew/byteutils, - ../beacon_chain/spec/[crypto, datatypes], - ../beacon_chain/ssz/dynamic_navigator + ../beacon_chain/spec/[crypto, datatypes] type QueryCmd* = enum diff --git a/research/block_sim.nim b/research/block_sim.nim index e73712e3a..0b92fa7c8 100644 --- a/research/block_sim.nim +++ b/research/block_sim.nim @@ -117,7 +117,8 @@ cli do(slots = SLOTS_PER_EPOCH * 6, Eth2Digest(), attPool.getAttestationsForBlock(state), @[], - noRollback) + noRollback, + cache) var newBlock = SignedBeaconBlock( diff --git a/research/state_sim.nim b/research/state_sim.nim index 032e342e8..8617e7a98 100644 --- a/research/state_sim.nim +++ b/research/state_sim.nim @@ -95,7 +95,8 @@ cli do(slots = SLOTS_PER_EPOCH * 6, withTimer(timers[t]): signedBlock = addTestBlock( - state[], latest_block_root, attestations = blockAttestations, flags = flags) + state[], latest_block_root, cache, attestations = blockAttestations, + flags = flags) latest_block_root = withTimerRet(timers[tHashBlock]): hash_tree_root(signedBlock.message) diff --git a/tests/test_attestation_pool.nim b/tests/test_attestation_pool.nim index 3bccc4015..119c430e5 100644 --- a/tests/test_attestation_pool.nim +++ b/tests/test_attestation_pool.nim @@ -149,8 +149,9 @@ suiteReport "Attestation pool processing" & preset(): attestations.len == 1 timedTest "Fork choice returns latest block with no attestations": + var cache = get_empty_per_epoch_cache() let - b1 = addTestBlock(state.data, blockPool.tail.root) + b1 = addTestBlock(state.data, blockPool.tail.root, cache) b1Root = hash_tree_root(b1.message) b1Add = blockPool.add(b1Root, b1)[] head = pool.selectHead() @@ -159,7 +160,7 @@ suiteReport "Attestation pool processing" & preset(): head == b1Add let - b2 = addTestBlock(state.data, b1Root) + b2 = addTestBlock(state.data, b1Root, cache) b2Root = hash_tree_root(b2.message) b2Add = blockPool.add(b2Root, b2)[] head2 = pool.selectHead() @@ -170,7 +171,7 @@ suiteReport "Attestation pool processing" & preset(): timedTest "Fork choice returns block with attestation": var cache = get_empty_per_epoch_cache() let - b10 = makeTestBlock(state.data, blockPool.tail.root) + b10 = makeTestBlock(state.data, blockPool.tail.root, cache) b10Root = hash_tree_root(b10.message) b10Add = blockPool.add(b10Root, b10)[] head = pool.selectHead() @@ -179,7 +180,7 @@ suiteReport "Attestation pool processing" & preset(): head == b10Add let - b11 = makeTestBlock(state.data, blockPool.tail.root, + b11 = makeTestBlock(state.data, blockPool.tail.root, cache, graffiti = Eth2Digest(data: [1'u8, 0, 0, 0 ,0 ,0 ,0 ,0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) ) b11Root = hash_tree_root(b11.message) diff --git a/tests/test_block_pool.nim b/tests/test_block_pool.nim index 3e5df50c4..41a11b462 100644 --- a/tests/test_block_pool.nim +++ b/tests/test_block_pool.nim @@ -10,7 +10,7 @@ import options, sequtils, unittest, ./testutil, ./testblockutil, - ../beacon_chain/spec/[datatypes, digest, helpers], + ../beacon_chain/spec/[datatypes, digest, helpers, validator], ../beacon_chain/[beacon_node_types, block_pool, state_transition, ssz] suiteReport "BlockRef and helpers" & preset(): @@ -88,9 +88,10 @@ suiteReport "Block pool processing" & preset(): db = makeTestDB(SLOTS_PER_EPOCH) pool = BlockPool.init(db) stateData = newClone(pool.loadTailState()) - b1 = addTestBlock(stateData.data, pool.tail.root) + cache = get_empty_per_epoch_cache() + b1 = addTestBlock(stateData.data, pool.tail.root, cache) b1Root = hash_tree_root(b1.message) - b2 = addTestBlock(stateData.data, b1Root) + b2 = addTestBlock(stateData.data, b1Root, cache) b2Root {.used.} = hash_tree_root(b2.message) timedTest "getRef returns nil for missing blocks": @@ -132,7 +133,7 @@ suiteReport "Block pool processing" & preset(): process_slots(stateData.data, stateData.data.data.slot + 1) let - b4 = addTestBlock(stateData.data, b2Root) + b4 = addTestBlock(stateData.data, b2Root, cache) b4Root = hash_tree_root(b4.message) b4Add = pool.add(b4Root, b4)[] @@ -284,12 +285,13 @@ when const_preset == "minimal": # These require some minutes in mainnet var db = makeTestDB(SLOTS_PER_EPOCH) pool = BlockPool.init(db) + cache = get_empty_per_epoch_cache() timedTest "prune heads on finalization" & preset(): block: # Create a fork that will not be taken var - blck = makeTestBlock(pool.headState.data, pool.head.blck.root) + blck = makeTestBlock(pool.headState.data, pool.head.blck.root, cache) check: pool.add(hash_tree_root(blck.message), blck).isOk for i in 0 ..< (SLOTS_PER_EPOCH * 6): @@ -299,9 +301,8 @@ when const_preset == "minimal": # These require some minutes in mainnet pool.tail.children.len == 2 pool.heads.len == 2 var - cache = get_empty_per_epoch_cache() blck = makeTestBlock( - pool.headState.data, pool.head.blck.root, + pool.headState.data, pool.head.blck.root, cache, attestations = makeFullAttestations( pool.headState.data.data, pool.head.blck.root, pool.headState.data.data.slot, cache, {})) @@ -332,7 +333,7 @@ when const_preset == "minimal": # These require some minutes in mainnet for i in 0 ..< (SLOTS_PER_EPOCH * 6 - 2): var blck = makeTestBlock( - pool.headState.data, pool.head.blck.root, + pool.headState.data, pool.head.blck.root, cache, attestations = makeFullAttestations( pool.headState.data.data, pool.head.blck.root, pool.headState.data.data.slot, cache, {})) @@ -345,7 +346,7 @@ when const_preset == "minimal": # These require some minutes in mainnet pool.headState.data, Slot(SLOTS_PER_EPOCH * 6 + 2) ) var blck = makeTestBlock( - pool.headState.data, pool.head.blck.root, + pool.headState.data, pool.head.blck.root, cache, attestations = makeFullAttestations( pool.headState.data.data, pool.head.blck.root, pool.headState.data.data.slot, cache, {})) diff --git a/tests/test_state_transition.nim b/tests/test_state_transition.nim index 7baa4d1c4..5b185067f 100644 --- a/tests/test_state_transition.nim +++ b/tests/test_state_transition.nim @@ -36,7 +36,8 @@ suiteReport "Block processing" & preset(): timedTest "Passes from genesis state, empty block" & preset(): var previous_block_root = hash_tree_root(genesisBlock.message) - new_block = makeTestBlock(state[], previous_block_root) + cache = get_empty_per_epoch_cache() + new_block = makeTestBlock(state[], previous_block_root, cache) let block_ok = state_transition(state[], new_block, {}, noRollback) @@ -53,9 +54,10 @@ suiteReport "Block processing" & preset(): timedTest "Passes through epoch update, empty block" & preset(): var previous_block_root = genesisRoot + cache = get_empty_per_epoch_cache() for i in 1..SLOTS_PER_EPOCH.int: - let new_block = makeTestBlock(state[], previous_block_root) + let new_block = makeTestBlock(state[], previous_block_root, cache) let block_ok = state_transition(state[], new_block, {}, noRollback) @@ -90,7 +92,7 @@ suiteReport "Block processing" & preset(): state[], GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1) let - new_block = makeTestBlock(state[], previous_block_root, + new_block = makeTestBlock(state[], previous_block_root, cache, attestations = @[attestation] ) check state_transition(state[], new_block, {}, noRollback) diff --git a/tests/testblockutil.nim b/tests/testblockutil.nim index 782a1b6af..66c3e6f5d 100644 --- a/tests/testblockutil.nim +++ b/tests/testblockutil.nim @@ -89,13 +89,13 @@ func signBlock*( proc addTestBlock*( state: var HashedBeaconState, parent_root: Eth2Digest, + cache: var StateCache, eth1_data = Eth1Data(), attestations = newSeq[Attestation](), deposits = newSeq[Deposit](), graffiti = Eth2Digest(), flags: set[UpdateFlag] = {}): SignedBeaconBlock = # Create and add a block to state - state will advance by one slot! - var cache = get_empty_per_epoch_cache() advance_slot(state, err(Opt[Eth2Digest]), flags, cache) let @@ -122,7 +122,8 @@ proc addTestBlock*( graffiti, attestations, deposits, - noRollback) + noRollback, + cache) doAssert message.isSome(), "Should have created a valid block!" @@ -136,6 +137,7 @@ proc addTestBlock*( proc makeTestBlock*( state: HashedBeaconState, parent_root: Eth2Digest, + cache: var StateCache, eth1_data = Eth1Data(), attestations = newSeq[Attestation](), deposits = newSeq[Deposit](), @@ -147,7 +149,8 @@ proc makeTestBlock*( # because the block includes the state root. var tmpState = newClone(state) addTestBlock( - tmpState[], parent_root, eth1_data, attestations, deposits, graffiti, flags) + tmpState[], parent_root, cache, eth1_data, attestations, deposits, + graffiti, flags) proc makeAttestation*( state: BeaconState, beacon_block_root: Eth2Digest,