From 4dc526fbb7d619d0675950b703dcb30c5eb22836 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 29 Jun 2019 02:16:16 +0800 Subject: [PATCH] In the end, `get_merkle_root` is back --- scripts/build_spec.py | 6 ++- specs/core/0_beacon-chain.md | 44 ++++++++++++++++--- .../test/genesis/test_genesis_trigger.py | 9 ++-- .../pyspec/eth2spec/test/helpers/deposits.py | 17 +++---- .../pyspec/eth2spec/utils/merkle_minimal.py | 2 +- .../eth2spec/utils/test_merkle_minimal.py | 5 ++- 6 files changed, 59 insertions(+), 24 deletions(-) diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 99c5cd69d..1bdebd130 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -11,7 +11,8 @@ from typing import ( ) -PHASE0_IMPORTS = '''from typing import ( +PHASE0_IMPORTS = '''from math import log2 +from typing import ( Any, Callable, Dict, Set, Sequence, Tuple, ) @@ -36,7 +37,8 @@ from eth2spec.utils.bls import ( from eth2spec.utils.hash_function import hash ''' -PHASE1_IMPORTS = '''from typing import ( +PHASE1_IMPORTS = '''from math import log2 +from typing import ( Any, Callable, Dict, Optional, Set, Sequence, MutableSequence, Tuple, ) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ea99bb473..25a7340d3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -51,6 +51,8 @@ - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - [`signing_root`](#signing_root) + - [`calc_merkle_tree_from_leaves`](#calc_merkle_tree_from_leaves) + - [`get_merkle_root`](#get_merkle_root) - [`bls_domain`](#bls_domain) - [`slot_to_epoch`](#slot_to_epoch) - [`get_previous_epoch`](#get_previous_epoch) @@ -558,6 +560,33 @@ The `hash` function is SHA256. `def signing_root(object: Container) -> Hash` is a function for computing signing messages, as defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers). +### `calc_merkle_tree_from_leaves` + +```python +zerohashes = [ZERO_HASH] +for layer in range(1, 100): + zerohashes.append(hash(zerohashes[layer - 1] + zerohashes[layer - 1])) +def calc_merkle_tree_from_leaves(values: Sequence[Hash], layer_count: int=32) -> Sequence[Sequence[Hash]]: + values = list(values) + tree = [values[::]] + for h in range(layer_count): + if len(values) % 2 == 1: + values.append(zerohashes[h]) + values = [hash(values[i] + values[i + 1]) for i in range(0, len(values), 2)] + tree.append(values[::]) + return tree +``` + +### `get_merkle_root` + +```python +def get_merkle_root(values: Sequence[Hash], pad_to: int=1) -> Hash: + layer_count = int(log2(pad_to)) + if len(values) == 0: + return zerohashes[layer_count] + return calc_merkle_tree_from_leaves(values, layer_count)[-1][0] +``` + ### `bls_domain` ```python @@ -1144,10 +1173,10 @@ def is_genesis_trigger(deposits: List[Deposit, 2**DEPOSIT_CONTRACT_TREE_DEPTH], # Process deposits state = BeaconState() - for i, deposit in enumerate(deposits): - state.eth1_data.deposit_root = hash_tree_root( - Vector[DepositData, len(deposits)](list(map(lambda deposit: deposit.data, deposits[:i]))) - ) + for deposit_index, deposit in enumerate(deposits): + leaves = [hash_tree_root(d.data) for d in deposits[:deposit_index + 1]] + state.eth1_data.deposit_root = get_merkle_root(leaves, 2**DEPOSIT_CONTRACT_TREE_DEPTH) + state.eth1_deposit_index = deposit_index process_deposit(state, deposit) # Count active validators at genesis @@ -1173,9 +1202,14 @@ def get_genesis_beacon_state(deposits: Sequence[Deposit], genesis_time: int, eth ) # Process genesis deposits - for deposit in deposits: + for deposit_index, deposit in enumerate(deposits): + leaves = [hash_tree_root(d.data) for d in deposits[:deposit_index + 1]] + state.eth1_data.deposit_root = get_merkle_root(leaves, 2**DEPOSIT_CONTRACT_TREE_DEPTH) + state.eth1_deposit_index = deposit_index process_deposit(state, deposit) + assert state.eth1_data.deposit_root == eth1_data.deposit_root + # Process genesis activations for validator in state.validators: if validator.effective_balance == MAX_EFFECTIVE_BALANCE: diff --git a/test_libs/pyspec/eth2spec/test/genesis/test_genesis_trigger.py b/test_libs/pyspec/eth2spec/test/genesis/test_genesis_trigger.py index 9425a1750..783def3ba 100644 --- a/test_libs/pyspec/eth2spec/test/genesis/test_genesis_trigger.py +++ b/test_libs/pyspec/eth2spec/test/genesis/test_genesis_trigger.py @@ -8,12 +8,11 @@ from eth2spec.test.helpers.deposits import ( @spectest_with_bls_switch def test_is_genesis_trigger_false(spec): deposit_count = 2 - genesis_deposits, deposit_root = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE) + genesis_deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE) genesis_time = 1546300800 yield "deposits", genesis_deposits yield "time", genesis_time - yield "deposit_root", deposit_root is_triggered = spec.is_genesis_trigger(genesis_deposits, genesis_time) assert is_triggered is False @@ -25,13 +24,12 @@ def test_is_genesis_trigger_false(spec): @spectest_with_bls_switch def test_is_genesis_trigger_true(spec): deposit_count = spec.GENESIS_ACTIVE_VALIDATOR_COUNT - genesis_deposits, deposit_root = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE) + genesis_deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE) SECONDS_PER_DAY = 86400 genesis_time = 1578009600 - 2 * SECONDS_PER_DAY yield "deposits", genesis_deposits yield "time", genesis_time - yield "deposit_root", deposit_root is_triggered = spec.is_genesis_trigger(genesis_deposits, genesis_time) assert is_triggered is True @@ -43,11 +41,10 @@ def test_is_genesis_trigger_true(spec): @spectest_with_bls_switch def test_is_genesis_trigger_not_enough_balance(spec): deposit_count = spec.GENESIS_ACTIVE_VALIDATOR_COUNT - genesis_deposits, deposit_root = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE - 1) + genesis_deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE - 1) genesis_time = 1546300800 yield "deposits", genesis_deposits yield "time", genesis_time - yield "deposit_root", deposit_root is_triggered = spec.is_genesis_trigger(genesis_deposits, genesis_time) assert is_triggered is False diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index bda4f1699..60ff2a3db 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -41,7 +41,9 @@ def build_deposit(spec, amount, withdrawal_credentials, signed): - deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, state=state, signed=signed) + deposit_data = build_deposit_data( + spec, pubkey, privkey, amount, withdrawal_credentials, state=state, signed=signed, + ) item = deposit_data.hash_tree_root() index = len(deposit_data_leaves) @@ -63,6 +65,7 @@ def build_deposit(spec, def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False): genesis_deposit_data_list = [] deposit_data_leaves = [] + genesis_deposits = [] for validator_index in range(genesis_validator_count): pubkey = pubkeys[validator_index] privkey = privkeys[validator_index] @@ -79,13 +82,11 @@ def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False deposit_data_leaves.append(item) genesis_deposit_data_list.append(deposit_data) - tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) - root = get_merkle_root((tuple(deposit_data_leaves))) - - genesis_deposits = list( - spec.Deposit(proof=list(get_merkle_proof(tree, item_index=index)), data=deposit_data) - for index, deposit_data in enumerate(genesis_deposit_data_list) - ) + tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) + root = get_merkle_root((tuple(deposit_data_leaves)), 2**32) + genesis_deposits.append( + spec.Deposit(proof=list(get_merkle_proof(tree, item_index=validator_index)), data=deposit_data) + ) return genesis_deposits, root diff --git a/test_libs/pyspec/eth2spec/utils/merkle_minimal.py b/test_libs/pyspec/eth2spec/utils/merkle_minimal.py index e3e81f3a5..e9416ea05 100644 --- a/test_libs/pyspec/eth2spec/utils/merkle_minimal.py +++ b/test_libs/pyspec/eth2spec/utils/merkle_minimal.py @@ -1,5 +1,5 @@ from .hash_function import hash -from math import log2 +from math import log2 ZERO_BYTES32 = b'\x00' * 32 diff --git a/test_libs/pyspec/eth2spec/utils/test_merkle_minimal.py b/test_libs/pyspec/eth2spec/utils/test_merkle_minimal.py index 1c72a649b..f1ed768e6 100644 --- a/test_libs/pyspec/eth2spec/utils/test_merkle_minimal.py +++ b/test_libs/pyspec/eth2spec/utils/test_merkle_minimal.py @@ -1,5 +1,5 @@ import pytest -from .merkle_minimal import zerohashes, merkleize_chunks +from .merkle_minimal import zerohashes, merkleize_chunks, get_merkle_root from .hash_function import hash @@ -53,6 +53,7 @@ cases = [ 'depth,count,pow2,value', cases, ) -def test_merkleize_chunks(depth, count, pow2, value): +def test_merkleize_chunks_and_get_merkle_root(depth, count, pow2, value): chunks = [e(i) for i in range(count)] assert merkleize_chunks(chunks, pad_to=pow2) == value + assert get_merkle_root(chunks, pad_to=pow2) == value