Mostly remove skipMerkleValidation by fixing Merkle proof construction/usage (#879)

* refactor and fix merkle proof construction in test suite and thereby remove most remaining skipMerkleValidation flags, now unnecessary

* a few non-semantic comment update/removals
This commit is contained in:
tersec 2020-04-10 13:59:17 +00:00 committed by GitHub
parent cbc998ed93
commit ccbbce79a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 55 additions and 56 deletions

View File

@ -5,13 +5,6 @@
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
# The other part is arguably part of attestation pool -- the validation's
# something that should be happing on receipt, not aggregation per se. In
# that part, check that messages conform -- so, check for each type
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#topics-and-messages
# specifies. So by the time this calls attestation pool, all validation's
# already done.
import import
options, options,
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator, ./spec/[beaconstate, datatypes, crypto, digest, helpers, validator,

View File

@ -80,7 +80,7 @@ const
# Time parameters # Time parameters
# --------------------------------------------------------------- # ---------------------------------------------------------------
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L71 # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L77
MIN_GENESIS_DELAY* = 86400 # 86400 seconds (1 day) MIN_GENESIS_DELAY* = 86400 # 86400 seconds (1 day)
SECONDS_PER_SLOT*{.intdefine.} = 12'u64 # Compile with -d:SECONDS_PER_SLOT=1 for 12x faster slots SECONDS_PER_SLOT*{.intdefine.} = 12'u64 # Compile with -d:SECONDS_PER_SLOT=1 for 12x faster slots

View File

@ -62,7 +62,7 @@ const
# Initial values # Initial values
# --------------------------------------------------------------- # ---------------------------------------------------------------
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L64 # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L70
# Unchanged # Unchanged
GENESIS_SLOT* = 0.Slot GENESIS_SLOT* = 0.Slot

View File

@ -31,7 +31,7 @@
# now. # now.
import import
collections/sets, chronicles, sets, options, collections/sets, chronicles, sets,
./extras, ./ssz, metrics, ./extras, ./ssz, metrics,
./spec/[datatypes, crypto, digest, helpers, validator], ./spec/[datatypes, crypto, digest, helpers, validator],
./spec/[state_transition_block, state_transition_epoch], ./spec/[state_transition_block, state_transition_epoch],

View File

@ -79,8 +79,7 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
let let
genesisState = genesisState =
initialize_beacon_state_from_eth1( initialize_beacon_state_from_eth1(Eth2Digest(), 0, deposits, flags)
Eth2Digest(), 0, deposits, flags + {skipMerkleValidation})
genesisBlock = get_initial_beacon_block(genesisState) genesisBlock = get_initial_beacon_block(genesisState)
echo "Starting simulation..." echo "Starting simulation..."

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2018-2019 Status Research & Development GmbH # Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -12,11 +12,13 @@ import
# Standard library # Standard library
math, random, math, random,
# Specs # Specs
../../beacon_chain/spec/[datatypes, crypto, helpers, digest, beaconstate], ../../beacon_chain/spec/[datatypes, crypto, helpers, digest],
# Internals # Internals
../../beacon_chain/[ssz, extras], ../../beacon_chain/[ssz, extras],
# Mocking procs # Mocking procs
./merkle_minimal, ./mock_validator_keys ./mock_validator_keys,
# Other test utilities, for attachMerkleProofs()
../testblockutil
func signMockDepositData( func signMockDepositData(
deposit_data: var DepositData, deposit_data: var DepositData,
@ -143,26 +145,6 @@ template mockGenesisDepositsImpl(
depositsData.add result[valIdx].data depositsData.add result[valIdx].data
depositsDataHash.add hash_tree_root(result[valIdx].data) depositsDataHash.add hash_tree_root(result[valIdx].data)
# 2nd & 3rd loops - build hashes and proofs
let root = hash_tree_root(depositsData)
let tree = merkleTreeFromLeaves(depositsDataHash)
# 4th loop - append proof
for valIdx in 0 ..< validatorCount.int:
# TODO ensure genesis & deposit process tests suffice to catch whether
# changes here break things; ensure that this matches the merkle proof
# sequence is_valid_merkle_branch(...) now looks for
result[valIdx].proof[0..31] = tree.getMerkleProof(valIdx)
result[valIdx].proof[32] =
Eth2Digest(data: int_to_bytes32((valIdx + 1).uint64))
doAssert is_valid_merkle_branch(
depositsDataHash[valIdx],
result[valIdx].proof,
DEPOSIT_CONTRACT_TREE_DEPTH,
valIdx.uint64,
root
)
proc mockGenesisBalancedDeposits*( proc mockGenesisBalancedDeposits*(
validatorCount: uint64, validatorCount: uint64,
amountInEth: Positive, amountInEth: Positive,
@ -179,6 +161,7 @@ proc mockGenesisBalancedDeposits*(
let amount = amountInEth.uint64 * 10'u64^9 let amount = amountInEth.uint64 * 10'u64^9
mockGenesisDepositsImpl(result, validatorCount,amount,flags): mockGenesisDepositsImpl(result, validatorCount,amount,flags):
discard discard
attachMerkleProofs(result)
proc mockGenesisUnBalancedDeposits*( proc mockGenesisUnBalancedDeposits*(
validatorCount: uint64, validatorCount: uint64,
@ -199,6 +182,7 @@ proc mockGenesisUnBalancedDeposits*(
mockGenesisDepositsImpl(result, validatorCount, amount, flags): mockGenesisDepositsImpl(result, validatorCount, amount, flags):
amount = rng.rand(amountRangeInEth).uint64 * 10'u64^9 amount = rng.rand(amountRangeInEth).uint64 * 10'u64^9
attachMerkleProofs(result)
proc mockUpdateStateForNewDeposit*( proc mockUpdateStateForNewDeposit*(
state: var BeaconState, state: var BeaconState,
@ -220,14 +204,13 @@ proc mockUpdateStateForNewDeposit*(
flags flags
) )
when false: # TODO var result_seq = @[result]
let tree = merkleTreeFromLeaves([hash_tree_root(result.data)]) attachMerkleProofs(result_seq)
result[valIdx].proof[0..31] = tree.getMerkleProof(0) result.proof = result_seq[0].proof
result[valIdx].proof[32] = int_to_bytes32(0 + 1)
# doAssert is_valid_merkle_branch(...)
# TODO: this logic from the eth2.0-specs test suite seems strange # TODO: this logic from the eth2.0-specs test suite seems strange
# but confirmed by running it # but confirmed by running it
state.eth1_deposit_index = 0 state.eth1_deposit_index = 0
state.eth1_data.deposit_root = hash_tree_root(result.data) state.eth1_data.deposit_root =
hash_tree_root(sszList(@[result.data], 2'i64^DEPOSIT_CONTRACT_TREE_DEPTH))
state.eth1_data.deposit_count = 1 state.eth1_data.deposit_count = 1

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2018-2019 Status Research & Development GmbH # Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -25,7 +25,7 @@ proc initGenesisState*(num_validators: uint64, genesis_time: uint64 = 0): Beacon
) )
initialize_beacon_state_from_eth1( initialize_beacon_state_from_eth1(
eth1BlockHash, 0, deposits, {skipBlsValidation, skipMerkleValidation}) eth1BlockHash, 0, deposits, {skipBlsValidation})
when isMainModule: when isMainModule:
# Smoke test # Smoke test

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH # Copyright (c) 2018-2020 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -56,8 +56,7 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
# State transition # State transition
# ---------------------------------------- # ----------------------------------------
check: state.process_deposit(deposit, check: state.process_deposit(deposit, {skipBlsValidation})
{skipBlsValidation, skipMerkleValidation})
# Check invariants # Check invariants
# ---------------------------------------- # ----------------------------------------
@ -101,8 +100,7 @@ suiteReport "[Unit - Spec - Block processing] Deposits " & preset():
# State transition # State transition
# ---------------------------------------- # ----------------------------------------
check: state.process_deposit(deposit, check: state.process_deposit(deposit, {skipBlsValidation})
{skipBlsValidation, skipMerkleValidation})
# Check invariants # Check invariants
# ---------------------------------------- # ----------------------------------------

View File

@ -99,7 +99,7 @@ suiteReport "Beacon chain DB" & preset():
let let
state = initialize_beacon_state_from_eth1( state = initialize_beacon_state_from_eth1(
eth1BlockHash, 0, makeInitialDeposits(SLOTS_PER_EPOCH), {skipBlsValidation, skipMerkleValidation}) eth1BlockHash, 0, makeInitialDeposits(SLOTS_PER_EPOCH), {skipBlsValidation})
root = hash_tree_root(state) root = hash_tree_root(state)
db.putState(state) db.putState(state)

View File

@ -16,6 +16,5 @@ import
suiteReport "Beacon state" & preset(): suiteReport "Beacon state" & preset():
timedTest "Smoke test initialize_beacon_state_from_eth1" & preset(): timedTest "Smoke test initialize_beacon_state_from_eth1" & preset():
let state = initialize_beacon_state_from_eth1( let state = initialize_beacon_state_from_eth1(
Eth2Digest(), 0, Eth2Digest(), 0, makeInitialDeposits(SLOTS_PER_EPOCH, {}), {})
makeInitialDeposits(SLOTS_PER_EPOCH, {}), {skipMerkleValidation})
check: state.validators.len == SLOTS_PER_EPOCH check: state.validators.len == SLOTS_PER_EPOCH

View File

@ -21,8 +21,7 @@ suiteReport "Block processing" & preset():
# Genesis state with minimal number of deposits # Genesis state with minimal number of deposits
# TODO bls verification is a bit of a bottleneck here # TODO bls verification is a bit of a bottleneck here
genesisState = initialize_beacon_state_from_eth1( genesisState = initialize_beacon_state_from_eth1(
Eth2Digest(), 0, Eth2Digest(), 0, makeInitialDeposits(), {})
makeInitialDeposits(), {skipMerkleValidation})
genesisBlock = get_initial_beacon_block(genesisState) genesisBlock = get_initial_beacon_block(genesisState)
genesisRoot = hash_tree_root(genesisBlock.message) genesisRoot = hash_tree_root(genesisBlock.message)

View File

@ -6,8 +6,9 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
options, stew/endians2, options, sequtils, stew/endians2,
chronicles, eth/trie/[db], chronicles, eth/trie/[db],
./mocking/merkle_minimal,
../beacon_chain/[beacon_chain_db, block_pool, extras, ssz, state_transition, ../beacon_chain/[beacon_chain_db, block_pool, extras, ssz, state_transition,
validator_pool], validator_pool],
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest, ../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
@ -57,11 +58,38 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
let signing_root = compute_signing_root(result.getDepositMessage, domain) let signing_root = compute_signing_root(result.getDepositMessage, domain)
result.data.signature = bls_sign(privkey, signing_root.data) result.data.signature = bls_sign(privkey, signing_root.data)
func makeInitialDeposits*( proc attachMerkleProofs*(deposits: var seq[Deposit]) =
let deposit_data_roots = mapIt(deposits, it.data.hash_tree_root)
var
deposit_data_sums: seq[Eth2Digest]
for prefix_root in hash_tree_roots_prefix(
deposit_data_roots, 1'i64 shl DEPOSIT_CONTRACT_TREE_DEPTH):
deposit_data_sums.add prefix_root
for val_idx in 0 ..< deposits.len:
let merkle_tree = merkleTreeFromLeaves(deposit_data_roots[0..val_idx])
deposits[val_idx].proof[0..31] = merkle_tree.getMerkleProof(val_idx)
deposits[val_idx].proof[32].data[0..7] = int_to_bytes8((val_idx + 1).uint64)
doAssert is_valid_merkle_branch(
deposit_data_roots[val_idx], deposits[val_idx].proof,
DEPOSIT_CONTRACT_TREE_DEPTH + 1, val_idx.uint64,
deposit_data_sums[val_idx])
proc makeInitialDeposits*(
n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] = n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] =
for i in 0..<n.int: for i in 0..<n.int:
result.add makeDeposit(i, flags) result.add makeDeposit(i, flags)
# This needs to be done as a batch, since the Merkle proof of the i'th
# deposit depends on the deposit (data) of the 0th through (i-1)st, of
# deposits. Computing partial hash_tree_root sequences of DepositData,
# and ideally (but not yet) efficiently only once calculating a Merkle
# tree utilizing as much of the shared substructure as feasible, means
# attaching proofs all together, as a separate step.
if skipMerkleValidation notin flags:
attachMerkleProofs(result)
func signBlock*( func signBlock*(
fork: Fork, genesis_validators_root: Eth2Digest, blck: BeaconBlock, fork: Fork, genesis_validators_root: Eth2Digest, blck: BeaconBlock,
privKey: ValidatorPrivKey, flags: UpdateFlags = {}): SignedBeaconBlock = privKey: ValidatorPrivKey, flags: UpdateFlags = {}): SignedBeaconBlock =

View File

@ -101,7 +101,7 @@ proc makeTestDB*(validators: int): BeaconChainDB =
genState = initialize_beacon_state_from_eth1( genState = initialize_beacon_state_from_eth1(
Eth2Digest(), 0, Eth2Digest(), 0,
makeInitialDeposits(validators, flags = {skipBlsValidation}), makeInitialDeposits(validators, flags = {skipBlsValidation}),
{skipBlsValidation, skipMerkleValidation}) {skipBlsValidation})
genBlock = get_initial_beacon_block(genState) genBlock = get_initial_beacon_block(genState)
makeTestDB(genState, genBlock) makeTestDB(genState, genBlock)