From 9b7e0ab2bede10892e187e6deff2b7cdc6b58b60 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Fri, 13 Mar 2020 17:15:25 +0000 Subject: [PATCH 01/22] Fix error in custody bit computation --- specs/phase1/custody-game.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/phase1/custody-game.md b/specs/phase1/custody-game.md index af3aadc96..822970a19 100644 --- a/specs/phase1/custody-game.md +++ b/specs/phase1/custody-game.md @@ -192,10 +192,10 @@ def get_custody_atoms(bytez: bytes) -> Sequence[bytes]: def compute_custody_bit(key: BLSSignature, data: bytes) -> bit: full_G2_element = bls.signature_to_G2(key) s = full_G2_element[0].coeffs - bits = [legendre_bit(sum(s[i % 2]**i * int.from_bytes(atom, "little")), BLS12_381_Q) - for i, atom in enumerate(get_custody_atoms(data))] - # XOR all atom bits - return bit(sum(bits) % 2) + custody_atoms = get_custody_atoms(data) + n = len(custody_atoms) + return legendre_bit(sum(s[i % 2]**i * int.from_bytes(atom, "little")) + for i, atom in enumerate(custody_atoms) + s[n % 2]**n, BLS12_381_Q) ``` ### `get_randao_epoch_for_custody_period` From 5315f3db35e68aee4e3d0f8620879a3dce9b2097 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 13 Mar 2020 12:00:34 -0600 Subject: [PATCH 02/22] clarify proposer_index must be from expected shuffling when validating block gossip --- specs/phase0/p2p-interface.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 77eeb21df..70efd8844 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -261,6 +261,7 @@ There are two primary global topics used to propagate beacon blocks and aggregat - `beacon_block` - This topic is used solely for propagating new signed beacon blocks to all nodes on the networks. Signed blocks are sent in their entirety. The following validations MUST pass before forwarding the `signed_beacon_block` on the network - The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot <= current_slot` (a client MAY queue future blocks for processing at the appropriate slot). - The block is from a slot greater than the latest finalized slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)` (a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc). + - The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated. - The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`. - The proposer signature, `signed_beacon_block.signature`, is valid. - `beacon_aggregate_and_proof` - This topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. (We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message` and `aggregate = aggregate_and_proof.aggregate`) From d299b06a1c9be7af43d12dc93f49ac79de72b942 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 16 Mar 2020 09:52:27 -0600 Subject: [PATCH 03/22] fix custody bit calculation format --- specs/phase1/custody-game.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase1/custody-game.md b/specs/phase1/custody-game.md index 822970a19..0d69c995a 100644 --- a/specs/phase1/custody-game.md +++ b/specs/phase1/custody-game.md @@ -194,8 +194,8 @@ def compute_custody_bit(key: BLSSignature, data: bytes) -> bit: s = full_G2_element[0].coeffs custody_atoms = get_custody_atoms(data) n = len(custody_atoms) - return legendre_bit(sum(s[i % 2]**i * int.from_bytes(atom, "little")) - for i, atom in enumerate(custody_atoms) + s[n % 2]**n, BLS12_381_Q) + a = sum(s[i % 2]**i * int.from_bytes(atom, "little") for i, atom in enumerate(custody_atoms) + s[n % 2]**n) + return legendre_bit(a, BLS12_381_Q) ``` ### `get_randao_epoch_for_custody_period` From e2ef4365e2b2feebc4ec0bac228a10c0343def51 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 16 Mar 2020 11:19:21 -0600 Subject: [PATCH 04/22] min total balance is now EFFECTIVE_BALANCE_INCREMENT. update testing --- specs/phase0/beacon-chain.md | 5 ++- tests/core/pyspec/eth2spec/test/context.py | 16 ++++++++ .../eth2spec/test/helpers/attestations.py | 5 ++- .../test_process_attestation.py | 6 +-- .../test_process_rewards_and_penalties.py | 38 +++++++++++++++++-- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index bd5e7f5f4..02b139d0d 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -996,10 +996,11 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: ```python def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei: """ - Return the combined effective balance of the ``indices``. (1 Gwei minimum to avoid divisions by zero.) + Return the combined effective balance of the ``indices``. + ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. Math safe up to ~10B ETH, afterwhich this overflows uint64. """ - return Gwei(max(1, sum([state.validators[index].effective_balance for index in indices]))) + return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) ``` #### `get_total_active_balance` diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 5338ccb9d..bdf26e637 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -71,6 +71,14 @@ def default_activation_threshold(spec): return spec.MAX_EFFECTIVE_BALANCE +def zero_activation_threshold(spec): + """ + Helper method to use the default balance activation threshold for state creation for tests. + Usage: `@with_custom_state(threshold_fn=one_gwei_activation_threshold, ...)` + """ + return 0 + + def default_balances(spec): """ Helper method to create a series of default balances. @@ -104,6 +112,14 @@ def misc_balances(spec): return [spec.MAX_EFFECTIVE_BALANCE] * num_validators + [spec.MIN_DEPOSIT_AMOUNT] * num_misc_validators +def low_single_balance(spec): + """ + Helper method to create a single of balance of 1 Gwei. + Usage: `@with_custom_state(balances_fn=low_single_balance, ...)` + """ + return [1] + + def single_phase(fn): """ Decorator that filters out the phases data. diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 047966890..281d11b45 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -39,7 +39,7 @@ def build_attestation_data(spec, state, slot, index): ) -def get_valid_attestation(spec, state, slot=None, index=None, signed=False): +def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signed=False): if slot is None: slot = state.slot if index is None: @@ -59,7 +59,8 @@ def get_valid_attestation(spec, state, slot=None, index=None, signed=False): aggregation_bits=aggregation_bits, data=attestation_data, ) - fill_aggregate_attestation(spec, state, attestation) + if not empty: + fill_aggregate_attestation(spec, state, attestation) if signed: sign_attestation(spec, state, attestation) return attestation diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 7937614a4..df42b6e1a 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -292,14 +292,12 @@ def test_bad_source_root(spec, state): @with_all_phases @spec_state_test def test_empty_aggregation_bits(spec, state): - attestation = get_valid_attestation(spec, state) + attestation = get_valid_attestation(spec, state, empty=True) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( + assert attestation.aggregation_bits == Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( *([0b0] * len(attestation.aggregation_bits))) - sign_attestation(spec, state, attestation) - yield from run_attestation_processing(spec, state, attestation) diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py index 111033799..33c739d5d 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py @@ -2,7 +2,9 @@ from copy import deepcopy from eth2spec.test.context import ( spec_state_test, with_all_phases, spec_test, - misc_balances, with_custom_state, default_activation_threshold, + misc_balances, low_single_balance, + with_custom_state, + default_activation_threshold, zero_activation_threshold, single_phase, ) from eth2spec.test.helpers.state import ( @@ -60,12 +62,12 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state): assert state.balances[index] == pre_state.balances[index] -def prepare_state_with_full_attestations(spec, state): +def prepare_state_with_full_attestations(spec, state, empty=False): attestations = [] for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY): # create an attestation for each slot in epoch if slot < spec.SLOTS_PER_EPOCH: - attestation = get_valid_attestation(spec, state, signed=True) + attestation = get_valid_attestation(spec, state, empty=empty, signed=True) attestations.append(attestation) # fill each created slot in state after inclusion delay if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0: @@ -143,6 +145,20 @@ def test_full_attestations_misc_balances(spec, state): assert state.balances[index] == pre_state.balances[index] +@with_all_phases +@spec_test +@with_custom_state(balances_fn=low_single_balance, threshold_fn=zero_activation_threshold) +@single_phase +def test_full_attestations_one_validaor_one_gwei(spec, state): + attestations = prepare_state_with_full_attestations(spec, state) + + yield from run_process_rewards_and_penalties(spec, state) + + # Few assertions. Mainly to check that this extreme case can run without exception + attesting_indices = spec.get_unslashed_attesting_indices(state, attestations) + assert len(attesting_indices) == 1 + + @with_all_phases @spec_state_test def test_no_attestations_all_penalties(spec, state): @@ -157,6 +173,22 @@ def test_no_attestations_all_penalties(spec, state): assert state.balances[index] < pre_state.balances[index] +@with_all_phases +@spec_state_test +def test_empty_attestations(spec, state): + attestations = prepare_state_with_full_attestations(spec, state, empty=True) + + pre_state = deepcopy(state) + + yield from run_process_rewards_and_penalties(spec, state) + + attesting_indices = spec.get_unslashed_attesting_indices(state, attestations) + assert len(attesting_indices) == 0 + + for index in range(len(pre_state.validators)): + assert state.balances[index] < pre_state.balances[index] + + @with_all_phases @spec_state_test def test_duplicate_attestation(spec, state): From e6998a61cd423a9954a58f787974a98eac0679bf Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 17 Mar 2020 10:54:48 -0600 Subject: [PATCH 05/22] remove IK handshake from noise spec (removed in core libp2p specs) --- specs/phase0/p2p-interface.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 70efd8844..fbbcb088e 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -157,8 +157,7 @@ The following SecIO parameters MUST be supported by all stacks: The [Libp2p-noise](https://github.com/libp2p/specs/tree/master/noise) secure channel handshake with `secp256k1` identities will be used for mainnet. -As specified in the libp2p specification, clients MUST support the `XX` handshake pattern and -can optionally implement the `IK` and `XXfallback` patterns for optimistic 0-RTT. +As specified in the libp2p specification, clients MUST support the `XX` handshake pattern. ## Protocol Negotiation From 0a9b306d60df2fbc673f2f8fdebffc0b4f85b70a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 18 Mar 2020 09:55:09 -0600 Subject: [PATCH 06/22] PR feedback from @hwwhww --- specs/phase0/beacon-chain.md | 5 +++++ tests/core/pyspec/eth2spec/test/context.py | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 02b139d0d..45a7df3d9 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1009,6 +1009,7 @@ def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei: def get_total_active_balance(state: BeaconState) -> Gwei: """ Return the combined effective balance of the active validators. + Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. """ return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state)))) ``` @@ -1290,6 +1291,10 @@ def get_unslashed_attesting_indices(state: BeaconState, ```python def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei: + """ + Return the combined effective balance of the set of unslashed validators participating in ``attestations``. + Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. + """ return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) ``` diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index bdf26e637..84c617a08 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -73,8 +73,8 @@ def default_activation_threshold(spec): def zero_activation_threshold(spec): """ - Helper method to use the default balance activation threshold for state creation for tests. - Usage: `@with_custom_state(threshold_fn=one_gwei_activation_threshold, ...)` + Helper method to use 0 gwei as the activation threshold for state creation for tests. + Usage: `@with_custom_state(threshold_fn=zero_activation_threshold, ...)` """ return 0 From 759af6734525d2f57747cb763a019cd2d2d5ea00 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 19 Mar 2020 19:22:57 +0100 Subject: [PATCH 07/22] Normalize attestation signature getter name --- specs/phase0/validator.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 6ddbef81f..02d86313a 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -404,12 +404,12 @@ Set `attestation.data = attestation_data` where `attestation_data` is the `Attes ##### Aggregate signature -Set `attestation.signature = signed_attestation_data` where `signed_attestation_data` is obtained from: +Set `attestation.signature = attestation_signature` where `attestation_signature` is obtained from: ```python -def get_signed_attestation_data(state: BeaconState, attestation: IndexedAttestation, privkey: int) -> BLSSignature: - domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch) - signing_root = compute_signing_root(attestation.data, domain) +def get_attestation_signature(state: BeaconState, attestation: AttestationData, privkey: int) -> BLSSignature: + domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.target.epoch) + signing_root = compute_signing_root(attestation, domain) return bls.Sign(privkey, signing_root) ``` From 2978f21a3929237a03db82b0fc9fd177e2cf38fc Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 19 Mar 2020 15:17:06 -0600 Subject: [PATCH 08/22] minor edit to validator get_attestation_signature --- specs/phase0/validator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 02d86313a..54cd19a93 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -407,9 +407,9 @@ Set `attestation.data = attestation_data` where `attestation_data` is the `Attes Set `attestation.signature = attestation_signature` where `attestation_signature` is obtained from: ```python -def get_attestation_signature(state: BeaconState, attestation: AttestationData, privkey: int) -> BLSSignature: - domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.target.epoch) - signing_root = compute_signing_root(attestation, domain) +def get_attestation_signature(state: BeaconState, attestation_data: AttestationData, privkey: int) -> BLSSignature: + domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch) + signing_root = compute_signing_root(attestation_data, domain) return bls.Sign(privkey, signing_root) ``` From 33f8f4936d3185920fe59b404792c91b614e30ae Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 20 Mar 2020 20:38:36 +0100 Subject: [PATCH 09/22] Fix base-reward memoization bug, improve memoization with LRU, and improve misc rewards test --- Makefile | 4 ++ setup.py | 40 ++++++++++++++----- tests/core/pyspec/eth2spec/test/context.py | 7 +++- .../test_process_rewards_and_penalties.py | 31 ++++++++------ 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index b468e648c..e8f3d21bc 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,10 @@ test: pyspec . venv/bin/activate; cd $(PY_SPEC_DIR); \ python -m pytest -n 4 --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec +find_test: pyspec + . venv/bin/activate; cd $(PY_SPEC_DIR); \ + python -m pytest -k=$(K) --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec + citest: pyspec mkdir -p tests/core/pyspec/test-reports/eth2spec; . venv/bin/activate; cd $(PY_SPEC_DIR); \ python -m pytest -n 4 --junitxml=eth2spec/test_results.xml eth2spec diff --git a/setup.py b/setup.py index 906e30240..9b5a6cabc 100644 --- a/setup.py +++ b/setup.py @@ -92,6 +92,8 @@ from dataclasses import ( field, ) +from lru import LRU + from eth2spec.utils.ssz.ssz_impl import hash_tree_root from eth2spec.utils.ssz.ssz_typing import ( View, boolean, Container, List, Vector, uint64, @@ -114,6 +116,8 @@ from dataclasses import ( field, ) +from lru import LRU + from eth2spec.utils.ssz.ssz_impl import hash_tree_root from eth2spec.utils.ssz.ssz_typing import ( View, boolean, Container, List, Vector, uint64, uint8, bit, @@ -152,8 +156,8 @@ def hash(x: bytes) -> Bytes32: # type: ignore return hash_cache[x] -def cache_this(key_fn, value_fn): # type: ignore - cache_dict = {} # type: ignore +def cache_this(key_fn, value_fn, lru_size): # type: ignore + cache_dict = LRU(size=lru_size) def wrapper(*args, **kw): # type: ignore key = key_fn(*args, **kw) @@ -164,35 +168,50 @@ def cache_this(key_fn, value_fn): # type: ignore return wrapper +_compute_shuffled_index = compute_shuffled_index +compute_shuffled_index = cache_this( + lambda index, index_count, seed: (index, index_count, seed), + _compute_shuffled_index, lru_size=SLOTS_PER_EPOCH * 3) + +_get_total_active_balance = get_total_active_balance +get_total_active_balance = cache_this( + lambda state: (state.validators.hash_tree_root(), state.slot), + _get_total_active_balance, lru_size=10) + _get_base_reward = get_base_reward get_base_reward = cache_this( - lambda state, index: (state.validators.hash_tree_root(), state.slot), - _get_base_reward) + lambda state, index: (state.validators.hash_tree_root(), state.slot, index), + _get_base_reward, lru_size=10) _get_committee_count_at_slot = get_committee_count_at_slot get_committee_count_at_slot = cache_this( lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_committee_count_at_slot) + _get_committee_count_at_slot, lru_size=SLOTS_PER_EPOCH * 3) _get_active_validator_indices = get_active_validator_indices get_active_validator_indices = cache_this( lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_active_validator_indices) + _get_active_validator_indices, lru_size=3) _get_beacon_committee = get_beacon_committee get_beacon_committee = cache_this( lambda state, slot, index: (state.validators.hash_tree_root(), state.randao_mixes.hash_tree_root(), slot, index), - _get_beacon_committee) + _get_beacon_committee, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) _get_matching_target_attestations = get_matching_target_attestations get_matching_target_attestations = cache_this( lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_target_attestations) + _get_matching_target_attestations, lru_size=10) _get_matching_head_attestations = get_matching_head_attestations get_matching_head_attestations = cache_this( lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_head_attestations)''' + _get_matching_head_attestations, lru_size=10) + +_get_attesting_indices = get_attesting_indices +get_attesting_indices = cache_this(lambda state, data, bits: + (state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root()), + _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3)''' def objects_to_spec(spec_object: SpecObject, imports: str, fork: str) -> str: @@ -481,6 +500,7 @@ setup( "py_ecc==2.0.0", "dataclasses==0.6", "remerkleable==0.1.12", - "ruamel.yaml==0.16.5" + "ruamel.yaml==0.16.5", + "lru-dict==1.1.6" ] ) diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 5338ccb9d..6e24c4cfe 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -6,6 +6,7 @@ from .helpers.genesis import create_genesis_state from .utils import vector_test, with_meta_tags +from random import Random from typing import Any, Callable, Sequence, TypedDict, Protocol from importlib import reload @@ -100,8 +101,10 @@ def misc_balances(spec): Usage: `@with_custom_state(balances_fn=misc_balances, ...)` """ num_validators = spec.SLOTS_PER_EPOCH * 8 - num_misc_validators = spec.SLOTS_PER_EPOCH - return [spec.MAX_EFFECTIVE_BALANCE] * num_validators + [spec.MIN_DEPOSIT_AMOUNT] * num_misc_validators + balances = [spec.MAX_EFFECTIVE_BALANCE * 2 * i // num_validators for i in range(num_validators)] + rng = Random(1234) + rng.shuffle(balances) + return balances def single_phase(fn): diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py index 111033799..692260585 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py @@ -1,8 +1,6 @@ -from copy import deepcopy - from eth2spec.test.context import ( spec_state_test, with_all_phases, spec_test, - misc_balances, with_custom_state, default_activation_threshold, + misc_balances, with_custom_state, single_phase, ) from eth2spec.test.helpers.state import ( @@ -24,7 +22,7 @@ def run_process_rewards_and_penalties(spec, state): @with_all_phases @spec_state_test def test_genesis_epoch_no_attestations_no_penalties(spec, state): - pre_state = deepcopy(state) + pre_state = state.copy() assert spec.compute_epoch_at_slot(state.slot) == spec.GENESIS_EPOCH @@ -52,7 +50,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state): # ensure has not cross the epoch boundary assert spec.compute_epoch_at_slot(state.slot) == spec.GENESIS_EPOCH - pre_state = deepcopy(state) + pre_state = state.copy() yield from run_process_rewards_and_penalties(spec, state) @@ -84,7 +82,7 @@ def prepare_state_with_full_attestations(spec, state): def test_full_attestations(spec, state): attestations = prepare_state_with_full_attestations(spec, state) - pre_state = deepcopy(state) + pre_state = state.copy() yield from run_process_rewards_and_penalties(spec, state) @@ -122,18 +120,19 @@ def test_full_attestations_random_incorrect_fields(spec, state): @with_all_phases @spec_test -@with_custom_state(balances_fn=misc_balances, threshold_fn=default_activation_threshold) +@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.MAX_EFFECTIVE_BALANCE // 2) @single_phase def test_full_attestations_misc_balances(spec, state): attestations = prepare_state_with_full_attestations(spec, state) - pre_state = deepcopy(state) + pre_state = state.copy() yield from run_process_rewards_and_penalties(spec, state) attesting_indices = spec.get_unslashed_attesting_indices(state, attestations) assert len(attesting_indices) > 0 assert len(attesting_indices) != len(pre_state.validators) + assert any(v.effective_balance != spec.MAX_EFFECTIVE_BALANCE for v in state.validators) for index in range(len(pre_state.validators)): if index in attesting_indices: assert state.balances[index] > pre_state.balances[index] @@ -141,13 +140,21 @@ def test_full_attestations_misc_balances(spec, state): assert state.balances[index] < pre_state.balances[index] else: assert state.balances[index] == pre_state.balances[index] + # Check if base rewards are consistent with effective balance. + brs = {} + for index in attesting_indices: + br = spec.get_base_reward(state, index) + if br in brs: + assert brs[br] == state.validators[index].effective_balance + else: + brs[br] = state.validators[index].effective_balance @with_all_phases @spec_state_test def test_no_attestations_all_penalties(spec, state): next_epoch(spec, state) - pre_state = deepcopy(state) + pre_state = state.copy() assert spec.compute_epoch_at_slot(state.slot) == spec.GENESIS_EPOCH + 1 @@ -173,8 +180,8 @@ def test_duplicate_attestation(spec, state): assert len(participants) > 0 - single_state = deepcopy(state) - dup_state = deepcopy(state) + single_state = state.copy() + dup_state = state.copy() inclusion_slot = state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY add_attestations_to_state(spec, single_state, [attestation], inclusion_slot) @@ -220,7 +227,7 @@ def test_attestations_some_slashed(spec, state): assert spec.compute_epoch_at_slot(state.slot) == spec.GENESIS_EPOCH + 1 assert len(state.previous_epoch_attestations) == spec.SLOTS_PER_EPOCH - pre_state = deepcopy(state) + pre_state = state.copy() yield from run_process_rewards_and_penalties(spec, state) From 93ff016e768576b9d5ac6e2915bd04b6b6b2f257 Mon Sep 17 00:00:00 2001 From: protolambda Date: Mon, 23 Mar 2020 20:35:50 +0100 Subject: [PATCH 10/22] fix base-reward lru size, fix indent, change total-active-balance key --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 9b5a6cabc..911eb65b0 100644 --- a/setup.py +++ b/setup.py @@ -175,13 +175,13 @@ compute_shuffled_index = cache_this( _get_total_active_balance = get_total_active_balance get_total_active_balance = cache_this( - lambda state: (state.validators.hash_tree_root(), state.slot), + lambda state: (state.validators.hash_tree_root(), compute_epoch_at_slot(state.slot)), _get_total_active_balance, lru_size=10) _get_base_reward = get_base_reward get_base_reward = cache_this( lambda state, index: (state.validators.hash_tree_root(), state.slot, index), - _get_base_reward, lru_size=10) + _get_base_reward, lru_size=2048) _get_committee_count_at_slot = get_committee_count_at_slot get_committee_count_at_slot = cache_this( @@ -209,9 +209,9 @@ get_matching_head_attestations = cache_this( _get_matching_head_attestations, lru_size=10) _get_attesting_indices = get_attesting_indices -get_attesting_indices = cache_this(lambda state, data, bits: - (state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root()), - _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3)''' +get_attesting_indices = cache_this( + lambda state, data, bits: (state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root()), + _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3)''' def objects_to_spec(spec_object: SpecObject, imports: str, fork: str) -> str: From 83851f69195707a923226d24f6c402e3ad75c322 Mon Sep 17 00:00:00 2001 From: Nathaniel Jensen Date: Tue, 24 Mar 2020 13:39:27 +1100 Subject: [PATCH 11/22] Fix inaccuracies in test format documentation. * `block_header` operation to accept input of type `BeaconBlock` * `voluntary_exit` operation to accept input of type `SignedVoluntaryExit`. * sanity/blocks to note that inputs are of type `SignedBeaconBlock` --- tests/formats/operations/README.md | 20 ++++++++++---------- tests/formats/sanity/blocks.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index f1ec0429a..bb4636ec0 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -18,11 +18,11 @@ A YAML-encoded `BeaconState`, the state before applying the operation. Also available as `pre.ssz`. -### `.yaml` +### `.yaml` A YAML-encoded operation object, e.g. a `ProposerSlashing`, or `Deposit`. -Also available as `.ssz`. +Also available as `.ssz`. ### `post.yaml` @@ -39,14 +39,14 @@ This excludes the other parts of the block-transition. Operations: -| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | -|-------------------------|----------------------|----------------------|--------------------------------------------------------| -| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | -| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | -| `block_header` | `Block` | **`block`** | `process_block_header(state, block)` | -| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | -| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | -| `voluntary_exit` | `VoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | +| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | +|-------------------------|-----------------------|----------------------|--------------------------------------------------------| +| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | +| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | +| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | +| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | +| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | +| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. diff --git a/tests/formats/sanity/blocks.md b/tests/formats/sanity/blocks.md index 2b50d19ca..991bc35d2 100644 --- a/tests/formats/sanity/blocks.md +++ b/tests/formats/sanity/blocks.md @@ -25,7 +25,7 @@ Also available as `pre.ssz`. A series of files, with `` in range `[0, blocks_count)`. Blocks need to be processed in order, following the main transition function (i.e. process slot and epoch transitions in between blocks as normal) -Each file is a YAML-encoded `BeaconBlock`. +Each file is a YAML-encoded `SignedBeaconBlock`. Each block is also available as `blocks_.ssz` From cf20a7ae3e3c6ac600b326f0751a97bbe3a9fc82 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 18 Mar 2020 18:20:25 +1100 Subject: [PATCH 12/22] Shift some ENR fields to MAY --- specs/phase0/p2p-interface.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index fbbcb088e..a85695a9c 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -622,6 +622,9 @@ This integration enables the libp2p stack to subsequently form connections and s The Ethereum Node Record (ENR) for an Ethereum 2.0 client MUST contain the following entries (exclusive of the sequence number and signature, which MUST be present in an ENR): - The compressed secp256k1 publickey, 33 bytes (`secp256k1` field). + +The ENR MAY contain the following entries: + - An IPv4 address (`ip` field) and/or IPv6 address (`ip6` field). - A TCP port (`tcp` field) representing the local libp2p listening port. - A UDP port (`udp` field) representing the local discv5 listening port. From f227e026fa295cc6a90c70f6eb04ab96810a8503 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 23 Mar 2020 16:22:13 +1100 Subject: [PATCH 13/22] Draft PING and ENR rpc protocol --- specs/phase0/p2p-interface.md | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index fbbcb088e..acd3fc8c0 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -601,6 +601,69 @@ Clients MUST support requesting blocks since the latest finalized epoch. Clients MUST respond with at least one block, if they have it. Clients MAY limit the number of blocks in the response. +#### Ping + +**Protocol ID:** `/eth2/beacon_chain/req/ping/1/` + +Request Content: + +``` +( + uint64 +) +``` + +Response Content: + +``` +( + uint64 +) +``` + +Sent intermittently, the PING protocol checks liveness of connected peers. +Peers send and respond with their local ENR sequence number. + +A client may then determine if their local record of a peer's ENR is up to date +and may request an updated version via the ENR RPC method if not. + +The request MUST be encoded as an SSZ-field. + +The response MUST consist of a single `response_chunk`. + +#### Enr + +**Protocol ID:** `/eth2/beacon_chain/req/enr/1/` + +No Request Content. + + +Response Content: + +``` +( + enr: Bytes, + subnet_expiries: []Slot +) +``` + +Requests the ENR of a peer. The request opens and negotiates the stream without +sending any request content. Once established the receiving peer responds with +it's local up-to-date ENR along with a list of `Slot` corresponding to +the expiry of all long-lived subnets specified by the `attnets` field in the +sent ENR. + +The `subnet_expiries` list MUST have a length corresponding to the number of +true values in the `attnets` bitfield. The order of the slots in `subnet_expiries` +MUST correspond to the order of long-lived subnets specified in the `attnets` +bitfield. + +The `enr` field represents the rlp-encoded bytes of the local ENR. + +The response MUST be encoded as an SSZ-container. + +The response MUST consist of a single `response_chunk`. + ## The discovery domain: discv5 Discovery Version 5 ([discv5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md)) is used for peer discovery, both in the interoperability testnet and mainnet. From 1c49f99094f3796ecd2d8ebf060b41c585292d35 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Tue, 24 Mar 2020 17:15:40 +1100 Subject: [PATCH 14/22] Fix bug in attestation reward calculation --- specs/phase0/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 45a7df3d9..23fa5ceee 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1372,7 +1372,7 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence if index in unslashed_attesting_indices: increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balance totals to avoid uint64 overflow reward_numerator = get_base_reward(state, index) * (attesting_balance // increment) - rewards[index] = reward_numerator // (total_balance // increment) + rewards[index] += reward_numerator // (total_balance // increment) else: penalties[index] += get_base_reward(state, index) From 88fddf237959b110fbe15e3484cf9703c1336adc Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 18 Mar 2020 13:26:03 +1100 Subject: [PATCH 15/22] Remove unneccessary clock disparity check There is no need to mention clock disparity when comparing two static slot values (assuming the clock disparity is less than a slot, even then I don't think that's the intention). --- specs/phase0/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index a85695a9c..bfd181587 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -259,7 +259,7 @@ There are two primary global topics used to propagate beacon blocks and aggregat - `beacon_block` - This topic is used solely for propagating new signed beacon blocks to all nodes on the networks. Signed blocks are sent in their entirety. The following validations MUST pass before forwarding the `signed_beacon_block` on the network - The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot <= current_slot` (a client MAY queue future blocks for processing at the appropriate slot). - - The block is from a slot greater than the latest finalized slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)` (a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc). + - The block is from a slot greater than the latest finalized slot -- i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)` (a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc). - The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated. - The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`. - The proposer signature, `signed_beacon_block.signature`, is valid. From d5a9af6469754da2ba4d1ede8650463b212955be Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 25 Mar 2020 11:57:37 -0600 Subject: [PATCH 16/22] update ping protocol to use MetaData --- specs/phase0/p2p-interface.md | 56 ++++++++++++++++++++++------------- specs/phase0/validator.md | 2 +- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index acd3fc8c0..0c436f057 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -28,6 +28,7 @@ It consists of four main sections: - [Multiplexing](#multiplexing) - [Eth2 network interaction domains](#eth2-network-interaction-domains) - [Configuration](#configuration) + - [MetaData](#metadata) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) @@ -49,6 +50,8 @@ It consists of four main sections: - [Goodbye](#goodbye) - [BeaconBlocksByRange](#beaconblocksbyrange) - [BeaconBlocksByRoot](#beaconblocksbyroot) + - [Ping](#ping) + - [GetMetaData](#getmetadata) - [The discovery domain: discv5](#the-discovery-domain-discv5) - [Integration into libp2p stacks](#integration-into-libp2p-stacks) - [ENR structure](#enr-structure) @@ -195,6 +198,22 @@ This section outlines constants that are used in this spec. | `ATTESTATION_PROPAGATION_SLOT_RANGE` | `32` | The maximum number of slots during which an attestation can be propagated. | | `MAXIMUM_GOSSIP_CLOCK_DISPARITY` | `500ms` | The maximum milliseconds of clock disparity assumed between honest nodes. | +## MetaData + +Clients MUST locally store the following `MetaData`: + +``` +( + seq_number: uint64 + attnets: Bitvector[ATTESTATION_SUBNET_COUNT] +) +``` + +Where + +- `seq_number` is a `uint64` starting at `0` used to version the node's metadata. If any other field in the local `MetaData` changes, the node MUST increment `seq_number` by 1. +- `attnets` is a `Bitvector` representing the node's persistent attestation subnet subscriptions. + ## The gossip domain: gossipsub Clients MUST support the [gossipsub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) libp2p protocol. @@ -621,44 +640,35 @@ Response Content: ) ``` -Sent intermittently, the PING protocol checks liveness of connected peers. -Peers send and respond with their local ENR sequence number. +Sent intermittently, the `Ping` protocol checks liveness of connected peers. +Peers send and respond with their local metadata sequence number (`MetaData.seq_number`). -A client may then determine if their local record of a peer's ENR is up to date -and may request an updated version via the ENR RPC method if not. +If the peer does not respond to the `Ping` request, the client MAY disconnect from the peer. + +A client can then determine if their local record of a peer's MetaData is up to date +and MAY request an updated version via the `MetaData` RPC method if not. The request MUST be encoded as an SSZ-field. The response MUST consist of a single `response_chunk`. -#### Enr +#### GetMetaData -**Protocol ID:** `/eth2/beacon_chain/req/enr/1/` +**Protocol ID:** `/eth2/beacon_chain/req/metadata/1/` No Request Content. - Response Content: ``` ( - enr: Bytes, - subnet_expiries: []Slot + MetaData ) ``` -Requests the ENR of a peer. The request opens and negotiates the stream without +Requests the MetaData of a peer. The request opens and negotiates the stream without sending any request content. Once established the receiving peer responds with -it's local up-to-date ENR along with a list of `Slot` corresponding to -the expiry of all long-lived subnets specified by the `attnets` field in the -sent ENR. - -The `subnet_expiries` list MUST have a length corresponding to the number of -true values in the `attnets` bitfield. The order of the slots in `subnet_expiries` -MUST correspond to the order of long-lived subnets specified in the `attnets` -bitfield. - -The `enr` field represents the rlp-encoded bytes of the local ENR. +it's local most up-to-date MetaData. The response MUST be encoded as an SSZ-container. @@ -693,12 +703,16 @@ Specifications of these parameters can be found in the [ENR Specification](http: #### Attestation subnet bitfield -The ENR MAY contain an entry (`attnets`) signifying the attestation subnet bitfield with the following form to more easily discover peers participating in particular attestation gossip subnets. +The ENR `attnets` entry signifies the attestation subnet bitfield with the following form to more easily discover peers participating in particular attestation gossip subnets. | Key | Value | |:-------------|:-------------------------------------------------| | `attnets` | SSZ `Bitvector[ATTESTATION_SUBNET_COUNT]` | +If a node's `MetaData.attnets` has any non-zero bit, the ENR MUST include the `attnets` entry with the same value as `MetaData.attnets`. + +If a node's `MetaData.attnets` is composed of all zeros, the ENR MAY optionally include the `attnets` entry or leave it out entirely. + #### Interop In the interoperability testnet, all peers will support all capabilities defined in this document (gossip, full Req/Resp suite, discovery protocol), therefore the ENR record does not need to carry Eth2 capability information, as it would be superfluous. diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 54cd19a93..bbed14ec2 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -202,7 +202,7 @@ Specifically a validator should: * Call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments. * Join the pubsub topic -- `committee_index{committee_index % ATTESTATION_SUBNET_COUNT}_beacon_attestation`. * For any current peer subscribed to the topic, the validator simply sends a `subscribe` message for the new topic. - * If an _insufficient_ number of current peers are subscribed to the topic, the validator must discover new peers on this topic. Via the discovery protocol, find peers with an ENR containing the `attnets` entry such that `ENR["attnets"][committee_index % ATTESTATION_SUBNET_COUNT] == True`. + * If an _insufficient_ number of current peers are subscribed to the topic, the validator must discover new peers on this topic. Via the discovery protocol, find peers with an ENR containing the `attnets` entry such that `ENR["attnets"][committee_index % ATTESTATION_SUBNET_COUNT] == True`. Then validate that the peers are still persisted on the desired topic by sending a `GetMetaData` and checking the resulting `attnets` field. ## Beacon chain responsibilities From 7af6a3afa494120f3a0e8828d3ee54636d0c3ada Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 25 Mar 2020 15:56:26 -0600 Subject: [PATCH 17/22] do not require non-aggregating validators to subscribe to attestation subnet for beacon committee duties --- specs/phase0/validator.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 54cd19a93..8d0e02144 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -200,9 +200,11 @@ The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahe Specifically a validator should: * Call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments. -* Join the pubsub topic -- `committee_index{committee_index % ATTESTATION_SUBNET_COUNT}_beacon_attestation`. - * For any current peer subscribed to the topic, the validator simply sends a `subscribe` message for the new topic. +* Find peers of the pubsub topic -- `committee_index{committee_index % ATTESTATION_SUBNET_COUNT}_beacon_attestation` -- and join if aggregator. * If an _insufficient_ number of current peers are subscribed to the topic, the validator must discover new peers on this topic. Via the discovery protocol, find peers with an ENR containing the `attnets` entry such that `ENR["attnets"][committee_index % ATTESTATION_SUBNET_COUNT] == True`. + * If the validator is assigned to be an aggregator for the slot (see `is_aggregator()`), then for any peer subscribed to the topic the validator sends a `subscribe` message for this topic. + +*Note*: If the validator is _not_ assigned to be an aggregator, the validator only needs sufficient number of peers on the topic to be able to publish messages. The validator does not need to _subscribe_ and listen to all messages on the topic. ## Beacon chain responsibilities From ee45cf77ef439df5b7ca47f52ff4f755e4902603 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 26 Mar 2020 07:47:20 -0600 Subject: [PATCH 18/22] proto feedback on MetaData --- specs/phase0/p2p-interface.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 0c436f057..4acd58bb2 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -214,6 +214,8 @@ Where - `seq_number` is a `uint64` starting at `0` used to version the node's metadata. If any other field in the local `MetaData` changes, the node MUST increment `seq_number` by 1. - `attnets` is a `Bitvector` representing the node's persistent attestation subnet subscriptions. +*Note*: `MetaData.seq_number` is used for versioning of the node's metadata, is entirely independent of the ENR sequence number, and will in most cases be out of sync with the ENR sequence number. + ## The gossip domain: gossipsub Clients MUST support the [gossipsub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) libp2p protocol. @@ -641,7 +643,7 @@ Response Content: ``` Sent intermittently, the `Ping` protocol checks liveness of connected peers. -Peers send and respond with their local metadata sequence number (`MetaData.seq_number`). +Peers request and respond with their local metadata sequence number (`MetaData.seq_number`). If the peer does not respond to the `Ping` request, the client MAY disconnect from the peer. From a83e7a5eccec74a83139a1a2a64669986e5ab220 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 26 Mar 2020 10:46:06 -0600 Subject: [PATCH 19/22] proto PR feedback --- specs/phase0/validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 8d0e02144..e97cf759c 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -200,9 +200,9 @@ The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahe Specifically a validator should: * Call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments. -* Find peers of the pubsub topic -- `committee_index{committee_index % ATTESTATION_SUBNET_COUNT}_beacon_attestation` -- and join if aggregator. +* Find peers of the pubsub topic `committee_index{committee_index % ATTESTATION_SUBNET_COUNT}_beacon_attestation`. * If an _insufficient_ number of current peers are subscribed to the topic, the validator must discover new peers on this topic. Via the discovery protocol, find peers with an ENR containing the `attnets` entry such that `ENR["attnets"][committee_index % ATTESTATION_SUBNET_COUNT] == True`. - * If the validator is assigned to be an aggregator for the slot (see `is_aggregator()`), then for any peer subscribed to the topic the validator sends a `subscribe` message for this topic. + * If the validator is assigned to be an aggregator for the slot (see `is_aggregator()`), then subscribe to the topic. *Note*: If the validator is _not_ assigned to be an aggregator, the validator only needs sufficient number of peers on the topic to be able to publish messages. The validator does not need to _subscribe_ and listen to all messages on the topic. From d42e08a6e1157108b4127c0166c387fab1a083dc Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 26 Mar 2020 11:43:32 -0600 Subject: [PATCH 20/22] verify proposer sig before proposer shuffling in p2p block gossip conditions --- specs/phase0/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index d53056f5b..841432efd 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -281,9 +281,9 @@ There are two primary global topics used to propagate beacon blocks and aggregat - `beacon_block` - This topic is used solely for propagating new signed beacon blocks to all nodes on the networks. Signed blocks are sent in their entirety. The following validations MUST pass before forwarding the `signed_beacon_block` on the network - The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot <= current_slot` (a client MAY queue future blocks for processing at the appropriate slot). - The block is from a slot greater than the latest finalized slot -- i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)` (a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc). - - The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated. - The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`. - - The proposer signature, `signed_beacon_block.signature`, is valid. + - The proposer signature, `signed_beacon_block.signature`, is valid with respect to the `proposer_index` pubkey. + - The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated. - `beacon_aggregate_and_proof` - This topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. (We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message` and `aggregate = aggregate_and_proof.aggregate`) - `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot` (a client MAY queue future aggregates for processing at the appropriate slot). - The aggregate attestation defined by `hash_tree_root(aggregate)` has _not_ already been seen (via aggregate gossip, within a block, or through the creation of an equivalent aggregate locally). From 68cc4c48478fc52b69365202cef27bf56e329a34 Mon Sep 17 00:00:00 2001 From: Martin Lundfall Date: Fri, 27 Mar 2020 14:02:56 +0100 Subject: [PATCH 21/22] Fix test doc links --- tests/generators/epoch_processing/README.md | 2 +- tests/generators/operations/README.md | 2 +- tests/generators/sanity/README.md | 2 +- tests/generators/ssz_static/README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/generators/epoch_processing/README.md b/tests/generators/epoch_processing/README.md index 9b57875e2..662b0b516 100644 --- a/tests/generators/epoch_processing/README.md +++ b/tests/generators/epoch_processing/README.md @@ -5,7 +5,7 @@ Epoch processing covers the sub-transitions during an epoch change. An epoch-processing test-runner can consume these sub-transition test-suites, and handle different kinds of epoch sub-transitions by processing the cases using the specified test handler. -Information on the format of the tests can be found in the [epoch-processing test formats documentation](../../specs/test_formats/epoch_processing/README.md). +Information on the format of the tests can be found in the [epoch-processing test formats documentation](../../formats/epoch_processing/README.md). diff --git a/tests/generators/operations/README.md b/tests/generators/operations/README.md index 5cb3afc98..a5d48c11b 100644 --- a/tests/generators/operations/README.md +++ b/tests/generators/operations/README.md @@ -6,7 +6,7 @@ Operations (or "transactions" in previous spec iterations), An operation test-runner can consume these operation test-suites, and handle different kinds of operations by processing the cases using the specified test handler. -Information on the format of the tests can be found in the [operations test formats documentation](../../specs/test_formats/operations/README.md). +Information on the format of the tests can be found in the [operations test formats documentation](../../formats/operations/README.md). diff --git a/tests/generators/sanity/README.md b/tests/generators/sanity/README.md index 6d2e2f30d..cbc6aef06 100644 --- a/tests/generators/sanity/README.md +++ b/tests/generators/sanity/README.md @@ -2,7 +2,7 @@ Sanity tests cover regular state-transitions in a common block-list format, to ensure the basics work. -Information on the format of the tests can be found in the [sanity test formats documentation](../../specs/test_formats/sanity/README.md). +Information on the format of the tests can be found in the [sanity test formats documentation](../../formats/sanity/README.md). diff --git a/tests/generators/ssz_static/README.md b/tests/generators/ssz_static/README.md index 2a5040192..160d1ebb4 100644 --- a/tests/generators/ssz_static/README.md +++ b/tests/generators/ssz_static/README.md @@ -3,4 +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 Eth2 data types. -Test-format documentation can be found [here](../../specs/test_formats/ssz_static/README.md). +Test-format documentation can be found [here](../../formats/ssz_static/README.md). From ba67165da23aa978dbe91c21be4970e4965a5889 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 27 Mar 2020 12:24:43 -0600 Subject: [PATCH 22/22] bump version to 0.11.1 --- tests/core/pyspec/eth2spec/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index 142464bf2..027934ea1 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -0.11.0 \ No newline at end of file +0.11.1 \ No newline at end of file