clean up block creation

* consistently use state at new block slot to produce block
* factor out signature funcs
* fix missing block application test
This commit is contained in:
Jacek Sieka 2020-03-20 00:48:03 +01:00 committed by tersec
parent ccace9a034
commit 689bcf71c4
15 changed files with 224 additions and 239 deletions

View File

@ -22,7 +22,8 @@
import
options,
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator],
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator,
state_transition_block],
./attestation_pool, ./beacon_node_types, ./ssz
# TODO gossipsub validation lives somewhere, maybe here
@ -33,13 +34,6 @@ const
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#configuration
ATTESTATION_PROPAGATION_SLOT_RANGE = 32
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/validator/0_beacon-chain-validator.md#aggregation-selection
func get_slot_signature(state: BeaconState, slot: Slot, privkey: ValidatorPrivKey):
ValidatorSig =
let domain =
get_domain(state, DOMAIN_BEACON_ATTESTER, compute_epoch_at_slot(slot))
bls_sign(privkey, hash_tree_root(slot).data, domain)
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregation-selection
func is_aggregator(state: BeaconState, slot: Slot, index: uint64,
slot_signature: ValidatorSig): bool =
@ -58,7 +52,7 @@ proc aggregate_attestations*(
let
slot = state.slot - 2
slot_signature = get_slot_signature(state, slot, privkey)
slot_signature = get_slot_signature(state.fork, slot, privkey)
if slot < 0:
return none(AggregateAndProof)

View File

@ -266,10 +266,12 @@ proc add*(pool: var AttestationPool, attestation: Attestation) =
pool.addResolved(blck, attestation)
proc getAttestationsForBlock*(
pool: AttestationPool, state: BeaconState,
newBlockSlot: Slot): seq[Attestation] =
pool: AttestationPool, state: BeaconState): seq[Attestation] =
## Retrieve attestations that may be added to a new block at the slot of the
## given state
logScope: pcs = "retrieve_attestation"
let newBlockSlot = state.slot
if newBlockSlot < (GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY):
debug "Too early for attestations",
newBlockSlot = shortLog(newBlockSlot),
@ -327,8 +329,7 @@ proc getAttestationsForBlock*(
# TODO we're checking signatures here every time which is very slow - this
# is needed because validate does nothing for now and we don't want
# to include a broken attestation
if not check_attestation(
state, attestation, {nextSlot}, cache):
if not check_attestation(state, attestation, {}, cache):
continue
for v in a.validations[1..^1]:

View File

@ -10,7 +10,8 @@ import
eth/p2p/enode, eth/[keys, async_utils], eth/p2p/discoveryv5/enr,
# Local modules
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network],
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network,
state_transition_block],
conf, time, state_transition, beacon_chain_db, validator_pool, extras,
attestation_pool, block_pool, eth2_network, eth2_discovery,
beacon_node_types, mainchain_monitor, version, ssz, ssz/dynamic_navigator,
@ -381,8 +382,8 @@ proc proposeBlock(node: BeaconNode,
cat = "fastforward"
return head
# Advance state to the slot immediately preceding the one we're creating a
# block for - potentially we will be processing empty slots along the way.
# Advance state to the slot that we're proposing for - this is the equivalent
# of running `process_slots` up to the slot of the new block.
let (nroot, nblck) = node.blockPool.withState(
node.blockPool.tmpState, head.atSlot(slot)):
let (eth1data, deposits) =
@ -394,45 +395,27 @@ proc proposeBlock(node: BeaconNode,
(node.mainchainMonitor.eth1Data,
node.mainchainMonitor.getPendingDeposits())
# To create a block, we'll first apply a partial block to the state, skipping
# some validations.
let
fork = state.fork
blockBody = BeaconBlockBody(
randao_reveal: validator.genRandaoReveal(fork, slot),
eth1_data: eth1data,
attestations:
node.attestationPool.getAttestationsForBlock(state, slot),
deposits: deposits)
var cache = get_empty_per_epoch_cache()
let proposer_index = get_beacon_proposer_index(state, cache)
if proposer_index.isNone:
doAssert false, "proposeBlock: missing proposer index"
let message = makeBeaconBlock(
state,
head.root,
validator.genRandaoReveal(state.fork, slot),
eth1data,
Eth2Digest(),
node.attestationPool.getAttestationsForBlock(state),
deposits)
if not message.isSome():
return head # already logged elsewhere!
var
newBlock = SignedBeaconBlock(
message: BeaconBlock(
slot: slot,
proposer_index: proposer_index.get.uint64,
parent_root: head.root,
body: blockBody))
tmpState = hashedState
discard state_transition(tmpState, newBlock, {skipStateRootValidation})
# TODO only enable in fast-fail debugging situations
# otherwise, bad attestations can bring down network
# doAssert ok # TODO: err, could this fail somehow?
newBlock.message.state_root = tmpState.root
message: message.get()
)
let blockRoot = hash_tree_root(newBlock.message)
# Careful, state no longer valid after here..
# We use the fork from the pre-newBlock state which should be fine because
# fork carries two epochs, so even if it's a fork block, the right thing
# will happen here
# Careful, state no longer valid after here because of the await..
newBlock.signature =
await validator.signBlockProposal(fork, slot, blockRoot)
await validator.signBlockProposal(state.fork, slot, blockRoot)
(blockRoot, newBlock)
@ -454,10 +437,12 @@ proc proposeBlock(node: BeaconNode,
SSZ.saveFile(
node.config.dumpDir / "block-" & $newBlock.message.slot & "-" &
shortLog(newBlockRef.root) & ".ssz", newBlock)
SSZ.saveFile(
node.config.dumpDir / "state-" & $tmpState.data.slot & "-" &
shortLog(newBlockRef.root) & "-" & shortLog(tmpState.root) & ".ssz",
tmpState.data)
node.blockPool.withState(
node.blockPool.tmpState, newBlockRef.atSlot(newBlockRef.slot)):
SSZ.saveFile(
node.config.dumpDir / "state-" & $state.slot & "-" &
shortLog(newBlockRef.root) & "-" & shortLog(root()) & ".ssz",
state())
node.network.broadcast(topicBeaconBlocks, newBlock)

View File

@ -16,16 +16,8 @@
# improved coverage, and to avoid unnecessary validation when replaying trusted
# (previously validated) blocks.
type
UpdateFlag* = enum
nextSlot ##\
## Perform the operation as if the next slot was being processed - this is
## useful when using the state to verify data that will go in the next slot,
## for example when proposing
## TODO need to be careful here, easy to assume that slot number change is
## enough, vs advancing the state - however, making a full state copy
## is expensive also :/
skipMerkleValidation ##\
## When processing deposits, skip verifying the Merkle proof trees of each
## deposit. This is a holdover from both interop issues with the malformed

View File

@ -444,11 +444,9 @@ proc check_attestation*(
## at the current slot. When acting as a proposer, the same rules need to
## be followed!
let stateSlot =
if nextSlot in flags: state.slot + 1
else: state.slot
let data = attestation.data
let
stateSlot = state.slot
data = attestation.data
trace "process_attestation: beginning",
attestation=attestation

View File

@ -592,6 +592,7 @@ func shortLog*(e: Epoch): uint64 =
func shortLog*(v: BeaconBlock): auto =
(
slot: shortLog(v.slot),
proposer_index: v.proposer_index,
parent_root: shortLog(v.parent_root),
state_root: shortLog(v.state_root),
proposer_slashings_len: v.body.proposer_slashings.len(),

View File

@ -436,3 +436,87 @@ proc process_block*(
return false
true
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
# TODO There's more to do here - the spec has helpers that deal set up some of
# the fields in here!
proc makeBeaconBlock*(
state: BeaconState,
parent_root: Eth2Digest,
randao_reveal: ValidatorSig,
eth1_data: Eth1Data,
graffiti: Eth2Digest,
attestations: seq[Attestation],
deposits: seq[Deposit]): 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()
let proposer_index = get_beacon_proposer_index(state, cache)
doAssert proposer_index.isSome, "Unable to get proposer index when proposing!"
# To create a block, we'll first apply a partial block to the state, skipping
# some validations.
var blck = BeaconBlock(
slot: state.slot,
proposer_index: proposer_index.get().uint64,
parent_root: parent_root,
body: BeaconBlockBody(
randao_reveal: randao_reveal,
eth1_data: eth1data,
graffiti: graffiti,
attestations: attestations,
deposits: deposits)
)
var tmpState = state
let ok = process_block(tmpState, blck, {skipBlsValidation}, cache)
if not ok:
warn "Unable to apply new block to state", blck = shortLog(blck)
return
blck.state_root = hash_tree_root(tmpState)
some(blck)
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
func get_slot_signature*(
fork: Fork, slot: Slot, privkey: ValidatorPrivKey): ValidatorSig =
let
domain =
get_domain(fork, DOMAIN_SELECTION_PROOF, compute_epoch_at_slot(slot))
signing_root = compute_signing_root(slot, domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
func get_epoch_signature*(
fork: Fork, slot: Slot, privkey: ValidatorPrivKey): ValidatorSig =
let
domain =
get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
signing_root = compute_signing_root(compute_epoch_at_slot(slot), domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
func get_block_signature*(
fork: Fork, slot: Slot, root: Eth2Digest, privkey: ValidatorPrivKey): ValidatorSig =
let
domain =
get_domain(fork, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(slot))
signing_root = compute_signing_root(root, domain)
blsSign(privKey, signing_root.data)
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
func get_attestation_signature*(
fork: Fork, attestation: AttestationData, privkey: ValidatorPrivKey): ValidatorSig =
let
attestationRoot = hash_tree_root(attestation)
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER, attestation.target.epoch)
signing_root = compute_signing_root(attestationRoot, domain)
blsSign(privKey, signing_root.data)

View File

@ -1,7 +1,7 @@
import
tables,
chronos, chronicles,
spec/[datatypes, crypto, digest, helpers], ssz,
spec/[datatypes, crypto, digest, state_transition_block], ssz,
beacon_node_types
func init*(T: type ValidatorPool): T =
@ -29,18 +29,12 @@ proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
if v.kind == inProcess:
# TODO state might become invalid after any async calls - it's fragile to
# care about this in here
let
domain =
get_domain(fork, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(slot))
# TODO this is an ugly hack to fake a delay and subsequent async reordering
# for the purpose of testing the external validator delay - to be
# replaced by something more sensible
await sleepAsync(chronos.milliseconds(1))
let signing_root = compute_signing_root(blockRoot, domain)
result = blsSign(v.privKey, signing_root.data)
result = get_block_signature(fork, slot, blockRoot, v.privKey)
else:
error "Unimplemented"
quit 1
@ -49,17 +43,12 @@ proc signAttestation*(v: AttachedValidator,
attestation: AttestationData,
fork: Fork): Future[ValidatorSig] {.async.} =
if v.kind == inProcess:
let
attestationRoot = hash_tree_root(attestation)
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER, attestation.target.epoch)
# TODO this is an ugly hack to fake a delay and subsequent async reordering
# for the purpose of testing the external validator delay - to be
# replaced by something more sensible
await sleepAsync(chronos.milliseconds(1))
let signing_root = compute_signing_root(attestationRoot, domain)
result = blsSign(v.privKey, signing_root.data)
result = get_attestation_signature(fork, attestation, v.privKey)
else:
error "Unimplemented"
quit 1
@ -67,11 +56,7 @@ proc signAttestation*(v: AttachedValidator,
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#randao-reveal
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork, slot: Slot):
ValidatorSig =
let
domain = get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
signing_root = compute_signing_root(compute_epoch_at_slot(slot).uint64, domain)
bls_sign(k, signing_root.data)
get_epoch_signature(fork, slot, k)
func genRandaoReveal*(v: AttachedValidator, fork: Fork, slot: Slot):
ValidatorSig =

View File

@ -121,8 +121,7 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
let
attestations_idx = state.slot
body = BeaconBlockBody(
attestations: attestations.getOrDefault(attestations_idx))
blockAttestations = attestations.getOrDefault(attestations_idx)
attestations.del attestations_idx
doAssert len(attestations) <=
@ -134,7 +133,8 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
else: tBlock
withTimer(timers[t]):
signedBlock = addBlock(state, latest_block_root, body, flags)
signedBlock = addTestBlock(
state, latest_block_root, attestations = blockAttestations, flags = flags)
latest_block_root = withTimerRet(timers[tHashBlock]):
hash_tree_root(signedBlock.message)

View File

@ -12,7 +12,8 @@ import
# Standard library
sets,
# Specs
../../beacon_chain/spec/[datatypes, beaconstate, helpers, validator, crypto],
../../beacon_chain/spec/[datatypes, beaconstate, helpers, validator, crypto,
state_transition_block],
# Internals
../../beacon_chain/[ssz, extras, state_transition],
# Mocking procs
@ -53,20 +54,6 @@ proc mockAttestationData(
epoch: target_epoch, root: epoch_boundary_root
)
proc get_attestation_signature(
state: BeaconState,
attestation_data: AttestationData,
privkey: ValidatorPrivKey
): ValidatorSig =
let domain = get_domain(
state = state,
domain_type = DOMAIN_BEACON_ATTESTER,
message_epoch = attestation_data.target.epoch
)
let signing_root = compute_signing_root(attestation_data, domain)
return blsSign(privkey, signing_root.data)
proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
var cache = get_empty_per_epoch_cache()
let participants = get_attesting_indices(
@ -79,7 +66,7 @@ proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
var first_iter = true # Can't do while loop on hashset
for validator_index in participants:
let sig = get_attestation_signature(
state, attestation.data, MockPrivKeys[validator_index]
state.fork, attestation.data, MockPrivKeys[validator_index]
)
if first_iter:
attestation.signature = sig

View File

@ -8,7 +8,7 @@
import
options,
# Specs
../../beacon_chain/spec/[datatypes, crypto, helpers, validator],
../../beacon_chain/spec/[datatypes, crypto, validator, state_transition_block],
# Internals
../../beacon_chain/[ssz, extras, state_transition],
# Mock helpers
@ -27,27 +27,10 @@ proc signMockBlockImpl(
let privkey = MockPrivKeys[proposer_index]
block:
let domain = get_domain(
state,
DOMAIN_RANDAO,
message_epoch = block_slot.compute_epoch_at_slot(),
)
let signing_root = compute_signing_root(
block_slot.compute_epoch_at_slot(),
domain
)
signedBlock.message.body.randao_reveal = bls_sign(privkey, signing_root.data)
block:
let domain = get_domain(
state,
DOMAIN_BEACON_PROPOSER,
message_epoch = block_slot.compute_epoch_at_slot(),
)
let signing_root = compute_signing_root(signedBlock.message, domain)
signedBlock.signature = bls_sign(privkey, signing_root.data)
signedBlock.message.body.randao_reveal = get_epoch_signature(
state.fork, block_slot, privkey)
signedBlock.signature = get_block_signature(
state.fork, block_slot, hash_tree_root(signedBlock.message), privkey)
proc signMockBlock*(
state: BeaconState,

View File

@ -35,17 +35,16 @@ when const_preset == "minimal": # Too much stack space used on mainnet
var cache = get_empty_per_epoch_cache()
let
# Create an attestation for slot 1!
beacon_committee = get_beacon_committee(state.data.data,
state.data.data.slot, 0, cache)
beacon_committee = get_beacon_committee(
state.data.data, state.data.data.slot, 0, cache)
attestation = makeAttestation(
state.data.data, state.blck.root, beacon_committee[0], cache)
pool.add(attestation)
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
let attestations = pool.getAttestationsForBlock(
state.data.data, state.data.data.slot + 1)
let attestations = pool.getAttestationsForBlock(state.data.data)
check:
attestations.len == 1
@ -54,8 +53,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
var cache = get_empty_per_epoch_cache()
let
# Create an attestation for slot 1!
bc0 = get_beacon_committee(state.data.data,
state.data.data.slot, 0, cache)
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
@ -71,10 +70,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
pool.add(attestation1)
pool.add(attestation0)
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
let attestations = pool.getAttestationsForBlock(
state.data.data, state.data.data.slot + 1)
let attestations = pool.getAttestationsForBlock(state.data.data)
check:
attestations.len == 1
@ -83,8 +81,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
var cache = get_empty_per_epoch_cache()
let
# Create an attestation for slot 1!
bc0 = get_beacon_committee(state.data.data,
state.data.data.slot, 0, cache)
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
attestation1 = makeAttestation(
@ -93,10 +91,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
pool.add(attestation0)
pool.add(attestation1)
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
let attestations = pool.getAttestationsForBlock(
state.data.data, state.data.data.slot + 1)
let attestations = pool.getAttestationsForBlock(state.data.data)
check:
attestations.len == 1
@ -106,8 +103,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
var
# Create an attestation for slot 1!
bc0 = get_beacon_committee(state.data.data,
state.data.data.slot, 0, cache)
bc0 = get_beacon_committee(
state.data.data, state.data.data.slot, 0, cache)
attestation0 = makeAttestation(
state.data.data, state.blck.root, bc0[0], cache)
attestation1 = makeAttestation(
@ -118,10 +115,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
pool.add(attestation0)
pool.add(attestation1)
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
let attestations = pool.getAttestationsForBlock(
state.data.data, state.data.data.slot + 1)
let attestations = pool.getAttestationsForBlock(state.data.data)
check:
attestations.len == 1
@ -142,17 +138,16 @@ when const_preset == "minimal": # Too much stack space used on mainnet
pool.add(attestation1)
pool.add(attestation0)
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
let attestations = pool.getAttestationsForBlock(
state.data.data, state.data.data.slot + 1)
let attestations = pool.getAttestationsForBlock(state.data.data)
check:
attestations.len == 1
timedTest "Fork choice returns latest block with no attestations":
let
b1 = addBlock(state.data.data, blockPool.tail.root, BeaconBlockBody())
b1 = addTestBlock(state.data.data, blockPool.tail.root)
b1Root = hash_tree_root(b1.message)
b1Add = blockPool.add(b1Root, b1)
head = pool.selectHead()
@ -161,7 +156,7 @@ when const_preset == "minimal": # Too much stack space used on mainnet
head == b1Add
let
b2 = addBlock(state.data.data, b1Root, BeaconBlockBody())
b2 = addTestBlock(state.data.data, b1Root)
b2Root = hash_tree_root(b2.message)
b2Add = blockPool.add(b2Root, b2)
head2 = pool.selectHead()
@ -172,7 +167,7 @@ when const_preset == "minimal": # Too much stack space used on mainnet
timedTest "Fork choice returns block with attestation":
var cache = get_empty_per_epoch_cache()
let
b10 = makeBlock(state.data.data, blockPool.tail.root, BeaconBlockBody())
b10 = makeTestBlock(state.data.data, blockPool.tail.root)
b10Root = hash_tree_root(b10.message)
b10Add = blockPool.add(b10Root, b10)
head = pool.selectHead()
@ -181,16 +176,15 @@ when const_preset == "minimal": # Too much stack space used on mainnet
head == b10Add
let
b11 = makeBlock(state.data.data, blockPool.tail.root, BeaconBlockBody(
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])
))
b11 = makeTestBlock(state.data.data, blockPool.tail.root,
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)
b11Add = blockPool.add(b11Root, b11)
bc1 = get_beacon_committee(state.data.data,
state.data.data.slot, 1, cache)
attestation0 = makeAttestation(
state.data.data, b10Root, bc1[0], cache)
bc1 = get_beacon_committee(
state.data.data, state.data.data.slot, 1, cache)
attestation0 = makeAttestation(state.data.data, b10Root, bc1[0], cache)
pool.add(attestation0)
@ -201,10 +195,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
head2 == b10Add
let
attestation1 = makeAttestation(
state.data.data, b11Root, bc1[1], cache)
attestation2 = makeAttestation(
state.data.data, b11Root, bc1[2], cache)
attestation1 = makeAttestation(state.data.data, b11Root, bc1[1], cache)
attestation2 = makeAttestation(state.data.data, b11Root, bc1[2], cache)
pool.add(attestation1)
let head3 = pool.selectHead()

View File

@ -89,9 +89,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
db = makeTestDB(SLOTS_PER_EPOCH)
pool = BlockPool.init(db)
state = pool.loadTailState().data.data
b1 = addBlock(state, pool.tail.root, BeaconBlockBody())
b1 = addTestBlock(state, pool.tail.root)
b1Root = hash_tree_root(b1.message)
b2 = addBlock(state, b1Root, BeaconBlockBody())
b2 = addTestBlock(state, b1Root)
b2Root {.used.} = hash_tree_root(b2.message)
timedTest "getRef returns nil for missing blocks":
@ -246,8 +246,7 @@ when const_preset == "minimal": # Too much stack space used on mainnet
block:
# Create a fork that will not be taken
var
blck = makeBlock(pool.headState.data.data, pool.head.blck.root,
BeaconBlockBody())
blck = makeTestBlock(pool.headState.data.data, pool.head.blck.root)
discard pool.add(hash_tree_root(blck.message), blck)
for i in 0 ..< (SLOTS_PER_EPOCH * 6):
@ -258,11 +257,11 @@ when const_preset == "minimal": # Too much stack space used on mainnet
pool.heads.len == 2
var
cache = get_empty_per_epoch_cache()
blck = makeBlock(pool.headState.data.data, pool.head.blck.root,
BeaconBlockBody(
attestations: makeFullAttestations(
pool.headState.data.data, pool.head.blck.root,
pool.headState.data.data.slot, cache, {})))
blck = makeTestBlock(
pool.headState.data.data, pool.head.blck.root,
attestations = makeFullAttestations(
pool.headState.data.data, pool.head.blck.root,
pool.headState.data.data.slot, cache, {}))
let added = pool.add(hash_tree_root(blck.message), blck)
pool.updateHead(added)

View File

@ -38,7 +38,7 @@ suiteReport "Block processing" & preset():
var
state = genesisState
previous_block_root = hash_tree_root(genesisBlock.message)
new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
new_block = makeTestBlock(state, previous_block_root)
let block_ok = state_transition(state, new_block, {})
@ -62,7 +62,7 @@ suiteReport "Block processing" & preset():
previous_block_root = genesisRoot
for i in 1..SLOTS_PER_EPOCH.int:
var new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
let new_block = makeTestBlock(state, previous_block_root)
let block_ok = state_transition(state, new_block, {})
@ -95,9 +95,9 @@ suiteReport "Block processing" & preset():
process_slots(state, GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1)
let
new_block = makeBlock(state, previous_block_root, BeaconBlockBody(
attestations: @[attestation]
))
new_block = makeTestBlock(state, previous_block_root,
attestations = @[attestation]
)
discard state_transition(state, new_block, {})
check:

View File

@ -11,7 +11,7 @@ import
../beacon_chain/[beacon_chain_db, block_pool, extras, ssz, state_transition,
validator_pool],
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
helpers, validator]
helpers, validator, state_transition_block]
func makeFakeValidatorPrivKey(i: int): ValidatorPrivKey =
# 0 is not a valid BLS private key - 1000 helps interop with rust BLS library,
@ -62,81 +62,70 @@ func makeInitialDeposits*(
for i in 0..<n.int:
result.add makeDeposit(i, flags)
proc addBlock*(
state: var BeaconState, previous_block_root: Eth2Digest,
body: BeaconBlockBody, flags: UpdateFlags = {}): SignedBeaconBlock =
proc addTestBlock*(
state: var BeaconState,
parent_root: Eth2Digest,
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!
# This is the equivalent of running
# updateState(state, prev_block, makeBlock(...), {skipBlsValidation})
# but avoids some slow block copies
# FIXME update comment - updateState no longer exists, could be `state_transition`
# but different number of parameters
state.slot += 1
process_slots(state, state.slot + 1)
var cache = get_empty_per_epoch_cache()
let proposer_index = get_beacon_proposer_index(state, cache)
state.slot -= 1
let
# Index from the new state, but registry from the old state.. hmm...
# In tests, let this throw
proposer = state.validators[proposer_index.get]
privKey = hackPrivKey(proposer)
randao_reveal =
if skipBlsValidation notin flags:
privKey.genRandaoReveal(state.fork, state.slot)
else:
ValidatorSig()
# TODO ugly hack; API needs rethinking
var new_body = body
if skipBlsValidation notin flags:
new_body.randao_reveal = privKey.genRandaoReveal(state.fork, state.slot + 1)
let
message = makeBeaconBlock(
state,
parent_root,
randao_reveal,
eth1_data,
graffiti,
attestations,
deposits)
new_body.eth1_data = Eth1Data()
doAssert message.isSome(), "Should have created a valid block!"
var
# 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
# would look with the new block applied.
new_block = SignedBeaconBlock(
message: BeaconBlock(
slot: state.slot + 1,
proposer_index: proposer_index.get.uint64,
parent_root: previous_block_root,
state_root: Eth2Digest(), # we need the new state first
body: new_body
)
message: message.get()
)
let block_ok = state_transition(state, new_block, {skipBlsValidation, skipStateRootValidation})
doAssert block_ok
# 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
new_block.message.state_root = hash_tree_root(state)
doAssert privKey.pubKey() == proposer.pubkey,
"signature key should be derived from private key! - wrong privkey?"
if skipBLSValidation notin flags:
let domain = get_domain(state, DOMAIN_BEACON_PROPOSER,
compute_epoch_at_slot(new_block.message.slot))
let signing_root = compute_signing_root(new_block.message, domain)
# We have a signature - put it in the block and we should be done!
new_block.signature = bls_sign(privKey, signing_root.data)
doAssert bls_verify(
proposer.pubkey,
signing_root.data, new_block.signature),
"we just signed this message - it should pass verification!"
let ok = process_block(state, new_block.message, flags, cache)
doAssert ok, "adding block after producing it should work"
new_block
proc makeBlock*(
state: BeaconState, previous_block_root: Eth2Digest,
body: BeaconBlockBody): SignedBeaconBlock =
proc makeTestBlock*(
state: BeaconState,
parent_root: Eth2Digest,
eth1_data = Eth1Data(),
attestations = newSeq[Attestation](),
deposits = newSeq[Deposit](),
graffiti = Eth2Digest(),
flags: set[UpdateFlag] = {}): SignedBeaconBlock =
# 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
# calculate what the state will look like after that block has been applied,
# because the block includes the state root.
var next_state = state
addBlock(next_state, previous_block_root, body)
var tmpState = state
addTestBlock(
tmpState, parent_root, eth1_data, attestations, deposits, graffiti, flags)
proc makeAttestation*(
state: BeaconState, beacon_block_root: Eth2Digest,
@ -158,11 +147,9 @@ proc makeAttestation*(
aggregation_bits.setBit sac_index
let
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
signing_root = compute_signing_root(data, domain)
sig =
if skipBLSValidation notin flags:
bls_sign(hackPrivKey(validator), signing_root.data)
get_attestation_signature(state.fork, data, hackPrivKey(validator))
else:
ValidatorSig()
@ -209,27 +196,24 @@ proc makeFullAttestations*(
let
committee = get_beacon_committee(state, slot, index, cache)
data = makeAttestationData(state, slot, index, beacon_block_root)
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
signing_root = compute_signing_root(data, domain)
doAssert committee.len() >= 1
# Initial attestation
var attestation = Attestation(
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
data: data,
signature: bls_sign(
hackPrivKey(state.validators[committee[0]]),
signing_root.data
)
signature: get_attestation_signature(
state.fork, data,
hackPrivKey(state.validators[committee[0]]))
)
# Aggregate the remainder
attestation.aggregation_bits.setBit 0
for j in 1 ..< committee.len():
attestation.aggregation_bits.setBit j
if skipBLSValidation notin flags:
attestation.signature.aggregate(bls_sign(
hackPrivKey(state.validators[committee[j]]),
signing_root.data
attestation.signature.aggregate(get_attestation_signature(
state.fork, data,
hackPrivKey(state.validators[committee[j]])
))
result.add attestation