From 8d19e7a5703ae9976b87e0e3b5e2f8ef99d9c76a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 20 Mar 2019 08:22:47 -0600 Subject: [PATCH 01/56] port tests from dev to master --- Makefile | 2 +- .../test_process_block_header.py | 26 +++ .../block_processing/test_process_deposit.py | 132 +++++++++++++ .../block_processing/test_voluntary_exit.py | 174 ++++++++++++++++++ tests/phase0/conftest.py | 6 + tests/phase0/helpers.py | 58 +++++- utils/phase0/minimal_ssz.py | 21 ++- 7 files changed, 410 insertions(+), 9 deletions(-) create mode 100644 tests/phase0/block_processing/test_process_block_header.py create mode 100644 tests/phase0/block_processing/test_process_deposit.py create mode 100644 tests/phase0/block_processing/test_voluntary_exit.py diff --git a/Makefile b/Makefile index b45cec410..88f17dcf9 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ clean: # runs a limited set of tests against a minimal config # run pytest with `-m` option to full suite test: - pytest -m "sanity and minimal_config" tests/ + pytest -m minimal_config tests/ $(BUILD_DIR)/phase0: diff --git a/tests/phase0/block_processing/test_process_block_header.py b/tests/phase0/block_processing/test_process_block_header.py new file mode 100644 index 000000000..4ec7e336f --- /dev/null +++ b/tests/phase0/block_processing/test_process_block_header.py @@ -0,0 +1,26 @@ +from copy import deepcopy +import pytest + + +from build.phase0.spec import ( + get_beacon_proposer_index, + process_block_header, +) +from tests.phase0.helpers import ( + build_empty_block_for_next_slot, +) + +# mark entire file as 'header' +pytestmark = pytest.mark.header + + +def test_proposer_slashed(state): + pre_state = deepcopy(state) + + block = build_empty_block_for_next_slot(pre_state) + proposer_index = get_beacon_proposer_index(pre_state, block.slot) + pre_state.validator_registry[proposer_index].slashed = True + with pytest.raises(AssertionError): + process_block_header(pre_state, block) + + return state, [block], None diff --git a/tests/phase0/block_processing/test_process_deposit.py b/tests/phase0/block_processing/test_process_deposit.py new file mode 100644 index 000000000..297ad37f1 --- /dev/null +++ b/tests/phase0/block_processing/test_process_deposit.py @@ -0,0 +1,132 @@ +from copy import deepcopy +import pytest + +import build.phase0.spec as spec + +from build.phase0.spec import ( + Deposit, + process_deposit, +) +from tests.phase0.helpers import ( + build_deposit, +) + + +# mark entire file as 'voluntary_exits' +pytestmark = pytest.mark.voluntary_exits + + +def test_success(state, deposit_data_leaves, pubkeys, privkeys): + pre_state = deepcopy(state) + + index = len(deposit_data_leaves) + pubkey = pubkeys[index] + privkey = privkeys[index] + deposit, root, deposit_data_leaves = build_deposit( + pre_state, + deposit_data_leaves, + pubkey, + privkey, + spec.MAX_DEPOSIT_AMOUNT, + ) + + pre_state.latest_eth1_data.deposit_root = root + pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves) + + post_state = deepcopy(pre_state) + + process_deposit(post_state, deposit) + + assert len(post_state.validator_registry) == len(state.validator_registry) + 1 + assert len(post_state.validator_balances) == len(state.validator_balances) + 1 + assert post_state.validator_registry[index].pubkey == pubkeys[index] + assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count + + return pre_state, deposit, post_state + + +def test_success_top_up(state, deposit_data_leaves, pubkeys, privkeys): + pre_state = deepcopy(state) + + validator_index = 0 + amount = spec.MAX_DEPOSIT_AMOUNT // 4 + pubkey = pubkeys[validator_index] + privkey = privkeys[validator_index] + deposit, root, deposit_data_leaves = build_deposit( + pre_state, + deposit_data_leaves, + pubkey, + privkey, + amount, + ) + + pre_state.latest_eth1_data.deposit_root = root + pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves) + pre_balance = pre_state.validator_balances[validator_index] + + post_state = deepcopy(pre_state) + + process_deposit(post_state, deposit) + + assert len(post_state.validator_registry) == len(state.validator_registry) + assert len(post_state.validator_balances) == len(state.validator_balances) + assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count + assert post_state.validator_balances[validator_index] == pre_balance + amount + + return pre_state, deposit, post_state + + +def test_wrong_index(state, deposit_data_leaves, pubkeys, privkeys): + pre_state = deepcopy(state) + + index = len(deposit_data_leaves) + pubkey = pubkeys[index] + privkey = privkeys[index] + deposit, root, deposit_data_leaves = build_deposit( + pre_state, + deposit_data_leaves, + pubkey, + privkey, + spec.MAX_DEPOSIT_AMOUNT, + ) + + # mess up deposit_index + deposit.index = pre_state.deposit_index + 1 + + pre_state.latest_eth1_data.deposit_root = root + pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves) + + post_state = deepcopy(pre_state) + + with pytest.raises(AssertionError): + process_deposit(post_state, deposit) + + return pre_state, deposit, None + + +def test_bad_merkle_proof(state, deposit_data_leaves, pubkeys, privkeys): + pre_state = deepcopy(state) + + index = len(deposit_data_leaves) + pubkey = pubkeys[index] + privkey = privkeys[index] + deposit, root, deposit_data_leaves = build_deposit( + pre_state, + deposit_data_leaves, + pubkey, + privkey, + spec.MAX_DEPOSIT_AMOUNT, + ) + + # mess up merkle branch + deposit.proof[-1] = spec.ZERO_HASH + + pre_state.latest_eth1_data.deposit_root = root + pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves) + + post_state = deepcopy(pre_state) + + with pytest.raises(AssertionError): + process_deposit(post_state, deposit) + + return pre_state, deposit, None diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py new file mode 100644 index 000000000..0801e4292 --- /dev/null +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -0,0 +1,174 @@ +from copy import deepcopy +import pytest + +import build.phase0.spec as spec + +from build.phase0.spec import ( + get_active_validator_indices, + get_current_epoch, + process_voluntary_exit, +) +from tests.phase0.helpers import ( + build_voluntary_exit, +) + + +# mark entire file as 'voluntary_exits' +pytestmark = pytest.mark.voluntary_exits + + +def test_success(state, pub_to_priv): + pre_state = deepcopy(state) + # + # setup pre_state + # + # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit + pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + # + # build voluntary exit + # + current_epoch = get_current_epoch(pre_state) + validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] + privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + + voluntary_exit = build_voluntary_exit( + pre_state, + current_epoch, + validator_index, + privkey, + ) + + post_state = deepcopy(pre_state) + + # + # test valid exit + # + process_voluntary_exit(post_state, voluntary_exit) + + assert not pre_state.validator_registry[validator_index].initiated_exit + assert post_state.validator_registry[validator_index].initiated_exit + + return pre_state, voluntary_exit, post_state + + +def test_validator_not_active(state, pub_to_priv): + pre_state = deepcopy(state) + current_epoch = get_current_epoch(pre_state) + validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] + privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + + # + # setup pre_state + # + pre_state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH + + # + # build and test voluntary exit + # + voluntary_exit = build_voluntary_exit( + pre_state, + current_epoch, + validator_index, + privkey, + ) + + with pytest.raises(AssertionError): + process_voluntary_exit(pre_state, voluntary_exit) + + return pre_state, voluntary_exit, None + + +def test_validator_already_exited(state, pub_to_priv): + pre_state = deepcopy(state) + # + # setup pre_state + # + # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit + pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = get_current_epoch(pre_state) + validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] + privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + + # but validator already has exited + pre_state.validator_registry[validator_index].exit_epoch = current_epoch + 2 + + # + # build voluntary exit + # + voluntary_exit = build_voluntary_exit( + pre_state, + current_epoch, + validator_index, + privkey, + ) + + with pytest.raises(AssertionError): + process_voluntary_exit(pre_state, voluntary_exit) + + return pre_state, voluntary_exit, None + + +def test_validator_already_initiated_exit(state, pub_to_priv): + pre_state = deepcopy(state) + # + # setup pre_state + # + # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit + pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = get_current_epoch(pre_state) + validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] + privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + + # but validator already has initiated exit + pre_state.validator_registry[validator_index].initiated_exit = True + + # + # build voluntary exit + # + voluntary_exit = build_voluntary_exit( + pre_state, + current_epoch, + validator_index, + privkey, + ) + + with pytest.raises(AssertionError): + process_voluntary_exit(pre_state, voluntary_exit) + + return pre_state, voluntary_exit, None + + +def test_validator_not_active_long_enough(state, pub_to_priv): + pre_state = deepcopy(state) + # + # setup pre_state + # + current_epoch = get_current_epoch(pre_state) + validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] + privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + + # but validator already has initiated exit + pre_state.validator_registry[validator_index].initiated_exit = True + + # + # build voluntary exit + # + voluntary_exit = build_voluntary_exit( + pre_state, + current_epoch, + validator_index, + privkey, + ) + + assert ( + current_epoch - pre_state.validator_registry[validator_index].activation_epoch < + spec.PERSISTENT_COMMITTEE_PERIOD + ) + + with pytest.raises(AssertionError): + process_voluntary_exit(pre_state, voluntary_exit) + + return pre_state, voluntary_exit, None diff --git a/tests/phase0/conftest.py b/tests/phase0/conftest.py index e92896e92..395929028 100644 --- a/tests/phase0/conftest.py +++ b/tests/phase0/conftest.py @@ -5,6 +5,7 @@ from build.phase0 import spec from tests.phase0.helpers import ( privkeys_list, pubkeys_list, + pubkey_to_privkey, create_genesis_state, ) @@ -34,6 +35,11 @@ def pubkeys(): return pubkeys_list +@pytest.fixture +def pub_to_priv(): + return pubkey_to_privkey + + def overwrite_spec_config(config): for field in config: setattr(spec, field, config[field]) diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 510361e9c..5c61685a6 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -13,6 +13,7 @@ from build.phase0.spec import ( DepositInput, DepositData, Eth1Data, + VoluntaryExit, # functions get_block_root, get_current_epoch, @@ -71,17 +72,29 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves): def create_genesis_state(num_validators, deposit_data_leaves): - initial_deposits, deposit_root = create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves) + initial_deposits, deposit_root = create_mock_genesis_validator_deposits( + num_validators, + deposit_data_leaves, + ) return get_genesis_beacon_state( initial_deposits, genesis_time=0, genesis_eth1_data=Eth1Data( deposit_root=deposit_root, + deposit_count=len(initial_deposits), block_hash=spec.ZERO_HASH, ), ) +def force_registry_change_at_next_epoch(state): + # artificially trigger registry update at next epoch transition + state.finalized_epoch = get_current_epoch(state) - 1 + for crosslink in state.latest_crosslinks: + crosslink.epoch = state.finalized_epoch + state.validator_registry_update_epoch = state.finalized_epoch - 1 + + def build_empty_block_for_next_slot(state): empty_block = get_empty_block() empty_block.slot = state.slot + 1 @@ -143,3 +156,46 @@ def build_attestation_data(state, slot, shard): crosslink_data_root=spec.ZERO_HASH, previous_crosslink=deepcopy(state.latest_crosslinks[shard]), ) + + +def build_voluntary_exit(state, epoch, validator_index, privkey): + voluntary_exit = VoluntaryExit( + epoch=epoch, + validator_index=validator_index, + signature=EMPTY_SIGNATURE, + ) + voluntary_exit.signature = bls.sign( + message_hash=signed_root(voluntary_exit), + privkey=privkey, + domain=get_domain( + fork=state.fork, + epoch=epoch, + domain_type=spec.DOMAIN_VOLUNTARY_EXIT, + ) + ) + + return voluntary_exit + + +def build_deposit(state, + deposit_data_leaves, + pubkey, + privkey, + amount): + deposit_data = build_deposit_data(state, pubkey, privkey, amount) + + item = hash(deposit_data.serialize()) + index = len(deposit_data_leaves) + deposit_data_leaves.append(item) + tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) + root = get_merkle_root((tuple(deposit_data_leaves))) + proof = list(get_merkle_proof(tree, item_index=index)) + assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root) + + deposit = Deposit( + proof=list(proof), + index=index, + deposit_data=deposit_data, + ) + + return deposit, root, deposit_data_leaves diff --git a/utils/phase0/minimal_ssz.py b/utils/phase0/minimal_ssz.py index 08bd68357..c4828d08f 100644 --- a/utils/phase0/minimal_ssz.py +++ b/utils/phase0/minimal_ssz.py @@ -39,15 +39,22 @@ def SSZType(fields): return SSZObject -class Vector(list): - def __init__(self, x): - list.__init__(self, x) - self.length = len(x) +class Vector(): + def __init__(self, items): + self.items = items + self.length = len(items) - def append(*args): - raise Exception("Cannot change the length of a vector") + def __getitem__(self, key): + return self.items[key] - remove = clear = extend = pop = insert = append + def __setitem__(self, key, value): + self.items[key] = value + + def __iter__(self): + return iter(self.items) + + def __len__(self): + return self.length def is_basic(typ): From c26d09540df8ccdeb66d37af9bf87435f4dbd6de Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 20 Mar 2019 08:51:40 -0600 Subject: [PATCH 02/56] port header tests --- .../test_process_block_header.py | 68 +++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/tests/phase0/block_processing/test_process_block_header.py b/tests/phase0/block_processing/test_process_block_header.py index 4ec7e336f..8998a6e8f 100644 --- a/tests/phase0/block_processing/test_process_block_header.py +++ b/tests/phase0/block_processing/test_process_block_header.py @@ -3,7 +3,8 @@ import pytest from build.phase0.spec import ( - get_beacon_proposer_index, + cache_state, + advance_slot, process_block_header, ) from tests.phase0.helpers import ( @@ -14,13 +15,70 @@ from tests.phase0.helpers import ( pytestmark = pytest.mark.header -def test_proposer_slashed(state): +def test_sucess(state): + pre_state = deepcopy(state) + block = build_empty_block_for_next_slot(pre_state) + + # + # setup pre_state to be ready for block transition + # + cache_state(pre_state) + advance_slot(pre_state) + + post_state = deepcopy(pre_state) + + # + # test block header + # + process_block_header(post_state, block) + + return state, [block], post_state + + +def test_invalid_slot(state): pre_state = deepcopy(state) + # mess up previous block root block = build_empty_block_for_next_slot(pre_state) - proposer_index = get_beacon_proposer_index(pre_state, block.slot) - pre_state.validator_registry[proposer_index].slashed = True + block.previous_block_root = b'\12'*32 + + # + # setup pre_state advancing two slots to induce error + # + cache_state(pre_state) + advance_slot(pre_state) + advance_slot(pre_state) + + post_state = deepcopy(pre_state) + + # + # test block header + # with pytest.raises(AssertionError): - process_block_header(pre_state, block) + process_block_header(post_state, block) + + return state, [block], None + + +def test_invalid_previous_block_root(state): + pre_state = deepcopy(state) + + # mess up previous block root + block = build_empty_block_for_next_slot(pre_state) + block.previous_block_root = b'\12'*32 + + # + # setup pre_state to be ready for block transition + # + cache_state(pre_state) + advance_slot(pre_state) + + post_state = deepcopy(pre_state) + + # + # test block header + # + with pytest.raises(AssertionError): + process_block_header(post_state, block) return state, [block], None From c10384d65fa653cc395490a84bb1abe227bc74ad Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 20 Mar 2019 09:19:58 -0600 Subject: [PATCH 03/56] use signed_root for block id purposes in blocks/state --- specs/core/0_beacon-chain.md | 7 ++++--- tests/phase0/helpers.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index daa1bc108..57ad5e110 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -684,7 +684,8 @@ def get_temporary_block_header(block: BeaconBlock) -> BeaconBlockHeader: previous_block_root=block.previous_block_root, state_root=ZERO_HASH, block_body_root=hash_tree_root(block.body), - signature=block.signature, + # signed_root(block) is used for block id purposes so signature is a stub + signature=EMPTY_SIGNATURE, ) ``` @@ -1689,7 +1690,7 @@ def cache_state(state: BeaconState) -> None: state.latest_block_header.state_root = previous_slot_state_root # store latest known block for previous slot - state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = hash_tree_root(state.latest_block_header) + state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = signed_root(state.latest_block_header) ``` ### Per-epoch processing @@ -2250,7 +2251,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: # Verify that the slots match assert block.slot == state.slot # Verify that the parent matches - assert block.previous_block_root == hash_tree_root(state.latest_block_header) + assert block.previous_block_root == signed_root(state.latest_block_header) # Save current block as the new latest block state.latest_block_header = get_temporary_block_header(block) # Verify proposer signature diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 510361e9c..16f5a5ea4 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -88,7 +88,7 @@ def build_empty_block_for_next_slot(state): previous_block_header = deepcopy(state.latest_block_header) if previous_block_header.state_root == spec.ZERO_HASH: previous_block_header.state_root = state.hash_tree_root() - empty_block.previous_block_root = previous_block_header.hash_tree_root() + empty_block.previous_block_root = signed_root(previous_block_header) return empty_block From b65ff4988d4129bce535843d1e25b1290b72fb4d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 20 Mar 2019 10:20:08 -0600 Subject: [PATCH 04/56] fix updated tests --- .../test_process_block_header.py | 84 +++++++------------ 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/tests/phase0/block_processing/test_process_block_header.py b/tests/phase0/block_processing/test_process_block_header.py index 8998a6e8f..241466437 100644 --- a/tests/phase0/block_processing/test_process_block_header.py +++ b/tests/phase0/block_processing/test_process_block_header.py @@ -3,6 +3,7 @@ import pytest from build.phase0.spec import ( + get_beacon_proposer_index, cache_state, advance_slot, process_block_header, @@ -15,70 +16,45 @@ from tests.phase0.helpers import ( pytestmark = pytest.mark.header -def test_sucess(state): - pre_state = deepcopy(state) - block = build_empty_block_for_next_slot(pre_state) +def prepare_state_for_header_processing(state): + cache_state(state) + advance_slot(state) - # - # setup pre_state to be ready for block transition - # - cache_state(pre_state) - advance_slot(pre_state) - post_state = deepcopy(pre_state) +def run_block_header_processing(state, block, valid=True): + """ + Run ``process_block_header`` returning the pre and post state. + If ``valid == False``, run expecting ``AssertionError`` + """ + prepare_state_for_header_processing(state) + post_state = deepcopy(state) + + if not valid: + with pytest.raises(AssertionError): + process_block_header(post_state, block) + return state, None - # - # test block header - # process_block_header(post_state, block) + return state, post_state - return state, [block], post_state + +def test_success(state): + block = build_empty_block_for_next_slot(state) + pre_state, post_state = run_block_header_processing(state, block) + return state, block, post_state def test_invalid_slot(state): - pre_state = deepcopy(state) + block = build_empty_block_for_next_slot(state) + block.slot = state.slot + 2 # invalid slot - # mess up previous block root - block = build_empty_block_for_next_slot(pre_state) - block.previous_block_root = b'\12'*32 - - # - # setup pre_state advancing two slots to induce error - # - cache_state(pre_state) - advance_slot(pre_state) - advance_slot(pre_state) - - post_state = deepcopy(pre_state) - - # - # test block header - # - with pytest.raises(AssertionError): - process_block_header(post_state, block) - - return state, [block], None + pre_state, post_state = run_block_header_processing(state, block, valid=False) + return pre_state, block, None def test_invalid_previous_block_root(state): - pre_state = deepcopy(state) + block = build_empty_block_for_next_slot(state) + block.previous_block_root = b'\12'*32 # invalid prev root - # mess up previous block root - block = build_empty_block_for_next_slot(pre_state) - block.previous_block_root = b'\12'*32 - - # - # setup pre_state to be ready for block transition - # - cache_state(pre_state) - advance_slot(pre_state) - - post_state = deepcopy(pre_state) - - # - # test block header - # - with pytest.raises(AssertionError): - process_block_header(post_state, block) - - return state, [block], None + pre_state, post_state = run_block_header_processing(state, block, valid=False) + return pre_state, block, None From 3e70478078608db8bd250981cdf9a1546f9ca143 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 21 Mar 2019 08:36:45 -0600 Subject: [PATCH 05/56] #805 direct to master --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 57ad5e110..fead1fad9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2134,8 +2134,8 @@ def update_registry_and_shuffling_data(state: BeaconState) -> None: state.current_shuffling_epoch = next_epoch state.current_shuffling_start_shard = ( state.current_shuffling_start_shard + - get_current_epoch_committee_count(state) % SHARD_COUNT - ) + get_current_epoch_committee_count(state) + ) % SHARD_COUNT state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch) else: # If processing at least one crosslink keeps failing, then reshuffle every power of two, From 65b1b8555cbf1bd87ff59ee0d72e7f01bad3a2de Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 25 Mar 2019 10:37:34 -0600 Subject: [PATCH 06/56] cleanup tests to more easily dump output --- .gitignore | 1 + tests/phase0/__init__.py | 0 .../block_processing/test_process_deposit.py | 18 +++++-- .../block_processing/test_voluntary_exit.py | 21 ++++---- tests/phase0/conftest.py | 18 ------- tests/phase0/helpers.py | 14 ++--- tests/phase0/test_sanity.py | 18 ++++--- utils/phase0/jsonize.py | 52 +++++++++++++++++++ 8 files changed, 95 insertions(+), 47 deletions(-) create mode 100644 tests/phase0/__init__.py create mode 100644 utils/phase0/jsonize.py diff --git a/.gitignore b/.gitignore index dfb38d170..f33dd5256 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /.pytest_cache build/ +output/ diff --git a/tests/phase0/__init__.py b/tests/phase0/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/phase0/block_processing/test_process_deposit.py b/tests/phase0/block_processing/test_process_deposit.py index 297ad37f1..0c5770195 100644 --- a/tests/phase0/block_processing/test_process_deposit.py +++ b/tests/phase0/block_processing/test_process_deposit.py @@ -4,11 +4,13 @@ import pytest import build.phase0.spec as spec from build.phase0.spec import ( - Deposit, + ZERO_HASH, process_deposit, ) from tests.phase0.helpers import ( build_deposit, + privkeys, + pubkeys, ) @@ -16,8 +18,10 @@ from tests.phase0.helpers import ( pytestmark = pytest.mark.voluntary_exits -def test_success(state, deposit_data_leaves, pubkeys, privkeys): +def test_success(state): pre_state = deepcopy(state) + # fill previous deposits with zero-hash + deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) index = len(deposit_data_leaves) pubkey = pubkeys[index] @@ -45,8 +49,9 @@ def test_success(state, deposit_data_leaves, pubkeys, privkeys): return pre_state, deposit, post_state -def test_success_top_up(state, deposit_data_leaves, pubkeys, privkeys): +def test_success_top_up(state): pre_state = deepcopy(state) + deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) validator_index = 0 amount = spec.MAX_DEPOSIT_AMOUNT // 4 @@ -76,8 +81,10 @@ def test_success_top_up(state, deposit_data_leaves, pubkeys, privkeys): return pre_state, deposit, post_state -def test_wrong_index(state, deposit_data_leaves, pubkeys, privkeys): +def test_wrong_index(state): pre_state = deepcopy(state) + deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) + index = len(deposit_data_leaves) pubkey = pubkeys[index] @@ -104,8 +111,9 @@ def test_wrong_index(state, deposit_data_leaves, pubkeys, privkeys): return pre_state, deposit, None -def test_bad_merkle_proof(state, deposit_data_leaves, pubkeys, privkeys): +def test_bad_merkle_proof(state): pre_state = deepcopy(state) + deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) index = len(deposit_data_leaves) pubkey = pubkeys[index] diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index 0801e4292..6adc81464 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -10,6 +10,7 @@ from build.phase0.spec import ( ) from tests.phase0.helpers import ( build_voluntary_exit, + pubkey_to_privkey, ) @@ -17,7 +18,7 @@ from tests.phase0.helpers import ( pytestmark = pytest.mark.voluntary_exits -def test_success(state, pub_to_priv): +def test_success(state): pre_state = deepcopy(state) # # setup pre_state @@ -30,7 +31,7 @@ def test_success(state, pub_to_priv): # current_epoch = get_current_epoch(pre_state) validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit( pre_state, @@ -52,11 +53,11 @@ def test_success(state, pub_to_priv): return pre_state, voluntary_exit, post_state -def test_validator_not_active(state, pub_to_priv): +def test_validator_not_active(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] # # setup pre_state @@ -79,7 +80,7 @@ def test_validator_not_active(state, pub_to_priv): return pre_state, voluntary_exit, None -def test_validator_already_exited(state, pub_to_priv): +def test_validator_already_exited(state): pre_state = deepcopy(state) # # setup pre_state @@ -89,7 +90,7 @@ def test_validator_already_exited(state, pub_to_priv): current_epoch = get_current_epoch(pre_state) validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] # but validator already has exited pre_state.validator_registry[validator_index].exit_epoch = current_epoch + 2 @@ -110,7 +111,7 @@ def test_validator_already_exited(state, pub_to_priv): return pre_state, voluntary_exit, None -def test_validator_already_initiated_exit(state, pub_to_priv): +def test_validator_already_initiated_exit(state): pre_state = deepcopy(state) # # setup pre_state @@ -120,7 +121,7 @@ def test_validator_already_initiated_exit(state, pub_to_priv): current_epoch = get_current_epoch(pre_state) validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] # but validator already has initiated exit pre_state.validator_registry[validator_index].initiated_exit = True @@ -141,14 +142,14 @@ def test_validator_already_initiated_exit(state, pub_to_priv): return pre_state, voluntary_exit, None -def test_validator_not_active_long_enough(state, pub_to_priv): +def test_validator_not_active_long_enough(state): pre_state = deepcopy(state) # # setup pre_state # current_epoch = get_current_epoch(pre_state) validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] # but validator already has initiated exit pre_state.validator_registry[validator_index].initiated_exit = True diff --git a/tests/phase0/conftest.py b/tests/phase0/conftest.py index 395929028..36a087941 100644 --- a/tests/phase0/conftest.py +++ b/tests/phase0/conftest.py @@ -3,9 +3,6 @@ import pytest from build.phase0 import spec from tests.phase0.helpers import ( - privkeys_list, - pubkeys_list, - pubkey_to_privkey, create_genesis_state, ) @@ -25,21 +22,6 @@ MINIMAL_CONFIG = { } -@pytest.fixture -def privkeys(): - return privkeys_list - - -@pytest.fixture -def pubkeys(): - return pubkeys_list - - -@pytest.fixture -def pub_to_priv(): - return pubkey_to_privkey - - def overwrite_spec_config(config): for field in config: setattr(spec, field, config[field]) diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 134fa797d..20054b821 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -31,18 +31,20 @@ from build.phase0.utils.merkle_minimal import ( ) -privkeys_list = [i + 1 for i in range(1000)] -pubkeys_list = [bls.privtopub(privkey) for privkey in privkeys_list] -pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys_list, pubkeys_list)} +privkeys = [i + 1 for i in range(1000)] +pubkeys = [bls.privtopub(privkey) for privkey in privkeys] +pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)} -def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves): +def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None): + if not deposit_data_leaves: + deposit_data_leaves = [] deposit_timestamp = 0 proof_of_possession = b'\x33' * 96 deposit_data_list = [] for i in range(num_validators): - pubkey = pubkeys_list[i] + pubkey = pubkeys[i] deposit_data = DepositData( amount=spec.MAX_DEPOSIT_AMOUNT, timestamp=deposit_timestamp, @@ -71,7 +73,7 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves): return genesis_validator_deposits, root -def create_genesis_state(num_validators, deposit_data_leaves): +def create_genesis_state(num_validators, deposit_data_leaves=None): initial_deposits, deposit_root = create_mock_genesis_validator_deposits( num_validators, deposit_data_leaves, diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 8f04f316c..6de6dfb45 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -43,6 +43,8 @@ from tests.phase0.helpers import ( build_attestation_data, build_deposit_data, build_empty_block_for_next_slot, + privkeys, + pubkeys, ) @@ -112,7 +114,7 @@ def test_empty_epoch_transition_not_finalizing(state): return state, [block], test_state -def test_proposer_slashing(state, pubkeys, privkeys): +def test_proposer_slashing(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) validator_index = get_active_validator_indices(test_state.validator_registry, current_epoch)[-1] @@ -172,9 +174,9 @@ def test_proposer_slashing(state, pubkeys, privkeys): return state, [block], test_state -def test_deposit_in_block(state, deposit_data_leaves, pubkeys, privkeys): +def test_deposit_in_block(state): pre_state = deepcopy(state) - test_deposit_data_leaves = deepcopy(deposit_data_leaves) + test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) index = len(test_deposit_data_leaves) pubkey = pubkeys[index] @@ -207,9 +209,9 @@ def test_deposit_in_block(state, deposit_data_leaves, pubkeys, privkeys): return pre_state, [block], post_state -def test_deposit_top_up(state, pubkeys, privkeys, deposit_data_leaves): +def test_deposit_top_up(state): pre_state = deepcopy(state) - test_deposit_data_leaves = deepcopy(deposit_data_leaves) + test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) validator_index = 0 amount = spec.MAX_DEPOSIT_AMOUNT // 4 @@ -245,7 +247,7 @@ def test_deposit_top_up(state, pubkeys, privkeys, deposit_data_leaves): return pre_state, [block], post_state -def test_attestation(state, pubkeys, privkeys): +def test_attestation(state): test_state = deepcopy(state) slot = state.slot shard = state.current_shuffling_start_shard @@ -314,7 +316,7 @@ def test_attestation(state, pubkeys, privkeys): return state, [attestation_block, epoch_block], test_state -def test_voluntary_exit(state, pubkeys, privkeys): +def test_voluntary_exit(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices(pre_state.validator_registry, get_current_epoch(pre_state))[-1] @@ -363,7 +365,7 @@ def test_voluntary_exit(state, pubkeys, privkeys): return pre_state, [initiate_exit_block, exit_block], post_state -def test_transfer(state, pubkeys, privkeys): +def test_transfer(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) sender_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1] diff --git a/utils/phase0/jsonize.py b/utils/phase0/jsonize.py new file mode 100644 index 000000000..816192ec6 --- /dev/null +++ b/utils/phase0/jsonize.py @@ -0,0 +1,52 @@ +from .minimal_ssz import hash_tree_root + + +def jsonize(value, typ, include_hash_tree_roots=False): + if isinstance(typ, str) and typ[:4] == 'uint': + return value + elif typ == 'bool': + assert value in (True, False) + return value + elif isinstance(typ, list): + return [jsonize(element, typ[0], include_hash_tree_roots) for element in value] + elif isinstance(typ, str) and typ[:4] == 'byte': + return '0x' + value.hex() + elif hasattr(typ, 'fields'): + ret = {} + for field, subtype in typ.fields.items(): + ret[field] = jsonize(getattr(value, field), subtype, include_hash_tree_roots) + if include_hash_tree_roots: + ret[field + "_hash_tree_root"] = '0x' + hash_tree_root(getattr(value, field), subtype).hex() + if include_hash_tree_roots: + ret["hash_tree_root"] = '0x' + hash_tree_root(value, typ).hex() + return ret + else: + print(value, typ) + raise Exception("Type not recognized") + + +def dejsonize(json, typ): + if isinstance(typ, str) and typ[:4] == 'uint': + return json + elif typ == 'bool': + assert json in (True, False) + return json + elif isinstance(typ, list): + return [dejsonize(element, typ[0]) for element in json] + elif isinstance(typ, str) and typ[:4] == 'byte': + return bytes.fromhex(json[2:]) + elif hasattr(typ, 'fields'): + temp = {} + for field, subtype in typ.fields.items(): + temp[field] = dejsonize(json[field], subtype) + if field + "_hash_tree_root" in json: + assert(json[field + "_hash_tree_root"][2:] == + hash_tree_root(temp[field], subtype).hex()) + ret = typ(**temp) + if "hash_tree_root" in json: + assert(json["hash_tree_root"][2:] == + hash_tree_root(ret, typ).hex()) + return ret + else: + print(json, typ) + raise Exception("Type not recognized") From 3fc24f3d415280484ff1c99813d060d39b242983 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 31 Mar 2019 21:20:43 -0500 Subject: [PATCH 07/56] Replace with empty instead of popping finished challenges --- specs/core/1_custody-game.md | 44 +++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index fd754634e..41ba9d953 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -31,6 +31,7 @@ - [`get_crosslink_chunk_count`](#get_crosslink_chunk_count) - [`get_custody_chunk_bit`](#get_custody_chunk_bit) - [`epoch_to_custody_period`](#epoch_to_custody_period) + - [`replace_empty_or_append`](#replace_empty_or_append) - [`verify_custody_key`](#verify_custody_key) - [Per-block processing](#per-block-processing) - [Transactions](#transactions) @@ -229,6 +230,18 @@ def epoch_to_custody_period(epoch: Epoch) -> int: return epoch // EPOCHS_PER_CUSTODY_PERIOD ``` +### `replace_empty_or_append` + +```python +def replace_empty_or_append(list: List[Any], empty_element: Any, new_element: Any) -> int: + for i in range(len(list)): + if list[i] == empty_element: + list[i] = new_element + return i + list.append(new_element) + return len(list) - 1 +``` + ### `verify_custody_key` ```python @@ -321,7 +334,7 @@ def process_chunk_challenge(state: BeaconState, depth = math.log2(next_power_of_two(get_custody_chunk_count(challenge.attestation))) assert challenge.chunk_index < 2**depth # Add new chunk challenge record - state.custody_chunk_challenge_records.append(CustodyChunkChallengeRecord( + new_record = CustodyChunkChallengeRecord( challenge_index=state.custody_challenge_index, challenger_index=get_beacon_proposer_index(state, state.slot), responder_index=challenge.responder_index @@ -329,7 +342,13 @@ def process_chunk_challenge(state: BeaconState, crosslink_data_root=challenge.attestation.data.crosslink_data_root, depth=depth, chunk_index=challenge.chunk_index, - )) + ) + replace_empty_or_append( + list=state.custody_chunk_challenge_records, + empty_element=CustodyChunkChallengeRecord(), + new_element=new_record + ) + state.custody_challenge_index += 1 # Postpone responder withdrawability responder.withdrawable_epoch = FAR_FUTURE_EPOCH @@ -385,7 +404,7 @@ def process_bit_challenge(state: BeaconState, custody_bit = get_bitfield_bit(attestation.custody_bitfield, attesters.index(responder_index)) assert custody_bit != chunk_bits_xor # Add new bit challenge record - state.custody_bit_challenge_records.append(CustodyBitChallengeRecord( + new_record = CustodyBitChallengeRecord( challenge_index=state.custody_challenge_index, challenger_index=challenge.challenger_index, responder_index=challenge.responder_index, @@ -393,7 +412,12 @@ def process_bit_challenge(state: BeaconState, crosslink_data_root=challenge.attestation.crosslink_data_root, chunk_bits=challenge.chunk_bits, responder_key=challenge.responder_key, - )) + ) + replace_empty_or_append( + list=state.custody_bit_challenge_records, + empty_element=CustodyBitChallengeRecord(), + new_element=new_record + ) state.custody_challenge_index += 1 # Postpone responder withdrawability responder.withdrawable_epoch = FAR_FUTURE_EPOCH @@ -434,7 +458,8 @@ def process_chunk_challenge_response(state: BeaconState, root=challenge.crosslink_data_root, ) # Clear the challenge - state.custody_chunk_challenge_records.remove(challenge) + records = state.custody_chunk_challenge_records + records[records.index(challenge)] = CustodyChunkChallengeRecord() # Reward the proposer proposer_index = get_beacon_proposer_index(state, state.slot) increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT) @@ -457,7 +482,8 @@ def process_bit_challenge_response(state: BeaconState, # Verify the chunk bit does not match the challenge chunk bit assert get_custody_chunk_bit(challenge.responder_key, response.chunk) != get_bitfield_bit(challenge.chunk_bits, response.chunk_index) # Clear the challenge - state.custody_bit_challenge_records.remove(challenge) + records = state.custody_bit_challenge_records + records[records.index(challenge)] = CustodyBitChallengeRecord() # Slash challenger slash_validator(state, challenge.challenger_index, challenge.responder_index) ``` @@ -471,12 +497,14 @@ def process_challenge_deadlines(state: BeaconState) -> None: for challenge in state.custody_chunk_challenge_records: if get_current_epoch(state) > challenge.deadline: slash_validator(state, challenge.responder_index, challenge.challenger_index) - state.custody_chunk_challenge_records.remove(challenge) + records = state.custody_chunk_challenge_records + records[records.index(challenge)] = CustodyChunkChallengeRecord() for challenge in state.custody_bit_challenge_records: if get_current_epoch(state) > challenge.deadline: slash_validator(state, challenge.responder_index, challenge.challenger_index) - state.custody_bit_challenge_records.remove(challenge) + records = state.custody_bit_challenge_records + records[records.index(challenge)] = CustodyBitChallengeRecord() ``` In `process_penalties_and_exits`, change the definition of `eligible` to the following (note that it is not a pure function because `state` is declared in the surrounding scope): From 9dde3a26612cbe1636529ec0d85f8462c8271f9b Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 9 Apr 2019 05:59:00 -0500 Subject: [PATCH 08/56] Update replace_empty_or_append Requires adding definitions of `empty` and `typeof` to the function puller. --- specs/core/1_custody-game.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 41ba9d953..88341ae98 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -233,9 +233,9 @@ def epoch_to_custody_period(epoch: Epoch) -> int: ### `replace_empty_or_append` ```python -def replace_empty_or_append(list: List[Any], empty_element: Any, new_element: Any) -> int: +def replace_empty_or_append(list: List[Any], new_element: Any) -> int: for i in range(len(list)): - if list[i] == empty_element: + if list[i] == empty(typeof(new_element)): list[i] = new_element return i list.append(new_element) @@ -343,11 +343,7 @@ def process_chunk_challenge(state: BeaconState, depth=depth, chunk_index=challenge.chunk_index, ) - replace_empty_or_append( - list=state.custody_chunk_challenge_records, - empty_element=CustodyChunkChallengeRecord(), - new_element=new_record - ) + replace_empty_or_append(state.custody_chunk_challenge_records, new_record) state.custody_challenge_index += 1 # Postpone responder withdrawability @@ -413,11 +409,7 @@ def process_bit_challenge(state: BeaconState, chunk_bits=challenge.chunk_bits, responder_key=challenge.responder_key, ) - replace_empty_or_append( - list=state.custody_bit_challenge_records, - empty_element=CustodyBitChallengeRecord(), - new_element=new_record - ) + replace_empty_or_append(state.custody_bit_challenge_records, new_record) state.custody_challenge_index += 1 # Postpone responder withdrawability responder.withdrawable_epoch = FAR_FUTURE_EPOCH From 9eba123e2e2e559c16b1b34335b7cd9b8ddc3c55 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 15 Apr 2019 07:54:08 +1000 Subject: [PATCH 09/56] Remove serialization from consensus Consensus now only cares about Merkleisation (i.e. `hash_tree_root`), not about serialization (i.e. `serialize`). This simplifies consensus code by a few tens of lines, is conceptually cleaner, and is more future proof. A corresponding change is required in the deposit contract. --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2d150837e..cb57c0a45 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2230,7 +2230,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: # Verify the Merkle branch merkle_branch_is_valid = verify_merkle_branch( - leaf=hash(serialize(deposit.data)), # 48 + 32 + 8 + 96 = 184 bytes serialization + leaf=hash_tree_root(deposit.data), proof=deposit.proof, depth=DEPOSIT_CONTRACT_TREE_DEPTH, index=deposit.index, From a25c436b78983b68ee40b40c79df3583b8c14159 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 15 Apr 2019 08:14:33 +1000 Subject: [PATCH 10/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cb57c0a45..dbf399a8c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1360,7 +1360,7 @@ The initial deployment phases of Ethereum 2.0 are implemented without consensus ### Deposit arguments -The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositData`. +The deposit contract has a single `deposit` function which takes as argument the `DepositData` fields. ### Withdrawal credentials From b6b82ae494edca81d92ad9ca5872c641f7fc3a7e Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 15 Apr 2019 08:15:20 +1000 Subject: [PATCH 11/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dbf399a8c..dbc1c9b5b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1360,7 +1360,7 @@ The initial deployment phases of Ethereum 2.0 are implemented without consensus ### Deposit arguments -The deposit contract has a single `deposit` function which takes as argument the `DepositData` fields. +The deposit contract has a single `deposit` function which takes as argument the `DepositData` elements. ### Withdrawal credentials From 084919e06383d1959d15484368b5f67e1ae61792 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 15 Apr 2019 08:29:08 +1000 Subject: [PATCH 12/56] Adjust tests --- tests/phase0/helpers.py | 4 ++-- tests/phase0/test_sanity.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 8c8064fc1..e20bb9484 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -61,7 +61,7 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=N amount=spec.MAX_DEPOSIT_AMOUNT, proof_of_possession=proof_of_possession, ) - item = hash(deposit_data.serialize()) + item = deposit_data.hash_tree_root() deposit_data_leaves.append(item) tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) root = get_merkle_root((tuple(deposit_data_leaves))) @@ -180,7 +180,7 @@ def build_deposit(state, amount): deposit_data = build_deposit_data(state, pubkey, privkey, amount) - item = hash(deposit_data.serialize()) + item = deposit_data.hash_tree_root() index = len(deposit_data_leaves) deposit_data_leaves.append(item) tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 08c7610c0..2c13c2415 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -179,7 +179,7 @@ def test_deposit_in_block(state): privkey = privkeys[index] deposit_data = build_deposit_data(pre_state, pubkey, privkey, spec.MAX_DEPOSIT_AMOUNT) - item = hash(deposit_data.serialize()) + item = deposit_data.hash_tree_root() test_deposit_data_leaves.append(item) tree = calc_merkle_tree_from_leaves(tuple(test_deposit_data_leaves)) root = get_merkle_root((tuple(test_deposit_data_leaves))) @@ -218,7 +218,7 @@ def test_deposit_top_up(state): deposit_data = build_deposit_data(pre_state, pubkey, privkey, amount) merkle_index = len(test_deposit_data_leaves) - item = hash(deposit_data.serialize()) + item = deposit_data.hash_tree_root() test_deposit_data_leaves.append(item) tree = calc_merkle_tree_from_leaves(tuple(test_deposit_data_leaves)) root = get_merkle_root((tuple(test_deposit_data_leaves))) From ed28515a9570bfb69f440e93488691d36df2e0e3 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Tue, 16 Apr 2019 16:16:13 +1000 Subject: [PATCH 13/56] Enables transferes of BAL > 32 ETH --- .../block_processing/test_process_deposit.py | 10 +++++----- py_tests/phase0/helpers.py | 2 +- py_tests/phase0/test_sanity.py | 6 +++--- specs/core/0_beacon-chain.md | 19 ++++++++++--------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/py_tests/phase0/block_processing/test_process_deposit.py b/py_tests/phase0/block_processing/test_process_deposit.py index cd682a4d4..31862c75d 100644 --- a/py_tests/phase0/block_processing/test_process_deposit.py +++ b/py_tests/phase0/block_processing/test_process_deposit.py @@ -32,7 +32,7 @@ def test_success(state): deposit_data_leaves, pubkey, privkey, - spec.MAX_DEPOSIT_AMOUNT, + spec.MAX_EFFECTIVE_BALANCE, ) pre_state.latest_eth1_data.deposit_root = root @@ -45,7 +45,7 @@ def test_success(state): assert len(post_state.validator_registry) == len(state.validator_registry) + 1 assert len(post_state.balances) == len(state.balances) + 1 assert post_state.validator_registry[index].pubkey == pubkeys[index] - assert get_balance(post_state, index) == spec.MAX_DEPOSIT_AMOUNT + assert get_balance(post_state, index) == spec.MAX_EFFECTIVE_BALANCE assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count return pre_state, deposit, post_state @@ -56,7 +56,7 @@ def test_success_top_up(state): deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) validator_index = 0 - amount = spec.MAX_DEPOSIT_AMOUNT // 4 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 pubkey = pubkeys[validator_index] privkey = privkeys[validator_index] deposit, root, deposit_data_leaves = build_deposit( @@ -95,7 +95,7 @@ def test_wrong_index(state): deposit_data_leaves, pubkey, privkey, - spec.MAX_DEPOSIT_AMOUNT, + spec.MAX_EFFECTIVE_BALANCE, ) # mess up deposit_index @@ -124,7 +124,7 @@ def test_bad_merkle_proof(state): deposit_data_leaves, pubkey, privkey, - spec.MAX_DEPOSIT_AMOUNT, + spec.MAX_EFFECTIVE_BALANCE, ) # mess up merkle branch diff --git a/py_tests/phase0/helpers.py b/py_tests/phase0/helpers.py index b28f480b2..cef4f4827 100644 --- a/py_tests/phase0/helpers.py +++ b/py_tests/phase0/helpers.py @@ -58,7 +58,7 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=N pubkey=pubkey, # insecurely use pubkey as withdrawal key as well withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:], - amount=spec.MAX_DEPOSIT_AMOUNT, + amount=spec.MAX_EFFECTIVE_BALANCE, proof_of_possession=proof_of_possession, ) item = hash(deposit_data.serialize()) diff --git a/py_tests/phase0/test_sanity.py b/py_tests/phase0/test_sanity.py index 95bf9089c..cc987002e 100644 --- a/py_tests/phase0/test_sanity.py +++ b/py_tests/phase0/test_sanity.py @@ -177,7 +177,7 @@ def test_deposit_in_block(state): index = len(test_deposit_data_leaves) pubkey = pubkeys[index] privkey = privkeys[index] - deposit_data = build_deposit_data(pre_state, pubkey, privkey, spec.MAX_DEPOSIT_AMOUNT) + deposit_data = build_deposit_data(pre_state, pubkey, privkey, spec.MAX_EFFECTIVE_BALANCE) item = hash(deposit_data.serialize()) test_deposit_data_leaves.append(item) @@ -201,7 +201,7 @@ def test_deposit_in_block(state): state_transition(post_state, block) assert len(post_state.validator_registry) == len(state.validator_registry) + 1 assert len(post_state.balances) == len(state.balances) + 1 - assert get_balance(post_state, index) == spec.MAX_DEPOSIT_AMOUNT + assert get_balance(post_state, index) == spec.MAX_EFFECTIVE_BALANCE assert post_state.validator_registry[index].pubkey == pubkeys[index] return pre_state, [block], post_state @@ -212,7 +212,7 @@ def test_deposit_top_up(state): test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry) validator_index = 0 - amount = spec.MAX_DEPOSIT_AMOUNT // 4 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 pubkey = pubkeys[validator_index] privkey = privkeys[validator_index] deposit_data = build_deposit_data(pre_state, pubkey, privkey, amount) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2d150837e..2e84b958f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -201,7 +201,7 @@ These configurations are updated for releases, but may be out of sync during `de | Name | Value | Unit | | - | - | :-: | | `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei | -| `MAX_DEPOSIT_AMOUNT` | `2**5 * 10**9` (= 32,000,000,000) | Gwei | +| `MAX_EFFECTIVE_BALANCE` | `2**5 * 10**9` (= 32,000,000,000) | Gwei | | `EJECTION_BALANCE` | `2**4 * 10**9` (= 16,000,000,000) | Gwei | | `HIGH_BALANCE_INCREMENT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei | @@ -1002,7 +1002,7 @@ def get_beacon_proposer_index(state: BeaconState, int_to_bytes8(i // 32) )[i % 32] candidate = first_committee[(current_epoch + i) % len(first_committee)] - if get_effective_balance(state, candidate) * 256 > MAX_DEPOSIT_AMOUNT * rand_byte: + if get_effective_balance(state, candidate) * 256 > MAX_EFFECTIVE_BALANCE * rand_byte: return candidate i += 1 ``` @@ -1081,7 +1081,7 @@ def get_effective_balance(state: BeaconState, index: ValidatorIndex) -> Gwei: """ Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. """ - return min(get_balance(state, index), MAX_DEPOSIT_AMOUNT) + return min(get_balance(state, index), MAX_EFFECTIVE_BALANCE) ``` ### `get_total_balance` @@ -1373,7 +1373,7 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat ### `Deposit` logs -Every Ethereum 1.0 deposit, of size between `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12 signature) is not verified by the deposit contract. +Every Ethereum 1.0 deposit, of size greater than `MIN_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12 signature) is not verified by the deposit contract. ### `Eth2Genesis` log @@ -1395,7 +1395,7 @@ For convenience, we provide the interface to the contract here: * `__init__()`: initializes the contract * `get_deposit_root() -> bytes32`: returns the current root of the deposit tree -* `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be within `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, inclusive. Each of these constants are specified in units of Gwei. +* `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be greater than `MIN_DEPOSIT_AMOUNT` inclusive. Each of these constants are specified in units of Gwei. ## On genesis @@ -1495,7 +1495,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], # Process genesis activations for validator_index in range(len(state.validator_registry)): - if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: + if get_effective_balance(state, validator_index) >= MAX_EFFECTIVE_BALANCE: activate_validator(state, validator_index, is_genesis=True) genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) @@ -1936,7 +1936,7 @@ def process_balance_driven_status_transitions(state: BeaconState) -> None: """ for index, validator in enumerate(state.validator_registry): balance = get_balance(state, index) - if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT: + if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_EFFECTIVE_BALANCE: validator.activation_eligibility_epoch = get_current_epoch(state) if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE: @@ -2335,10 +2335,11 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert get_balance(state, transfer.sender) >= max(transfer.amount, transfer.fee) # A transfer is valid in only one slot assert state.slot == transfer.slot - # Only withdrawn or not-yet-deposited accounts can transfer + # Only withdrawn, not-yet-deposited accounts, or the balance over MAX_EFFECTIVE_BALANCE can be transfered assert ( get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or - state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH + state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH or + transfer.amount + transfer.fee >= get_balance(statetransfer.sender) - get_effective_balance(transfer.sender) ) # Verify that the pubkey is valid assert ( From ae0afe389fc52ed5554b80fdae7960fd61a99299 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Tue, 16 Apr 2019 20:11:51 +1000 Subject: [PATCH 14/56] Cleaner assertion --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2e84b958f..974131027 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2339,7 +2339,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert ( get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH or - transfer.amount + transfer.fee >= get_balance(statetransfer.sender) - get_effective_balance(transfer.sender) + transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(transfer.sender) ) # Verify that the pubkey is valid assert ( From c783cdb2f4833d1de4767ab75fb5f5c49ae567e9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 17 Apr 2019 12:31:00 -0600 Subject: [PATCH 15/56] fix bug and add transfer tests --- specs/core/0_beacon-chain.md | 2 +- .../block_processing/test_process_transfer.py | 143 ++++++++++++++++++ test_libs/pyspec/tests/helpers.py | 50 ++++++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 test_libs/pyspec/tests/block_processing/test_process_transfer.py diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6aa562ca4..86a017508 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2280,7 +2280,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert ( get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH or - transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(transfer.sender) + transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(state, transfer.sender) ) # Verify that the pubkey is valid assert ( diff --git a/test_libs/pyspec/tests/block_processing/test_process_transfer.py b/test_libs/pyspec/tests/block_processing/test_process_transfer.py new file mode 100644 index 000000000..df49aff98 --- /dev/null +++ b/test_libs/pyspec/tests/block_processing/test_process_transfer.py @@ -0,0 +1,143 @@ +from copy import deepcopy +import pytest + +import eth2spec.phase0.spec as spec + +from eth2spec.phase0.spec import ( + get_active_validator_indices, + get_balance, + get_beacon_proposer_index, + get_current_epoch, + process_transfer, + set_balance, +) +from tests.helpers import ( + get_valid_transfer, + next_epoch, +) + + +# mark entire file as 'transfers' +pytestmark = pytest.mark.transfers + + +def run_transfer_processing(state, transfer, valid=True): + """ + Run ``process_transfer`` returning the pre and post state. + If ``valid == False``, run expecting ``AssertionError`` + """ + post_state = deepcopy(state) + + if not valid: + with pytest.raises(AssertionError): + process_transfer(post_state, transfer) + return state, None + + + process_transfer(post_state, transfer) + + proposer_index = get_beacon_proposer_index(state) + pre_transfer_sender_balance = state.balances[transfer.sender] + pre_transfer_recipient_balance = state.balances[transfer.recipient] + pre_transfer_proposer_balance = state.balances[proposer_index] + sender_balance = post_state.balances[transfer.sender] + recipient_balance = post_state.balances[transfer.recipient] + assert sender_balance == pre_transfer_sender_balance - transfer.amount - transfer.fee + assert recipient_balance == pre_transfer_recipient_balance + transfer.amount + assert post_state.balances[proposer_index] == pre_transfer_proposer_balance + transfer.fee + + return state, post_state + + +def test_success_non_activated(state): + transfer = get_valid_transfer(state) + # un-activate so validator can transfer + state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + + pre_state, post_state = run_transfer_processing(state, transfer) + + return pre_state, transfer, post_state + + +def test_success_withdrawable(state): + next_epoch(state) + + transfer = get_valid_transfer(state) + + # withdrawable_epoch in past so can transfer + state.validator_registry[transfer.sender].withdrawable_epoch = get_current_epoch(state) - 1 + + pre_state, post_state = run_transfer_processing(state, transfer) + + return pre_state, transfer, post_state + + +def test_success_active_above_max_effective(state): + sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] + amount = spec.MAX_EFFECTIVE_BALANCE // 32 + set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE + amount) + transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0) + + pre_state, post_state = run_transfer_processing(state, transfer) + + return pre_state, transfer, post_state + + +def test_active_but_transfer_past_effective_balance(state): + sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] + amount = spec.MAX_EFFECTIVE_BALANCE // 32 + set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE) + transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0) + + pre_state, post_state = run_transfer_processing(state, transfer, False) + + return pre_state, transfer, post_state + + +def test_incorrect_slot(state): + transfer = get_valid_transfer(state, slot=state.slot+1) + # un-activate so validator can transfer + state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + + pre_state, post_state = run_transfer_processing(state, transfer, False) + + return pre_state, transfer, post_state + + +def test_insufficient_balance(state): + sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] + amount = spec.MAX_EFFECTIVE_BALANCE + set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE) + transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount + 1, fee=0) + + # un-activate so validator can transfer + state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + + pre_state, post_state = run_transfer_processing(state, transfer, False) + + return pre_state, transfer, post_state + + +def test_no_dust(state): + sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] + balance = state.balances[sender_index] + transfer = get_valid_transfer(state, sender_index=sender_index, amount=balance - spec.MIN_DEPOSIT_AMOUNT + 1, fee=0) + + # un-activate so validator can transfer + state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + + pre_state, post_state = run_transfer_processing(state, transfer, False) + + return pre_state, transfer, post_state + + +def test_invalid_pubkey(state): + transfer = get_valid_transfer(state) + state.validator_registry[transfer.sender].withdrawal_credentials = spec.ZERO_HASH + + # un-activate so validator can transfer + state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + + pre_state, post_state = run_transfer_processing(state, transfer, False) + + return pre_state, transfer, post_state \ No newline at end of file diff --git a/test_libs/pyspec/tests/helpers.py b/test_libs/pyspec/tests/helpers.py index 9ef891219..650790b5a 100644 --- a/test_libs/pyspec/tests/helpers.py +++ b/test_libs/pyspec/tests/helpers.py @@ -21,11 +21,13 @@ from eth2spec.phase0.spec import ( DepositData, Eth1Data, ProposerSlashing, + Transfer, VoluntaryExit, # functions convert_to_indexed, get_active_validator_indices, get_attestation_participants, + get_balance, get_block_root, get_crosslink_committee_for_attestation, get_current_epoch, @@ -291,6 +293,48 @@ def get_valid_attestation(state, slot=None): return attestation +def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None): + if slot is None: + slot = state.slot + current_epoch = get_current_epoch(state) + if sender_index is None: + sender_index = get_active_validator_indices(state, current_epoch)[-1] + recipient_index = get_active_validator_indices(state, current_epoch)[0] + transfer_pubkey = pubkeys[-1] + transfer_privkey = privkeys[-1] + + if fee is None: + fee = get_balance(state, sender_index) // 32 + if amount is None: + amount = get_balance(state, sender_index) - fee + + transfer = Transfer( + sender=sender_index, + recipient=recipient_index, + amount=amount, + fee=fee, + slot=slot, + pubkey=transfer_pubkey, + signature=EMPTY_SIGNATURE, + ) + transfer.signature = bls.sign( + message_hash=signing_root(transfer), + privkey=transfer_privkey, + domain=get_domain( + fork=state.fork, + epoch=get_current_epoch(state), + domain_type=spec.DOMAIN_TRANSFER, + ) + ) + + # ensure withdrawal_credentials reproducable + state.validator_registry[transfer.sender].withdrawal_credentials = ( + spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:] + ) + + return transfer + + def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0): message_hash = AttestationDataAndCustodyBit( data=attestation_data, @@ -311,3 +355,9 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0) def next_slot(state): block = build_empty_block_for_next_slot(state) state_transition(state, block) + + +def next_epoch(state): + block = build_empty_block_for_next_slot(state) + block.slot += spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH) + state_transition(state, block) From 9c14900c772774d72ec03aa79d894d2954ae1cef Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 18 Apr 2019 10:45:22 +1000 Subject: [PATCH 16/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 86a017508..d58a8b316 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2260,8 +2260,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: ##### Transfers -Note: Transfers are a temporary functionality for phases 0 and 1, to be removed in phase 2. - Verify that `len(block.body.transfers) <= MAX_TRANSFERS` and that all transfers are distinct. For each `transfer` in `block.body.transfers`, run the following function: From d9afb67e29bd9558d2e7d2aff1d110b8d057775a Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 18 Apr 2019 17:45:28 +1000 Subject: [PATCH 17/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d58a8b316..bab26ff77 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1366,7 +1366,7 @@ For convenience, we provide the interface to the contract here: * `__init__()`: initializes the contract * `get_deposit_root() -> bytes32`: returns the current root of the deposit tree -* `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be greater than `MIN_DEPOSIT_AMOUNT` inclusive. Each of these constants are specified in units of Gwei. +* `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be at least `MIN_DEPOSIT_AMOUNT`. Each of these constants are specified in units of Gwei. ## On genesis From 40a898f1253960e7d42c21e9163343e5b14570dd Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 18 Apr 2019 17:46:31 +1000 Subject: [PATCH 18/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bab26ff77..6a01cf029 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1344,7 +1344,7 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat ### `Deposit` logs -Every Ethereum 1.0 deposit, of size greater than `MIN_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12 signature) is not verified by the deposit contract. +Every Ethereum 1.0 deposit, of size at least `MIN_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract. ### `Eth2Genesis` log From 72f4e2d3b613dacf6b0ef7e29268906156d1bbba Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 18 Apr 2019 17:51:50 +1000 Subject: [PATCH 19/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6a01cf029..4f4158e42 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2274,10 +2274,10 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert get_balance(state, transfer.sender) >= max(transfer.amount, transfer.fee) # A transfer is valid in only one slot assert state.slot == transfer.slot - # Only withdrawn, not-yet-deposited accounts, or the balance over MAX_EFFECTIVE_BALANCE can be transfered + # Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE assert ( + state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or - state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH or transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(state, transfer.sender) ) # Verify that the pubkey is valid From cb5c95b84e6e2e8970fe3cf6ce68df277eb82b29 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Thu, 18 Apr 2019 12:35:22 +0200 Subject: [PATCH 20/56] Fixes tests --- .../pyspec/tests/block_processing/test_process_transfer.py | 2 +- test_libs/pyspec/tests/test_sanity.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/tests/block_processing/test_process_transfer.py b/test_libs/pyspec/tests/block_processing/test_process_transfer.py index df49aff98..71d0894bd 100644 --- a/test_libs/pyspec/tests/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/tests/block_processing/test_process_transfer.py @@ -52,7 +52,7 @@ def run_transfer_processing(state, transfer, valid=True): def test_success_non_activated(state): transfer = get_valid_transfer(state) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validator_registry[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH pre_state, post_state = run_transfer_processing(state, transfer) diff --git a/test_libs/pyspec/tests/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py index ee4ab405d..2004a2f25 100644 --- a/test_libs/pyspec/tests/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -442,7 +442,7 @@ def test_transfer(state): spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer_pubkey)[1:] ) # un-activate so validator can transfer - pre_state.validator_registry[sender_index].activation_epoch = spec.FAR_FUTURE_EPOCH + pre_state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH post_state = deepcopy(pre_state) # From fad9b4672aa1340ead82c227c7c4d72b46dbc399 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 19 Apr 2019 18:09:29 +1000 Subject: [PATCH 21/56] Disallow transfers As discussed in yesterday's call, temporarily disable transfers until the network is deemed stable enough. We can consider doing a "test-run hard fork" changing this constant prior to the phase 1 hard fork. --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f04a04877..a133fcf42 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -257,7 +257,7 @@ These configurations are updated for releases, but may be out of sync during `de | `MAX_ATTESTATIONS` | `2**7` (= 128) | | `MAX_DEPOSITS` | `2**4` (= 16) | | `MAX_VOLUNTARY_EXITS` | `2**4` (= 16) | -| `MAX_TRANSFERS` | `2**4` (= 16) | +| `MAX_TRANSFERS` | `0` | ### Signature domains From 66cf4e95c149fe156adf46db377a62aa66456283 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Fri, 19 Apr 2019 18:43:26 +0300 Subject: [PATCH 22/56] Added signing_root to ssz_static tests --- test_generators/ssz_static/README.md | 17 +++++++++++++++++ test_generators/ssz_static/main.py | 8 +++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/test_generators/ssz_static/README.md b/test_generators/ssz_static/README.md index 014c71517..01892ecc2 100644 --- a/test_generators/ssz_static/README.md +++ b/test_generators/ssz_static/README.md @@ -2,3 +2,20 @@ The purpose of this test-generator is to provide test-vectors for the most important applications of SSZ: the serialization and hashing of ETH 2.0 data types + +#### Test case +Example: +```yaml +- type_name: DepositData + value: {pubkey: '0x364194dbcda9974ec8e57aa0d556ced515e43ce450e21aa8f9b2099a528679fcf45aed142db60b7f848bd399b63f0933', + withdrawal_credentials: '0xad1256c89ae823b24e1d81fae3d3d382d60012d8399f469ff404e3bbf908027a', + amount: 2672254660871140633, signature: '0x5c3fe3bdbf58d0fb4cdb63a19a67082c697ef910c182dc824c8fb048c935b4b46f522c36047ae36feef84654c1e868f3a0edd76852c09e35414782160767439b49aceaa4219cc25016effcc82a9e17b336efee40ab37e3a47fc31da557027491'} + serialized: '0x364194dbcda9974ec8e57aa0d556ced515e43ce450e21aa8f9b2099a528679fcf45aed142db60b7f848bd399b63f0933ad1256c89ae823b24e1d81fae3d3d382d60012d8399f469ff404e3bbf908027a19359bb274c115255c3fe3bdbf58d0fb4cdb63a19a67082c697ef910c182dc824c8fb048c935b4b46f522c36047ae36feef84654c1e868f3a0edd76852c09e35414782160767439b49aceaa4219cc25016effcc82a9e17b336efee40ab37e3a47fc31da557027491' + root: '0x2eaae270579fc1a1eabde69c841221cb3dfab9de7ad99fcfbee8fe0c198878b7' + signing_root: '0x844655facb151b633410ffc698d8467c6488ae87f2d5f739d39c9bfc18750524' +``` +**type_name** - Name of valid Eth2.0 type from the spec +**value** - Field values used to create type instance +**serialized** - [SSZ serialization](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#serialization) of the value +**root** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) of the value +**signing_root** - (Optional) [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) of the value, if type contains ``signature`` field \ No newline at end of file diff --git a/test_generators/ssz_static/main.py b/test_generators/ssz_static/main.py index 010ca2735..1234294db 100644 --- a/test_generators/ssz_static/main.py +++ b/test_generators/ssz_static/main.py @@ -2,7 +2,11 @@ from random import Random from eth2spec.debug import random_value, encode from eth2spec.phase0 import spec -from eth2spec.utils.minimal_ssz import hash_tree_root, serialize +from eth2spec.utils.minimal_ssz import ( + hash_tree_root, + signing_root, + serialize, +) from eth_utils import ( to_tuple, to_dict ) @@ -21,6 +25,8 @@ def create_test_case(rng: Random, name: str, mode: random_value.RandomizationMod yield "value", encode.encode(value, typ) yield "serialized", '0x' + serialize(value).hex() yield "root", '0x' + hash_tree_root(value).hex() + if hasattr(value, "signature"): + yield "signing_root", '0x' + signing_root(value).hex() @to_tuple From 8f9133c8c3b8d3e8665f0a3b93b1454ed7849d97 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 20 Apr 2019 11:33:15 +1000 Subject: [PATCH 23/56] update CI config: caching of repo and venv, and split install from tests run --- .circleci/config.yml | 168 +++++++++++++++++++------------------ Makefile | 16 ++-- test_libs/pyspec/README.md | 5 +- 3 files changed, 103 insertions(+), 86 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5be6ed500..9a7172866 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,89 +1,97 @@ version: 2.1 +commands: + restore_cached_venv: + description: "Restores a cached venv" + parameters: + reqs_checksum: + type: string + default: "1234" + venv_name: + type: string + default: "default-name" + steps: + - restore_cache: + keys: + - << parameters.venv_name >>-venv-<< parameters.reqs_checksum >> + # fallback to using the latest cache if no exact match is found + - << parameters.venv_name >>-venv- + save_cached_venv: + description: "Saves a venv into a cache" + parameters: + reqs_checksum: + type: string + default: "1234" + venv_path: + type: string + default: "venv" + venv_name: + type: string + default: "default-name" + steps: + - save_cache: + key: << parameters.venv_name >>-venv-<< parameters.reqs_checksum >> + paths: << parameters.venv_path >> jobs: - build: + checkout_specs: docker: - image: circleci/python:3.6 - working_directory: ~/repo - + working_directory: ~/specs-repo steps: + # Restore git repo at point close to target branch/revision, to speed up checkout + - restore_cache: + keys: + - v1-specs-repo-{{ .Branch }}-{{ .Revision }} + - v1-specs-repo-{{ .Branch }}- + - v1-specs-repo- - checkout - run: - name: Build pyspec - command: make pyspec - + name: Clean up git repo to reduce cache size + command: git gc + # Save the git checkout as a cache, to make cloning next time faster. + - save_cache: + key: v1-specs-repo-{{ .Branch }}-{{ .Revision }} + paths: + - ~/specs-repo + install_test: + docker: + - image: circleci/python:3.6 + working_directory: ~/specs-repo + steps: + - restore_cache: + key: v1-specs-repo-{{ .Branch }}-{{ .Revision }} + - restore_cached_venv: + venv_name: v1-pyspec + reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}' + - run: + name: Install pyspec requirements + command: make install_test + - save_cached_venv: + venv_name: v1-pyspec + reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}' + venv_path: ./test_libs/pyspec/venv + test: + docker: + - image: circleci/python:3.6 + working_directory: ~/specs-repo + steps: + - restore_cache: + key: v1-specs-repo-{{ .Branch }}-{{ .Revision }} + - restore_cached_venv: + venv_name: v1-pyspec + reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}' - run: name: Run py-tests - command: make test - -# TODO see #928: decide on CI triggering of yaml tests building, -# and destination of output (new yaml tests LFS-configured repository) -# -# - run: -# name: Generate YAML tests -# command: make gen_yaml_tests -# -# - store_artifacts: -# path: test-reports -# destination: test-reports -# -# - run: -# name: Save YAML tests for deployment -# command: | -# mkdir /tmp/workspace -# cp -r yaml_tests /tmp/workspace/ -# git log -1 >> /tmp/workspace/latest_commit_message -# - persist_to_workspace: -# root: /tmp/workspace -# paths: -# - yaml_tests -# - latest_commit_message -# commit: -# docker: -# - image: circleci/python:3.6 -# steps: -# - attach_workspace: -# at: /tmp/workspace -# - add_ssh_keys: -# fingerprints: -# - "01:85:b6:36:96:a6:84:72:e4:9b:4e:38:ee:21:97:fa" -# - run: -# name: Checkout test repository -# command: | -# ssh-keyscan -H github.com >> ~/.ssh/known_hosts -# git clone git@github.com:ethereum/eth2.0-tests.git -# - run: -# name: Commit and push generated YAML tests -# command: | -# cd eth2.0-tests -# git config user.name 'eth2TestGenBot' -# git config user.email '47188154+eth2TestGenBot@users.noreply.github.com' -# for filename in /tmp/workspace/yaml_tests/*; do -# rm -rf $(basename $filename) -# cp -r $filename . -# done -# git add . -# if git diff --cached --exit-code >& /dev/null; then -# echo "No changes to commit" -# else -# echo -e "Update generated tests\n\nLatest commit message from eth2.0-specs:\n" > commit_message -# cat /tmp/workspace/latest_commit_message >> commit_message -# git commit -F commit_message -# git push origin master -# fi -#workflows: -# version: 2.1 -# -# build_and_commit: -# jobs: -# - build: -# filters: -# tags: -# only: /.*/ -# - commit: -# requires: -# - build -# filters: -# tags: -# only: /.*/ -# branches: -# ignore: /.*/ \ No newline at end of file + command: make citest + - store_test_results: + path: test_libs/pyspec/test-reports +workflows: + version: 2.1 + test_spec: + jobs: + - checkout_specs + - install_test: + requires: + - checkout_specs + - test: + requires: + - install_test diff --git a/Makefile b/Makefile index b39538791..71d150983 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ PY_SPEC_PHASE_0_TARGETS = $(PY_SPEC_DIR)/eth2spec/phase0/spec.py PY_SPEC_ALL_TARGETS = $(PY_SPEC_PHASE_0_TARGETS) -.PHONY: clean all test gen_yaml_tests pyspec phase0 +.PHONY: clean all test citest gen_yaml_tests pyspec phase0 install_test all: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) @@ -27,11 +27,17 @@ clean: rm -rf $(PY_SPEC_ALL_TARGETS) # "make gen_yaml_tests" to run generators -gen_yaml_tests: $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) +gen_yaml_tests: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) + +# installs the packages to run pyspec tests +install_test: + cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; -# runs a limited set of tests against a minimal config test: $(PY_SPEC_ALL_TARGETS) - cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; python -m pytest -m minimal_config . + cd $(PY_SPEC_DIR); . venv/bin/activate; python -m pytest -m minimal_config . + +citest: $(PY_SPEC_ALL_TARGETS) + cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; python -m pytest --junitxml=test-reports/eth2spec/test_results.xml -m minimal_config . # "make pyspec" to create the pyspec for all phases. pyspec: $(PY_SPEC_ALL_TARGETS) @@ -69,5 +75,5 @@ $(YAML_TEST_DIR): # For any target within the tests dir, build it using the build_yaml_tests function. # (creation of output dir is a dependency) -$(YAML_TEST_DIR)%: $(YAML_TEST_DIR) +$(YAML_TEST_DIR)%: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(call build_yaml_tests,$*) diff --git a/test_libs/pyspec/README.md b/test_libs/pyspec/README.md index b3cab11d2..20c01bde4 100644 --- a/test_libs/pyspec/README.md +++ b/test_libs/pyspec/README.md @@ -19,6 +19,8 @@ Or, to build a single file, specify the path, e.g. `make test_libs/pyspec/eth2sp ## Py-tests +After building, you can install the dependencies for running the `pyspec` tests with `make install_test` + These tests are not intended for client-consumption. These tests are sanity tests, to verify if the spec itself is consistent. @@ -38,8 +40,9 @@ python3 -m venv venv . venv/bin/activate pip3 install -r requirements.txt ``` -Note: make sure to run `make pyspec` from the root of the specs repository, +Note: make sure to run `make -B pyspec` from the root of the specs repository, to build the parts of the pyspec module derived from the markdown specs. +The `-B` flag may be helpful to force-overwrite the `pyspec` output after you made a change to the markdown source files. Run the tests: ``` From 2b171b19c410d4242b8d750708e45111c1935d09 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 20 Apr 2019 12:18:56 +1000 Subject: [PATCH 24/56] fix generator --- test_generators/operations/deposits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_generators/operations/deposits.py b/test_generators/operations/deposits.py index 454c6f22d..bd9c392a5 100644 --- a/test_generators/operations/deposits.py +++ b/test_generators/operations/deposits.py @@ -29,7 +29,7 @@ def build_deposit_data(state, message_hash=signing_root(deposit_data), privkey=privkey, domain=spec.get_domain( - state.fork, + state, spec.get_current_epoch(state), spec.DOMAIN_DEPOSIT, ) From 55aa12d7bd5bb4a9d8324b4f26fd3c9d3df8b173 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 20 Apr 2019 12:23:10 +1000 Subject: [PATCH 25/56] parallelism support for make gen_yaml_tests --- Makefile | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index b39538791..0a3e03e33 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ clean: rm -rf $(PY_SPEC_ALL_TARGETS) # "make gen_yaml_tests" to run generators -gen_yaml_tests: $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) +gen_yaml_tests: $(YAML_TEST_TARGETS) # runs a limited set of tests against a minimal config test: $(PY_SPEC_ALL_TARGETS) @@ -48,24 +48,30 @@ CURRENT_DIR = ${CURDIR} # The function that builds a set of suite files, by calling a generator for the given type (param 1) define build_yaml_tests - $(info running generator $(1)) - # Create the output - mkdir -p $(YAML_TEST_DIR)$(1) - - # 1) Create a virtual environment - # 2) Activate the venv, this is where dependencies are installed for the generator - # 3) Install all the necessary requirements - # 4) Run the generator. The generator is assumed to have an "main.py" file. - # 5) We output to the tests dir (generator program should accept a "-o " argument. - cd $(GENERATOR_DIR)$(1); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; python3 main.py -o $(CURRENT_DIR)/$(YAML_TEST_DIR)$(1) -c $(CURRENT_DIR)/$(CONFIGS_DIR) - - $(info generator $(1) finished) + # Started! + # Create output directory + # Navigate to the generator + # Create a virtual environment, if it does not exist already + # Activate the venv, this is where dependencies are installed for the generator + # Install all the necessary requirements + # Run the generator. The generator is assumed to have an "main.py" file. + # We output to the tests dir (generator program should accept a "-o " argument. + echo "generator $(1) started"; \ + mkdir -p $(YAML_TEST_DIR)$(1); \ + cd $(GENERATOR_DIR)$(1); \ + if test -d venv; then python3 -m venv venv; fi; \ + . venv/bin/activate; \ + pip3 install -r requirements.txt; \ + python3 main.py -o $(CURRENT_DIR)/$(YAML_TEST_DIR)$(1) -c $(CURRENT_DIR)/$(CONFIGS_DIR); \ + echo "generator $(1) finished" endef # The tests dir itself is simply build by creating the directory (recursively creating deeper directories if necessary) $(YAML_TEST_DIR): $(info creating directory, to output yaml targets to: ${YAML_TEST_TARGETS}) mkdir -p $@ +$(YAML_TEST_DIR)/: + $(info ignoring duplicate yaml tests dir) # For any target within the tests dir, build it using the build_yaml_tests function. # (creation of output dir is a dependency) From 69ab4140decb7f98a1143dcd4582a2b872de18c9 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 20 Apr 2019 12:25:24 +1000 Subject: [PATCH 26/56] Add note on parallelism --- test_generators/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test_generators/README.md b/test_generators/README.md index 743157aae..66534e5a8 100644 --- a/test_generators/README.md +++ b/test_generators/README.md @@ -28,9 +28,12 @@ make clean This runs all the generators. ```bash -make gen_yaml_tests +make -j 4 gen_yaml_tests ``` +The `-j N` flag makes the generators run in parallel, with `N` being the amount of cores. + + ### Running a single generator The make file auto-detects generators in the `test_generators/` directory, From 14ff452314d566a595ccbdaf2f4cca9fc161c69e Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 20 Apr 2019 12:28:50 +1000 Subject: [PATCH 27/56] move yaml output target --- .gitignore | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ce047240a..3dd86fc80 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ venv build/ output/ -yaml_tests/ +eth2.0-spec-tests/ .pytest_cache # Dynamically built from Markdown spec diff --git a/Makefile b/Makefile index 0a3e03e33..b93c7ea95 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ SPEC_DIR = ./specs SCRIPT_DIR = ./scripts TEST_LIBS_DIR = ./test_libs PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec -YAML_TEST_DIR = ./yaml_tests +YAML_TEST_DIR = ./eth2.0-spec-tests/tests GENERATOR_DIR = ./test_generators CONFIGS_DIR = ./configs From 1a95996035bf59a91678a3c23c683370a1d3e72f Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Sat, 20 Apr 2019 01:01:06 -0500 Subject: [PATCH 28/56] i.e. + e.g. standardization (#970) --- specs/bls_signature.md | 2 +- specs/light_client/merkle_proofs.md | 2 +- specs/light_client/sync_protocol.md | 2 +- specs/validator/0_beacon-chain-validator.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/bls_signature.md b/specs/bls_signature.md index 14a4f1cb7..beef19df5 100644 --- a/specs/bls_signature.md +++ b/specs/bls_signature.md @@ -88,7 +88,7 @@ def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]: `modular_squareroot(x)` returns a solution `y` to `y**2 % q == x`, and `None` if none exists. If there are two solutions the one with higher imaginary component is favored; if both solutions have equal imaginary component the one with higher real component is favored (note that this is equivalent to saying that the single solution with either imaginary component > p/2 or imaginary component zero and real component > p/2 is favored). -The following is a sample implementation; implementers are free to implement modular square roots as they wish. Note that `x2 = -x1` is an _additive modular inverse_ so real and imaginary coefficients remain in `[0 .. q-1]`. `coerce_to_int(element: Fq) -> int` is a function that takes Fq element `element` (ie. integers `mod q`) and converts it to a regular integer. +The following is a sample implementation; implementers are free to implement modular square roots as they wish. Note that `x2 = -x1` is an _additive modular inverse_ so real and imaginary coefficients remain in `[0 .. q-1]`. `coerce_to_int(element: Fq) -> int` is a function that takes Fq element `element` (i.e. integers `mod q`) and converts it to a regular integer. ```python Fq2_order = q ** 2 - 1 diff --git a/specs/light_client/merkle_proofs.md b/specs/light_client/merkle_proofs.md index 63c018f2f..b38167bb5 100644 --- a/specs/light_client/merkle_proofs.md +++ b/specs/light_client/merkle_proofs.md @@ -102,7 +102,7 @@ def get_generalized_indices(obj: Any, path: List[int], root: int=1) -> List[int] ## Merkle multiproofs -We define a Merkle multiproof as a minimal subset of nodes in a Merkle tree needed to fully authenticate that a set of nodes actually are part of a Merkle tree with some specified root, at a particular set of generalized indices. For example, here is the Merkle multiproof for positions 0, 1, 6 in an 8-node Merkle tree (ie. generalized indices 8, 9, 14): +We define a Merkle multiproof as a minimal subset of nodes in a Merkle tree needed to fully authenticate that a set of nodes actually are part of a Merkle tree with some specified root, at a particular set of generalized indices. For example, here is the Merkle multiproof for positions 0, 1, 6 in an 8-node Merkle tree (i.e. generalized indices 8, 9, 14): ``` . diff --git a/specs/light_client/sync_protocol.md b/specs/light_client/sync_protocol.md index 900b2e64f..257590f4d 100644 --- a/specs/light_client/sync_protocol.md +++ b/specs/light_client/sync_protocol.md @@ -27,7 +27,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers ### Expansions -We define an "expansion" of an object as an object where a field in an object that is meant to represent the `hash_tree_root` of another object is replaced by the object. Note that defining expansions is not a consensus-layer-change; it is merely a "re-interpretation" of the object. Particularly, the `hash_tree_root` of an expansion of an object is identical to that of the original object, and we can define expansions where, given a complete history, it is always possible to compute the expansion of any object in the history. The opposite of an expansion is a "summary" (eg. `BeaconBlockHeader` is a summary of `BeaconBlock`). +We define an "expansion" of an object as an object where a field in an object that is meant to represent the `hash_tree_root` of another object is replaced by the object. Note that defining expansions is not a consensus-layer-change; it is merely a "re-interpretation" of the object. Particularly, the `hash_tree_root` of an expansion of an object is identical to that of the original object, and we can define expansions where, given a complete history, it is always possible to compute the expansion of any object in the history. The opposite of an expansion is a "summary" (e.g. `BeaconBlockHeader` is a summary of `BeaconBlock`). We define two expansions: diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 632bf2b62..cb19097dd 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -60,7 +60,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers ## Introduction -This document represents the expected behavior of an "honest validator" with respect to Phase 0 of the Ethereum 2.0 protocol. This document does not distinguish between a "node" (ie. the functionality of following and reading the beacon chain) and a "validator client" (ie. the functionality of actively participating in consensus). The separation of concerns between these (potentially) two pieces of software is left as a design decision that is out of scope. +This document represents the expected behavior of an "honest validator" with respect to Phase 0 of the Ethereum 2.0 protocol. This document does not distinguish between a "node" (i.e. the functionality of following and reading the beacon chain) and a "validator client" (i.e. the functionality of actively participating in consensus). The separation of concerns between these (potentially) two pieces of software is left as a design decision that is out of scope. A validator is an entity that participates in the consensus of the Ethereum 2.0 protocol. This is an optional role for users in which they can post ETH as collateral and verify and attest to the validity of blocks to seek financial returns in exchange for building and securing the protocol. This is similar to proof of work networks in which a miner provides collateral in the form of hardware/hash-power to seek returns in exchange for building and securing the protocol. @@ -141,7 +141,7 @@ A validator has two primary responsibilities to the beacon chain -- [proposing b A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function). -There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks). +There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312500 validators = 10 million ETH, that's once per ~3 weeks). #### Block header From a2a737b7289ea4bcbd9f77ca723cb197f1387828 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 20 Apr 2019 01:45:18 -0500 Subject: [PATCH 29/56] Signal non-final status of base reward and desired issuance goal --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 624413879..c0f03e4ed 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -241,7 +241,7 @@ These configurations are updated for releases, but may be out of sync during `de | `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) | | `MIN_PENALTY_QUOTIENT` | `2**5` (= 32) | -* The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. +* **The `BASE_REWARD_QUOTIENT` is NOT final. Once all other protocol details are finalized it will be adjusted, to target a theoretical maximum total issuance of `2**21` ETH per year if `2**27` ETH is validating (and therefore `2**20` per year if `2**25` ETH is validating, etc etc)** * The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. ### Max operations per block From 75fae6f311f27ce0977f6cb361ec097c92d9b817 Mon Sep 17 00:00:00 2001 From: Diederik Loerakker Date: Sat, 20 Apr 2019 18:13:45 +1000 Subject: [PATCH 30/56] Change sorted[-1] to max() (#972) --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 624413879..9da1ba25c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1236,7 +1236,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH] - exit_queue_epoch = sorted(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))])[-1] + exit_queue_epoch = max(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += 1 From 08d921a6c9338ac901a53da1cf7b59da8b7f2990 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sat, 20 Apr 2019 22:48:02 -0700 Subject: [PATCH 31/56] Make crosslink_data_root comment more explicit (#973) --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 9da1ba25c..0ae4c1160 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -295,7 +295,7 @@ The types are defined topologically to aid in facilitating an executable version 'epoch': 'uint64', # Root of the previous crosslink 'previous_crosslink_root': 'bytes32', - # Shard data since the previous crosslink + # Root of the crosslinked shard data since the previous crosslink 'crosslink_data_root': 'bytes32', } ``` From 04d498695e680e2c4c7a8b575bdd73e4f0265f85 Mon Sep 17 00:00:00 2001 From: protolambda Date: Mon, 22 Apr 2019 14:01:04 +1000 Subject: [PATCH 32/56] update test format docs --- specs/test_formats/ssz_static/core.md | 9 +++++++++ test_generators/ssz_static/README.md | 17 +---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/specs/test_formats/ssz_static/core.md b/specs/test_formats/ssz_static/core.md index 8a5067f03..ee712a830 100644 --- a/specs/test_formats/ssz_static/core.md +++ b/specs/test_formats/ssz_static/core.md @@ -13,6 +13,7 @@ type_name: string -- string, object name, formatted as in spec. E.g. "BeaconBlo value: dynamic -- the YAML-encoded value, of the type specified by type_name. serialized: bytes -- string, SSZ-serialized data, hex encoded, with prefix 0x root: bytes32 -- string, hash-tree-root of the value, hex encoded, with prefix 0x +signing_root: bytes32 -- string, signing-root of the value, hex encoded, with prefix 0x. Optional, present if type contains ``signature`` field ``` ## Condition @@ -20,4 +21,12 @@ root: bytes32 -- string, hash-tree-root of the value, hex encoded, with pre A test-runner can implement the following assertions: - Serialization: After parsing the `value`, SSZ-serialize it: the output should match `serialized` - Hash-tree-root: After parsing the `value`, Hash-tree-root it: the output should match `root` + - Optionally also check signing-root, if present. - Deserialization: SSZ-deserialize the `serialized` value, and see if it matches the parsed `value` + +## References + + +**`serialized`**: [SSZ serialization](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#serialization) +**`root`** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) +**`signing_root`** - [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) diff --git a/test_generators/ssz_static/README.md b/test_generators/ssz_static/README.md index 01892ecc2..d73556e1b 100644 --- a/test_generators/ssz_static/README.md +++ b/test_generators/ssz_static/README.md @@ -3,19 +3,4 @@ The purpose of this test-generator is to provide test-vectors for the most important applications of SSZ: the serialization and hashing of ETH 2.0 data types -#### Test case -Example: -```yaml -- type_name: DepositData - value: {pubkey: '0x364194dbcda9974ec8e57aa0d556ced515e43ce450e21aa8f9b2099a528679fcf45aed142db60b7f848bd399b63f0933', - withdrawal_credentials: '0xad1256c89ae823b24e1d81fae3d3d382d60012d8399f469ff404e3bbf908027a', - amount: 2672254660871140633, signature: '0x5c3fe3bdbf58d0fb4cdb63a19a67082c697ef910c182dc824c8fb048c935b4b46f522c36047ae36feef84654c1e868f3a0edd76852c09e35414782160767439b49aceaa4219cc25016effcc82a9e17b336efee40ab37e3a47fc31da557027491'} - serialized: '0x364194dbcda9974ec8e57aa0d556ced515e43ce450e21aa8f9b2099a528679fcf45aed142db60b7f848bd399b63f0933ad1256c89ae823b24e1d81fae3d3d382d60012d8399f469ff404e3bbf908027a19359bb274c115255c3fe3bdbf58d0fb4cdb63a19a67082c697ef910c182dc824c8fb048c935b4b46f522c36047ae36feef84654c1e868f3a0edd76852c09e35414782160767439b49aceaa4219cc25016effcc82a9e17b336efee40ab37e3a47fc31da557027491' - root: '0x2eaae270579fc1a1eabde69c841221cb3dfab9de7ad99fcfbee8fe0c198878b7' - signing_root: '0x844655facb151b633410ffc698d8467c6488ae87f2d5f739d39c9bfc18750524' -``` -**type_name** - Name of valid Eth2.0 type from the spec -**value** - Field values used to create type instance -**serialized** - [SSZ serialization](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#serialization) of the value -**root** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) of the value -**signing_root** - (Optional) [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) of the value, if type contains ``signature`` field \ No newline at end of file +Test-format documentation can be found [here](../../specs/test_formats/ssz_static/README.md). From 0da60ba90d09a60ea926ccd046b3edf267fc05f3 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 22 Apr 2019 15:12:30 +1000 Subject: [PATCH 33/56] Fix activation queue bug Fix bug [flagged by @NIC619 and @hwwhww](https://github.com/ethereum/eth2.0-specs/pull/850#issuecomment-485275575) whereby the `activation_epoch` of validators dequeued since the finalized epoch was overwritten. Cosmetic changes: 1) Remove `activate_validator` (there is no overlap between genesis and non-genesis activations) 2) Improve comments related to activation queue --- specs/core/0_beacon-chain.md | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0ae4c1160..ea2225ffd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -94,7 +94,6 @@ - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - [Routines for updating validator status](#routines-for-updating-validator-status) - - [`activate_validator`](#activate_validator) - [`initiate_validator_exit`](#initiate_validator_exit) - [`slash_validator`](#slash_validator) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) @@ -1205,22 +1204,6 @@ def get_churn_limit(state: BeaconState) -> int: Note: All functions in this section mutate `state`. -#### `activate_validator` - -```python -def activate_validator(state: BeaconState, index: ValidatorIndex) -> None: - """ - Activate the validator of the given ``index``. - Note that this function mutates ``state``. - """ - validator = state.validator_registry[index] - if state.slot == GENESIS_SLOT: - validator.activation_eligibility_epoch = GENESIS_EPOCH - validator.activation_epoch = GENESIS_EPOCH - else: - validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) -``` - #### `initiate_validator_exit` ```python @@ -1340,9 +1323,10 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], process_deposit(state, deposit) # Process genesis activations - for index in range(len(state.validator_registry)): + for index, validator in enumerate(state.validator_registry): if get_effective_balance(state, index) >= MAX_DEPOSIT_AMOUNT: - activate_validator(state, index) + validator.activation_eligibility_epoch = GENESIS_EPOCH + validator.activation_epoch = GENESIS_EPOCH genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): @@ -1745,14 +1729,16 @@ def process_registry_updates(state: BeaconState) -> None: if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE: initiate_validator_exit(state, index) - # Process activations + # Queue validators are eligible for activation and not dequeued prior to finalized epoch activation_queue = sorted([ index for index, validator in enumerate(state.validator_registry) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch) ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) + # Dequeued validators for activation up to churn limit (without resetting activation epoch) for index in activation_queue[:get_churn_limit(state)]: - activate_validator(state, index) + if validator.activation_epoch != FAR_FUTURE_EPOCH: + validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### Slashings From dc275f024d04265deaad09fc28c1e7289db43573 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 22 Apr 2019 15:16:34 +1000 Subject: [PATCH 34/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ea2225ffd..d04f12a6d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1729,7 +1729,7 @@ def process_registry_updates(state: BeaconState) -> None: if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE: initiate_validator_exit(state, index) - # Queue validators are eligible for activation and not dequeued prior to finalized epoch + # Queue validators eligible for activation and not dequeued for activation prior to finalized epoch activation_queue = sorted([ index for index, validator in enumerate(state.validator_registry) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and From 92e4bba7df4f4cc67911253e1822bbac90562c96 Mon Sep 17 00:00:00 2001 From: Diederik Loerakker Date: Mon, 22 Apr 2019 16:38:44 +1000 Subject: [PATCH 35/56] small constants update to reflect new genesis slot, and rename block sig domain (#978) --- configs/constant_presets/mainnet.yaml | 6 +++--- configs/constant_presets/minimal.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/constant_presets/mainnet.yaml b/configs/constant_presets/mainnet.yaml index d06febb77..8b9dade73 100644 --- a/configs/constant_presets/mainnet.yaml +++ b/configs/constant_presets/mainnet.yaml @@ -42,8 +42,8 @@ HIGH_BALANCE_INCREMENT: 1000000000 # Initial values # --------------------------------------------------------------- GENESIS_FORK_VERSION: 0x00000000 -# 2**32, GENESIS_EPOCH is derived from this constant -GENESIS_SLOT: 4294967296 +# 0, GENESIS_EPOCH is derived from this constant +GENESIS_SLOT: 0 GENESIS_START_SHARD: 0 # 2**64 - 1 FAR_FUTURE_EPOCH: 18446744073709551615 @@ -116,7 +116,7 @@ MAX_TRANSFERS: 16 # Signature domains # --------------------------------------------------------------- -DOMAIN_BEACON_BLOCK: 0 +DOMAIN_BEACON_PROPOSER: 0 DOMAIN_RANDAO: 1 DOMAIN_ATTESTATION: 2 DOMAIN_DEPOSIT: 3 diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index 80af5398c..edc447c45 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -42,8 +42,8 @@ HIGH_BALANCE_INCREMENT: 1000000000 # Initial values # --------------------------------------------------------------- GENESIS_FORK_VERSION: 0x00000000 -# 2**32, GENESIS_EPOCH is derived from this constant -GENESIS_SLOT: 4294967296 +# 0, GENESIS_EPOCH is derived from this constant +GENESIS_SLOT: 0 GENESIS_START_SHARD: 0 # 2**64 - 1 FAR_FUTURE_EPOCH: 18446744073709551615 @@ -116,7 +116,7 @@ MAX_TRANSFERS: 16 # Signature domains # --------------------------------------------------------------- -DOMAIN_BEACON_BLOCK: 0 +DOMAIN_BEACON_PROPOSER: 0 DOMAIN_RANDAO: 1 DOMAIN_ATTESTATION: 2 DOMAIN_DEPOSIT: 3 From 7043bb90800b2be43781e36b6f87472bf0e38eba Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Mon, 22 Apr 2019 12:09:56 +0300 Subject: [PATCH 36/56] test: clean up of ssz_static references styling --- specs/test_formats/ssz_static/core.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/test_formats/ssz_static/core.md b/specs/test_formats/ssz_static/core.md index ee712a830..059f11027 100644 --- a/specs/test_formats/ssz_static/core.md +++ b/specs/test_formats/ssz_static/core.md @@ -28,5 +28,5 @@ A test-runner can implement the following assertions: **`serialized`**: [SSZ serialization](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#serialization) -**`root`** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) -**`signing_root`** - [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) +**`root`** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) function +**`signing_root`** - [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) function From 1c5cc1299a7c94e8f9dc123ef103c4d84c8971ff Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 22 Apr 2019 20:49:07 +1000 Subject: [PATCH 37/56] Update specs/core/0_beacon-chain.md Co-Authored-By: JustinDrake --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d04f12a6d..b2796a588 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1737,7 +1737,7 @@ def process_registry_updates(state: BeaconState) -> None: ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) # Dequeued validators for activation up to churn limit (without resetting activation epoch) for index in activation_queue[:get_churn_limit(state)]: - if validator.activation_epoch != FAR_FUTURE_EPOCH: + if validator.activation_epoch == FAR_FUTURE_EPOCH: validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` From 5744fef808e5668ad616ca64685d1c85a5e598b3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 22 Apr 2019 09:18:20 -0600 Subject: [PATCH 38/56] clean up some notes on deposits --- specs/core/0_beacon-chain.md | 2 +- specs/validator/0_beacon-chain-validator.md | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 20d7f5d43..615c66048 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1345,7 +1345,7 @@ For convenience, we provide the interface to the contract here: * `__init__()`: initializes the contract * `get_deposit_root() -> bytes32`: returns the current root of the deposit tree -* `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be within `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, inclusive. Each of these constants are specified in units of Gwei. +* `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])`: adds a deposit instance to the deposit tree, incorporating the input arguments and the value transferred in the given call. Note: the amount of value transferred *must* be within `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, inclusive. Each of these constants are specified in units of Gwei. ## On genesis diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 632bf2b62..966e238ab 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -101,11 +101,10 @@ In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW To submit a deposit: * Pack the validator's [initialization parameters](#initialization) into `deposit_data`, a [`DepositData`](../core/0_beacon-chain.md#depositdata) SSZ object. -* Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`. -* Set `deposit_data.proof_of_possession = proof_of_possession`. * Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`. * Set `deposit_data.amount = amount`. -* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit(deposit_input: bytes[512])` along with `serialize(deposit_data)` as the singular `bytes` input along with a deposit of `amount` Gwei. +* Let `signature` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`. +* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])` along with a deposit of `amount` Gwei. _Note_: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_DEPOSIT_AMOUNT`. From d648b091b5bd35fce9653ad0e2167edeeb81d45a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 22 Apr 2019 09:33:46 -0600 Subject: [PATCH 39/56] lint --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 615c66048..0b2ccf028 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1345,7 +1345,7 @@ For convenience, we provide the interface to the contract here: * `__init__()`: initializes the contract * `get_deposit_root() -> bytes32`: returns the current root of the deposit tree -* `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])`: adds a deposit instance to the deposit tree, incorporating the input arguments and the value transferred in the given call. Note: the amount of value transferred *must* be within `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, inclusive. Each of these constants are specified in units of Gwei. +* `deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])`: adds a deposit instance to the deposit tree, incorporating the input arguments and the value transferred in the given call. Note: the amount of value transferred *must* be within `MIN_DEPOSIT_AMOUNT` and `MAX_DEPOSIT_AMOUNT`, inclusive. Each of these constants are specified in units of Gwei. ## On genesis From e13cec146661f8f8a47e2de03efb95ecceaeb01f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 22 Apr 2019 10:02:31 -0600 Subject: [PATCH 40/56] increase MAX_TRANSFERS for transfer test --- test_libs/pyspec/tests/test_sanity.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test_libs/pyspec/tests/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py index 3442e8182..3201ba936 100644 --- a/test_libs/pyspec/tests/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -380,7 +380,10 @@ def test_voluntary_exit(state): return pre_state, [initiate_exit_block, exit_block], post_state -def test_transfer(state): +def test_transfer(state, config): + # overwrite default 0 to test + spec.MAX_TRANSFERS = 1 + pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) sender_index = get_active_validator_indices(pre_state, current_epoch)[-1] From d4a33dbcaa093588c9cca1479bb1487ccd630c63 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 22 Apr 2019 15:29:47 -0600 Subject: [PATCH 41/56] add descriptions of typeof and default functions --- specs/core/1_custody-game.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 88341ae98..c8625af5b 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -28,6 +28,8 @@ - [`BeaconState`](#beaconstate) - [`BeaconBlockBody`](#beaconblockbody) - [Helpers](#helpers) + - [`typeof`](#typeof) + - [`empty`](#empty) - [`get_crosslink_chunk_count`](#get_crosslink_chunk_count) - [`get_custody_chunk_bit`](#get_custody_chunk_bit) - [`epoch_to_custody_period`](#epoch_to_custody_period) @@ -204,6 +206,14 @@ Add the following fields to the end of the specified container objects. Fields w ## Helpers +### `typeof` + +The `typeof` function accepts and SSZ object as a single input and returns the corresponding SSZ type. + +### `empty` + +The `empty` function accepts and SSZ type as input and returns an object of that type with all fields initialized to default values. + ### `get_crosslink_chunk_count` ```python From 2650a2c0616661f8a5bc2e018df5ac34cd2d3027 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 23 Apr 2019 07:16:52 -0700 Subject: [PATCH 42/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6d4f396ae..ccab159f4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -797,7 +797,7 @@ def get_split_offset(list_size: int, chunks: int, index: int) -> int: ```python def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: """ - Return the number of committees in one epoch. + Return the number of committees at ``epoch``. """ active_validators = get_active_validator_indices(state, epoch) return max( @@ -813,6 +813,9 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: ```python def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: + """ + Return the minimum number of shards that get processed at ``epoch``. + """ return min(get_epoch_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) ``` From 5619e7df9c04cfa74bdcd60cb5a43fb8215a5d64 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 23 Apr 2019 09:21:30 -0600 Subject: [PATCH 43/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ccab159f4..ed2b1403a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -814,7 +814,7 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: ```python def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: """ - Return the minimum number of shards that get processed at ``epoch``. + Return the number of shards to increment ``state.shard_shard`` during ``epoch``. """ return min(get_epoch_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) ``` From e26112af37efba0c837a986d06c3a35afb3a54ed Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 23 Apr 2019 08:36:40 -0700 Subject: [PATCH 44/56] Update 0_beacon-chain.md typo fix --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ed2b1403a..184411925 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -814,7 +814,7 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: ```python def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: """ - Return the number of shards to increment ``state.shard_shard`` during ``epoch``. + Return the number of shards to increment ``state.latest_start_shard`` during ``epoch``. """ return min(get_epoch_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) ``` From 5e4afc2dd0cb34af647bad88620395c04e5bbaab Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:49:59 -0500 Subject: [PATCH 45/56] Update rpc-interface.md --- specs/networking/rpc-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index 5d408b5a0..f1da8f7e3 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -247,7 +247,7 @@ Requests a list of block roots and slots from the peer. The `count` parameter MU Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `1` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is empty for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were empty in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further empty, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. A `skip_slots` value of `0` returns all blocks. -The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Clients could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data. +The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Clients could, for instance, request every 10th block from a set of peers where each peer has a different starting block in order to populate block data. ### Beacon Block Bodies @@ -287,6 +287,6 @@ Requests the `block_bodies` associated with the provided `block_roots` from the **Response Body:** TBD -Requests contain the hashes of Merkle tree nodes that when merkelized yield the block's `state_root`. +Requests contain the hashes of Merkle tree nodes that when merkleized yield the block's `state_root`. The response will contain the values that, when hashed, yield the hashes inside the request body. From d64d97eee78645a67c436887bc5c69cf03b9d821 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:52:06 -0500 Subject: [PATCH 46/56] Update core.md --- specs/test_formats/ssz_static/core.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/test_formats/ssz_static/core.md b/specs/test_formats/ssz_static/core.md index 059f11027..1d470c338 100644 --- a/specs/test_formats/ssz_static/core.md +++ b/specs/test_formats/ssz_static/core.md @@ -1,6 +1,6 @@ # Test format: SSZ static types -The goal of this type is to provide clients with a solid reference how the known SSZ objects should be encoded. +The goal of this type is to provide clients with a solid reference for how the known SSZ objects should be encoded. Each object described in the Phase-0 spec is covered. This is important, as many of the clients aiming to serialize/deserialize objects directly into structs/classes do not support (or have alternatives for) generic SSZ encoding/decoding. @@ -27,6 +27,6 @@ A test-runner can implement the following assertions: ## References -**`serialized`**: [SSZ serialization](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#serialization) -**`root`** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) function -**`signing_root`** - [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) function +**`serialized`**: [SSZ serialization](../../simple-serialize.md#serialization) +**`root`** - [hash_tree_root](../../simple-serialize.md#merkleization) function +**`signing_root`** - [signing_root](../../simple-serialize.md#self-signed-containers) function From 47773f0da562bbcf15d533c1b61404c89162005c Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:53:09 -0500 Subject: [PATCH 47/56] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index aa5b7e302..b2b369e11 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -To learn more about sharding and eth2.0/Serenity, see the [sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm). +To learn more about sharding and eth2.0/Serenity, see the [sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm). This repo hosts the current eth2.0 specifications. Discussions about design rationale and proposed changes can be brought up and discussed as issues. Solidified, agreed upon changes to spec can be made through pull requests. @@ -11,10 +11,10 @@ This repo hosts the current eth2.0 specifications. Discussions about design rati Core specifications for eth2.0 client validation can be found in [specs/core](specs/core). These are divided into phases. Each subsequent phase depends upon the prior. The current phases specified are: * [Phase 0 -- The Beacon Chain](specs/core/0_beacon-chain.md) -* [Phase 1 -- Custody game](specs/core/1_custody-game.md) +* [Phase 1 -- Custody Game](specs/core/1_custody-game.md) * [Phase 1 -- Shard Data Chains](specs/core/1_shard-data-chains.md) -Accompanying documents can be found in [specs](specs) and include +Accompanying documents can be found in [specs](specs) and include: * [SimpleSerialize (SSZ) spec](specs/simple-serialize.md) * [BLS signature verification](specs/bls_signature.md) * [General test format](specs/test_formats/README.md) From cf1c78b2410a1982bdffaf375987c533c22ab047 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:55:15 -0500 Subject: [PATCH 48/56] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 184411925..83698a771 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1,6 +1,6 @@ # Ethereum 2.0 Phase 0 -- The Beacon Chain -**NOTICE**: This document is a work in progress for researchers and implementers. It reflects recent spec changes and takes precedence over the Python proof-of-concept implementation [[python-poc]](#ref-python-poc). +**NOTICE**: This document is a work in progress for researchers and implementers. It reflects recent spec changes and takes precedence over the Python proof-of-concept implementation [[python-poc]](https://github.com/ethereum/beacon_chain). ## Table of contents From afbec6851261561086bad312fbc41b2d7f2b58a3 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:57:15 -0500 Subject: [PATCH 49/56] Update README.md --- test_libs/pyspec/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/README.md b/test_libs/pyspec/README.md index 20c01bde4..b9bf86220 100644 --- a/test_libs/pyspec/README.md +++ b/test_libs/pyspec/README.md @@ -58,4 +58,4 @@ The pyspec is not a replacement. ## License -Same as the spec itself, see LICENSE file in spec repository root. +Same as the spec itself, see [LICENSE](../../LICENSE) file in spec repository root. From f164702b704a296c29b05e45251a5749f6cd101f Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:59:15 -0500 Subject: [PATCH 50/56] Update README.md --- specs/test_formats/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/test_formats/README.md b/specs/test_formats/README.md index da2e38c01..273659ce9 100644 --- a/specs/test_formats/README.md +++ b/specs/test_formats/README.md @@ -118,7 +118,7 @@ Separation of configuration and tests aims to: Note: Some clients prefer compile-time constants and optimizations. They should compile for each configuration once, and run the corresponding tests per build target. -The format is described in `configs/constant_presets`. +The format is described in [`configs/constant_presets`](../../configs/constant_presets/README.md#format). ## Fork-timeline @@ -129,7 +129,7 @@ A fork timeline is (preferably) loaded in as a configuration object into a clien - we may decide on an epoch number for a fork based on external events (e.g. Eth1 log event), a client should be able to activate a fork dynamically. -The format is described in `configs/fork_timelines`. +The format is described in [`configs/fork_timelines`](../../configs/fork_timelines/README.md#format). ## Config sourcing From 1d59b335904a3b0f0c8952d129a2c98bec26d8ce Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:59:49 -0500 Subject: [PATCH 51/56] Update README.md --- test_generators/ssz_static/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_generators/ssz_static/README.md b/test_generators/ssz_static/README.md index d73556e1b..453d6d0e5 100644 --- a/test_generators/ssz_static/README.md +++ b/test_generators/ssz_static/README.md @@ -1,6 +1,6 @@ # SSZ-static The purpose of this test-generator is to provide test-vectors for the most important applications of SSZ: - the serialization and hashing of ETH 2.0 data types + the serialization and hashing of ETH 2.0 data types. Test-format documentation can be found [here](../../specs/test_formats/ssz_static/README.md). From 2048b657b62b568d881b562671bc314152b13b7c Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 12:59:58 -0500 Subject: [PATCH 52/56] Update sign_msg.md --- specs/test_formats/bls/sign_msg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/test_formats/bls/sign_msg.md b/specs/test_formats/bls/sign_msg.md index dd93174f2..9916f2cc2 100644 --- a/specs/test_formats/bls/sign_msg.md +++ b/specs/test_formats/bls/sign_msg.md @@ -12,7 +12,7 @@ input: output: bytes96 -- expected signature ``` -All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x` +All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`. ## Condition From dbcac289c80e719b67e9e885feef756cd184d05a Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 13:00:05 -0500 Subject: [PATCH 53/56] Update priv_to_pub.md --- specs/test_formats/bls/priv_to_pub.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/test_formats/bls/priv_to_pub.md b/specs/test_formats/bls/priv_to_pub.md index 7af148d0f..ef62241ae 100644 --- a/specs/test_formats/bls/priv_to_pub.md +++ b/specs/test_formats/bls/priv_to_pub.md @@ -9,7 +9,7 @@ input: bytes32 -- the private key output: bytes48 -- the public key ``` -All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x` +All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`. ## Condition From babf2721c7ef517673e1f3497899ed36aa8d6259 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 13:00:15 -0500 Subject: [PATCH 54/56] Update msg_hash_g2_uncompressed.md --- specs/test_formats/bls/msg_hash_g2_uncompressed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/test_formats/bls/msg_hash_g2_uncompressed.md b/specs/test_formats/bls/msg_hash_g2_uncompressed.md index f42ea9998..792fe1f03 100644 --- a/specs/test_formats/bls/msg_hash_g2_uncompressed.md +++ b/specs/test_formats/bls/msg_hash_g2_uncompressed.md @@ -11,7 +11,7 @@ input: output: List[List[bytes48]] -- 3 lists, each a length of two ``` -All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x` +All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`. ## Condition From 58c50c2f08af4ca83619d6ff21270df471984c5a Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 13:00:25 -0500 Subject: [PATCH 55/56] Update msg_hash_g2_compressed.md --- specs/test_formats/bls/msg_hash_g2_compressed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/test_formats/bls/msg_hash_g2_compressed.md b/specs/test_formats/bls/msg_hash_g2_compressed.md index 4e194e90b..2feeb92ba 100644 --- a/specs/test_formats/bls/msg_hash_g2_compressed.md +++ b/specs/test_formats/bls/msg_hash_g2_compressed.md @@ -11,7 +11,7 @@ input: output: List[bytes48] -- length of two ``` -All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x` +All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`. ## Condition From b6a085d0d7555c7ec3818b557930cd6087361f9d Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Tue, 23 Apr 2019 13:01:21 -0500 Subject: [PATCH 56/56] Update bls_signature.md --- specs/bls_signature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/bls_signature.md b/specs/bls_signature.md index beef19df5..18e2d8c9a 100644 --- a/specs/bls_signature.md +++ b/specs/bls_signature.md @@ -86,7 +86,7 @@ def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]: ### `modular_squareroot` -`modular_squareroot(x)` returns a solution `y` to `y**2 % q == x`, and `None` if none exists. If there are two solutions the one with higher imaginary component is favored; if both solutions have equal imaginary component the one with higher real component is favored (note that this is equivalent to saying that the single solution with either imaginary component > p/2 or imaginary component zero and real component > p/2 is favored). +`modular_squareroot(x)` returns a solution `y` to `y**2 % q == x`, and `None` if none exists. If there are two solutions, the one with higher imaginary component is favored; if both solutions have equal imaginary component, the one with higher real component is favored (note that this is equivalent to saying that the single solution with either imaginary component > p/2 or imaginary component zero and real component > p/2 is favored). The following is a sample implementation; implementers are free to implement modular square roots as they wish. Note that `x2 = -x1` is an _additive modular inverse_ so real and imaginary coefficients remain in `[0 .. q-1]`. `coerce_to_int(element: Fq) -> int` is a function that takes Fq element `element` (i.e. integers `mod q`) and converts it to a regular integer.