From 7f39f79b2e72654920b2e12127cfdfe6ad0088c6 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 31 Jan 2019 07:55:27 -0800 Subject: [PATCH 01/57] Use 2*63 for the genesis slot --- 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 d93cb21d71..952f0f9d09 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -199,7 +199,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | Name | Value | | - | - | | `GENESIS_FORK_VERSION` | `0` | -| `GENESIS_SLOT` | `2**19` | +| `GENESIS_SLOT` | `2**63` | | `GENESIS_EPOCH` | `slot_to_epoch(GENESIS_SLOT)` | | `GENESIS_START_SHARD` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | From 5488e7b6a4d20e88072953e4ca2432f3c206e72c Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 31 Jan 2019 10:12:43 -0600 Subject: [PATCH 02/57] SSZ list Merkle hashing change The current spec is arguably inconsistent, in that if a set of N values gets chunked into M chunks where M is not an exact power of 2, the chunks between M and next_power_of_2(M) are filled with SSZ_CHUNK_SIZE zero bytes each, but the last chunk is not padded, and could be arbitrarily short (eg. if the values are 4 bytes and there are 257 of them, then that gets serialized into eight chunks chunks where the first four are 64 values each, the fifth is 4 bytes corresponding to the last value, and the last three chunks are SSZ_CHUNK_SIZE zero bytes). This PR fills every chunk up to exactly SSZ_CHUNK_SIZE bytes for consistency. --- specs/simple-serialize.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 13cc472995..2296a47c12 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -385,7 +385,7 @@ Return the hash of the serialization of the value. #### List/Vectors -First, we define some helpers and then the Merkle tree function. +First, we define some helpers and then the Merkle tree function. `zpad(input: bytes, length: int) -> bytes` is a helper that extends the given `bytes` value to the desired `length` by adding zero bytes on the right. ```python # Merkle tree hash of a list of homogenous, non-empty items @@ -401,7 +401,7 @@ def merkle_hash(lst): items_per_chunk = SSZ_CHUNK_SIZE // len(lst[0]) # Build a list of chunks based on the number of items in the chunk - chunkz = [b''.join(lst[i:i+items_per_chunk]) for i in range(0, len(lst), items_per_chunk)] + chunkz = [zpad(b''.join(lst[i:i+items_per_chunk]), SSZ_CHUNK_SIZE) for i in range(0, len(lst), items_per_chunk)] else: # Leave large items alone chunkz = lst From 9271e6e31878682888bb051ced9a3c8920d1fb1c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 31 Jan 2019 17:47:09 -0600 Subject: [PATCH 03/57] Update specs/simple-serialize.md Co-Authored-By: vbuterin --- specs/simple-serialize.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 2296a47c12..2838c876c1 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -401,7 +401,10 @@ def merkle_hash(lst): items_per_chunk = SSZ_CHUNK_SIZE // len(lst[0]) # Build a list of chunks based on the number of items in the chunk - chunkz = [zpad(b''.join(lst[i:i+items_per_chunk]), SSZ_CHUNK_SIZE) for i in range(0, len(lst), items_per_chunk)] + chunkz = [ + zpad(b''.join(lst[i:i + items_per_chunk]), SSZ_CHUNK_SIZE) + for i in range(0, len(lst), items_per_chunk) + ] else: # Leave large items alone chunkz = lst From acb432ef3ac77dde854f89949ca8b4e9320de490 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 31 Jan 2019 17:49:51 -0600 Subject: [PATCH 04/57] Update specs/simple-serialize.md Co-Authored-By: vbuterin --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 2838c876c1..1820768b31 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -385,7 +385,7 @@ Return the hash of the serialization of the value. #### List/Vectors -First, we define some helpers and then the Merkle tree function. `zpad(input: bytes, length: int) -> bytes` is a helper that extends the given `bytes` value to the desired `length` by adding zero bytes on the right. +First, we define some helpers and then the Merkle tree function. `def zpad(input: bytes, length: int) -> bytes: return input + b'\x00' * (length - len(input))` is a helper that extends the given `bytes` value to the desired `length` by adding zero bytes on the right. ```python # Merkle tree hash of a list of homogenous, non-empty items From 8ffd9adcaffbb0176471ee5664e62e6fe2d4eaf7 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 31 Jan 2019 18:03:23 -0600 Subject: [PATCH 05/57] Hash_tree_root -> hash_tree_root_internal Clarifies the distinction between "internal" hash roots (may be < 32 bytes for trivial objects) and "external" ones (zpadded to 32). --- specs/simple-serialize.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 13cc472995..d37441ebe2 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -371,7 +371,12 @@ return typ(**values), item_index ### Tree Hash -The below `hash_tree_root` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For the final output only (ie. not intermediate outputs), if the output is less than 32 bytes, right-zero-pad it to 32 bytes. The goal is collision resistance *within* each type, not between types. +The below `hash_tree_root_internal` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For use as a "final output" (eg. for signing), use `hash_tree_root(x) = zpad(hash_tree_root_internal(x), 32)`, where `zpad` is a helper that extends the given `bytes` value to the desired `length` by adding zero bytes on the right: + +``` +def zpad(input: bytes, length: int) -> bytes: + return input + b'\x00' * (length - len(input))` +``` Refer to [the helper function `hash`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#hash) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. @@ -416,20 +421,20 @@ def merkle_hash(lst): return hash(chunkz[0] + datalen) ``` -To `hash_tree_root` a list, we simply do: +To `hash_tree_root_internal` a list, we simply do: ```python -return merkle_hash([hash_tree_root(item) for item in value]) +return merkle_hash([hash_tree_root_internal(item) for item in value]) ``` -Where the inner `hash_tree_root` is a recursive application of the tree-hashing function (returning less than 32 bytes for short single values). +Where the inner `hash_tree_root_internal` is a recursive application of the tree-hashing function (returning less than 32 bytes for short single values). #### Container Recursively tree hash the values in the container in the same order as the fields, and return the hash of the concatenation of the results. ```python -return hash(b''.join([hash_tree_root(getattr(x, field)) for field in value.fields])) +return hash(b''.join([hash_tree_root_internal(getattr(x, field)) for field in value.fields])) ``` ## Implementations From dd197e46a6a2e147b1760a5f4a8730a4c56f0d18 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Feb 2019 22:31:00 +0800 Subject: [PATCH 06/57] Fix `zpad` --- specs/simple-serialize.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index d37441ebe2..b609aa1000 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -373,9 +373,9 @@ return typ(**values), item_index The below `hash_tree_root_internal` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For use as a "final output" (eg. for signing), use `hash_tree_root(x) = zpad(hash_tree_root_internal(x), 32)`, where `zpad` is a helper that extends the given `bytes` value to the desired `length` by adding zero bytes on the right: -``` +```python def zpad(input: bytes, length: int) -> bytes: - return input + b'\x00' * (length - len(input))` + return input + b'\x00' * (length - len(input)) ``` Refer to [the helper function `hash`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#hash) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. From e0867c030f65e18781fddf6295b879d958aecb88 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Feb 2019 22:34:10 +0800 Subject: [PATCH 07/57] Fix typo --- 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 d93cb21d71..1404f51907 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1660,7 +1660,7 @@ Below are the processing steps that happen at every slot. #### Block roots -* Let `previous_block_root` be the `tree_hash_root` of the previous beacon block processed in the chain. +* Let `previous_block_root` be the `hash_tree_root` of the previous beacon block processed in the chain. * Set `state.latest_block_roots[(state.slot - 1) % LATEST_BLOCK_ROOTS_LENGTH] = previous_block_root`. * If `state.slot % LATEST_BLOCK_ROOTS_LENGTH == 0` append `merkle_root(state.latest_block_roots)` to `state.batched_block_roots`. From 0b8ccf1e64f97326f360de7b48b1ae871b2f992c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 1 Feb 2019 23:45:09 +0800 Subject: [PATCH 08/57] Update 0_beacon-chain.md (#547) --- specs/core/0_beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d93cb21d71..13f4801718 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -940,7 +940,6 @@ def generate_seed(state: BeaconState, """ Generate a seed for the given ``epoch``. """ - return hash( get_randao_mix(state, epoch - SEED_LOOKAHEAD) + get_active_index_root(state, epoch) @@ -1000,7 +999,7 @@ def get_attestation_participants(state: BeaconState, ### `is_power_of_two` -``` +```python def is_power_of_two(value: int) -> bool: """ Check if ``value`` is a power of two integer. From 378e1ba9a681be900e86fd87e9729c657aceb8cf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 2 Feb 2019 02:06:53 +0800 Subject: [PATCH 09/57] Misc fixes of `get_next_epoch_crosslink_committees` --- specs/core/0_beacon-chain.md | 2 +- specs/validator/0_beacon-chain-validator.md | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d93cb21d71..dbc8db0e98 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -833,7 +833,7 @@ def get_next_epoch_committee_count(state: BeaconState) -> int: ```python def get_crosslink_committees_at_slot(state: BeaconState, slot: SlotNumber, - registry_change=False: bool) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: + registry_change: bool=False) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: """ Return the list of ``(committee, shard)`` tuples for the ``slot``. diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index b01e7a3af4..3df3d758d7 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -345,15 +345,23 @@ Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is determi ```python def get_next_epoch_crosslink_committees(state: BeaconState, - validator_index: ValidatorIndex) -> List[Tuple[ValidatorIndex], ShardNumber]: + validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) potential_committees = [] - for validator_registry in [False, True]: + for registry_change in [False, True]: for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): - shard_committees = get_crosslink_committees_at_slot(state, slot, validator_registry) - selected_committees = [committee for committee in shard_committees if validator_index in committee[0]] + crosslink_committees = get_crosslink_committees_at_slot( + state, + slot, + registry_change=registry_change, + ) + selected_committees = [ + committee # type: Tuple[List[ValidatorIndex], ShardNumber] + for committee in crosslink_committees + if validator_index in committee[0] + ] if len(selected_committees) > 0: potential_assignments.append(selected_committees) break From d53d333733acc187f4257cc2dcc420b2b05e8650 Mon Sep 17 00:00:00 2001 From: Gregory Markou Date: Fri, 1 Feb 2019 14:04:17 -0800 Subject: [PATCH 10/57] fix startup typo --- 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 d93cb21d71..1e33e7e877 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1552,7 +1552,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, is_genesis=True) - genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) + genesis_active_index_root = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH)) for index in range(LATEST_INDEX_ROOTS_LENGTH): state.latest_index_roots[index] = genesis_active_index_root state.current_epoch_seed = generate_seed(state, GENESIS_EPOCH) @@ -1737,7 +1737,7 @@ For each `attestation` in `block.body.attestations`: ```python assert attestation.custody_bitfield == b'\x00' * len(attestation.custody_bitfield) # [TO BE REMOVED IN PHASE 1] assert attestation.aggregation_bitfield != b'\x00' * len(attestation.aggregation_bitfield) - + crosslink_committee = [ committee for committee, shard in get_crosslink_committees_at_slot(state, attestation.data.slot) if shard == attestation.data.shard From 7e7e5e27956556afceb01970ac9e8393a4ee930f Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 1 Feb 2019 17:06:04 -0800 Subject: [PATCH 11/57] Removes the source of the Vyper contract from the spec. This change allows for easier maintenance of the code and the spec by uncoupling them. Before any edit to either document resulted in having to synchronize the other. By adding a reference to the canonical repo for the code we avoid having to maintain a duplicate copy here. --- specs/core/0_beacon-chain.md | 83 +++--------------------------------- 1 file changed, 6 insertions(+), 77 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8a83149ffe..ad904332b0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1378,86 +1378,15 @@ When sufficiently many full deposits have been made the deposit contract emits t ### Vyper code -```python -## compiled with v0.1.0-beta.6 ## - -MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei -MAX_DEPOSIT_AMOUNT: constant(uint256) = 32000000000 # Gwei -GWEI_PER_ETH: constant(uint256) = 1000000000 # 10**9 -CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant(uint256) = 16384 # 2**14 -DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32 -TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32 -SECONDS_PER_DAY: constant(uint256) = 86400 - -Deposit: event({deposit_root: bytes32, data: bytes[528], merkle_tree_index: bytes[8], branch: bytes32[32]}) -ChainStart: event({deposit_root: bytes32, time: bytes[8]}) - -zerohashes: bytes32[32] -branch: bytes32[32] -deposit_count: uint256 -full_deposit_count: uint256 -chainStarted: public(bool) - -@public -def __init__(): - for i in range(31): - self.zerohashes[i+1] = sha3(concat(self.zerohashes[i], self.zerohashes[i])) - self.branch[i+1] = self.zerohashes[i+1] - -@public -@constant -def get_deposit_root() -> bytes32: - root:bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000 - size:uint256 = self.deposit_count - for h in range(32): - if size % 2 == 1: - root = sha3(concat(self.branch[h], root)) - else: - root = sha3(concat(root, self.zerohashes[h])) - size /= 2 - return root - -@payable -@public -def deposit(deposit_input: bytes[512]): - assert msg.value >= as_wei_value(MIN_DEPOSIT_AMOUNT, "gwei") - assert msg.value <= as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei") - - index: uint256 = self.deposit_count - deposit_amount: bytes[8] = slice(concat("", convert(msg.value / GWEI_PER_ETH, bytes32)), start=24, len=8) - deposit_timestamp: bytes[8] = slice(concat("", convert(block.timestamp, bytes32)), start=24, len=8) - deposit_data: bytes[528] = concat(deposit_amount, deposit_timestamp, deposit_input) - merkle_tree_index: bytes[8] = slice(concat("", convert(index, bytes32)), start=24, len=8) - - # add deposit to merkle tree - i: int128 = 0 - power_of_two: uint256 = 2 - for _ in range(32): - if (index+1) % power_of_two != 0: - break - i += 1 - power_of_two *= 2 - value:bytes32 = sha3(deposit_data) - for j in range(32): - if j < i: - value = sha3(concat(self.branch[j], value)) - self.branch[i] = value - - self.deposit_count += 1 +The source for the Vyper contract lives in a separate repository at [https://github.com/ethereum/deposit_contract](https://github.com/ethereum/deposit_contract). - new_deposit_root:bytes32 = self.get_deposit_root() - log.Deposit(new_deposit_root, deposit_data, merkle_tree_index, self.branch) +Note: to save ~10x on gas this contract uses a somewhat unintuitive progressive Merkle root calculation algo that requires only O(log(n)) storage. See https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py for an implementation of the same algo in python tested for correctness. - if msg.value == as_wei_value(MAX_DEPOSIT_AMOUNT, "gwei"): - self.full_deposit_count += 1 - if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD: - timestamp_day_boundary: uint256 = as_unitless_number(block.timestamp) - as_unitless_number(block.timestamp) % SECONDS_PER_DAY + SECONDS_PER_DAY - chainstart_time: bytes[8] = slice(concat("", convert(timestamp_day_boundary, bytes32)), start=24, len=8) - log.ChainStart(new_deposit_root, chainstart_time) - self.chainStarted = True -``` +For convenience, we provide the interface to the contract here: -Note: to save ~10x on gas this contract uses a somewhat unintuitive progressive Merkle root calculation algo that requires only O(log(n)) storage. See https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py for an implementation of the same algo in python tested for correctness. +* `__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. ## On startup From 8d82ee8ce7ea1d3d3bb166683cd497e115cc852b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 1 Feb 2019 21:02:09 -0800 Subject: [PATCH 12/57] do not mix in epoch to seed in get_shuffling. add epoch to generate_seed --- 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 8a83149ffe..1ee3596e81 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -778,7 +778,6 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle - seed = xor(seed, int_to_bytes32(epoch)) shuffled_active_validator_indices = shuffle(active_validator_indices, seed) # Split the shuffled list into committees_per_epoch pieces @@ -942,7 +941,8 @@ def generate_seed(state: BeaconState, """ return hash( get_randao_mix(state, epoch - SEED_LOOKAHEAD) + - get_active_index_root(state, epoch) + get_active_index_root(state, epoch) + + int_to_bytes32(epoch) ) ``` From a781eb5a4f22e2b4b74c509f44d6c210f5f6bb87 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sat, 2 Feb 2019 06:09:45 +0100 Subject: [PATCH 13/57] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 8a83149ffe..6f10ad1f9c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -821,6 +821,9 @@ def get_current_epoch_committee_count(state: BeaconState) -> int: ```python def get_next_epoch_committee_count(state: BeaconState) -> int: + """ + Return the number of committees in the next epoch of the given ``state``. + """ next_active_validators = get_active_validator_indices( state.validator_registry, get_current_epoch(state) + 1, From f3d47e26fc30f87f09b008f328545c81ad403bdf Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Sat, 2 Feb 2019 13:49:52 +0100 Subject: [PATCH 14/57] `shard` -> `attestation.data.shard` --- 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 d93cb21d71..fa963f8a84 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1731,7 +1731,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. -* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`. +* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[attestation.data.shard].shard_block_root`. * Verify bitfields and aggregate signature: ```python From 650f4a20dfa25c6aa85a64dfe21aca5de75f8eeb Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 2 Feb 2019 13:16:39 -0800 Subject: [PATCH 15/57] clarify eth1 that there will be at most 1 --- 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 caaac363f6..1c7573453f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1688,7 +1688,7 @@ Below are the processing steps that happen at every `block`. #### Eth1 data -* If `block.eth1_data` equals `eth1_data_vote.eth1_data` for some `eth1_data_vote` in `state.eth1_data_votes`, set `eth1_data_vote.vote_count += 1`. +* If there exists an `eth1_data_vote` in `states.eth1_data_votes` for which `eth1_data_vote.eth1_data == block.eth1_data` (there will be at most one), set `eth1_data_vote.vote_count += 1`. * Otherwise, append to `state.eth1_data_votes` a new `Eth1DataVote(eth1_data=block.eth1_data, vote_count=1)`. #### Operations From 2a32e7f6653959da1f21e2812426cb9d87baf24a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 2 Feb 2019 16:41:59 -0800 Subject: [PATCH 16/57] convert int_to_bytes to little endian --- specs/core/0_beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index caaac363f6..f67afd8a67 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -710,8 +710,8 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: if remaining == 1: break - # Read 3-bytes of `source` as a 24-bit big-endian integer. - sample_from_source = int.from_bytes(source[position:position + rand_bytes], 'big') + # Read 3-bytes of `source` as a 24-bit little-endian integer. + sample_from_source = int.from_bytes(source[position:position + rand_bytes], 'little') # Sample values greater than or equal to `sample_max` will cause # modulo bias when mapped into the `remaining` range. @@ -1015,7 +1015,7 @@ def is_power_of_two(value: int) -> bool: ### `int_to_bytes1`, `int_to_bytes2`, ... -`int_to_bytes1(x): return x.to_bytes(1, 'big')`, `int_to_bytes2(x): return x.to_bytes(2, 'big')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96. +`int_to_bytes1(x): return x.to_bytes(1, 'little')`, `int_to_bytes2(x): return x.to_bytes(2, 'little')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96. ### `get_effective_balance` From e4c4c04e52bde69edf85d36726cbfa3b9aa9fae7 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sun, 3 Feb 2019 10:36:21 +0100 Subject: [PATCH 17/57] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 48075c8d4f..5c1d158b51 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -54,6 +54,7 @@ - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - [`slot_to_epoch`](#slot_to_epoch) + - [`get_previous_epoch`](#get_previous_epoch) - [`get_current_epoch`](#get_current_epoch) - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) @@ -639,6 +640,18 @@ def slot_to_epoch(slot: SlotNumber) -> EpochNumber: return slot // EPOCH_LENGTH ``` +### `get_previous_epoch` + +```python +def get_previous_epoch(state: BeaconState) -> EpochNumber: + """` + Return the previous epoch of the given ``state``. + """ + if slot_to_epoch(state.slot) > GENESIS_EPOCH: + return slot_to_epoch(state.slot) - 1 + return slot_to_epoch(state.slot) +``` + ### `get_current_epoch` ```python @@ -844,7 +857,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, """ epoch = slot_to_epoch(slot) current_epoch = get_current_epoch(state) - previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch + previous_epoch = get_previous_epoch(state) next_epoch = current_epoch + 1 assert previous_epoch <= epoch <= next_epoch @@ -1827,7 +1840,7 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. #### Helpers * Let `current_epoch = get_current_epoch(state)`. -* Let `previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch`. +* Let `previous_epoch = get_previous_epoch(state)`. * Let `next_epoch = current_epoch + 1`. [Validators](#dfn-Validator) attesting during the current epoch: From 969896b0a83bed0e1f412556ff4027f85983fb01 Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Sun, 3 Feb 2019 11:14:02 +0100 Subject: [PATCH 18/57] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 48075c8d4f..b6c4d51f3c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -76,6 +76,7 @@ - [`is_power_of_two`](#is_power_of_two) - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) - [`get_effective_balance`](#get_effective_balance) + - [`total_balance`](#total_balance) - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - [`get_bitfield_bit`](#get_bitfield_bit) @@ -1027,6 +1028,16 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: return min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT) ``` +### `total_balance` + +```python +def total_balance(state: State, validators: [ValidatorIndex]) -> Gwei: + """ + Return the combined effective balance of an array of validators. + """ + sum([get_effective_balance(state, i) for i in validators]) +``` + ### `get_fork_version` ```python @@ -1832,31 +1843,31 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. [Validators](#dfn-Validator) attesting during the current epoch: -* Let `current_total_balance = sum([get_effective_balance(state, i) for i in get_active_validator_indices(state.validator_registry, current_epoch)])`. +* Let `current_total_balance = total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch))`. * Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: this is the set of attestations of slots in the epoch `current_epoch`, _not_ attestations that got included in the chain during the epoch `current_epoch`.) * Validators justifying the epoch boundary block at the start of the current epoch: * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(current_epoch)) and a.data.justified_epoch == state.justified_epoch]`. * Let `current_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_boundary_attestations]`. - * Let `current_epoch_boundary_attesting_balance = sum([get_effective_balance(state, i) for i in current_epoch_boundary_attester_indices])`. + * Let `current_epoch_boundary_attesting_balance = total_balance(state, current_epoch_boundary_attester_indices)`. [Validators](#dfn-Validator) attesting during the previous epoch: -* Let `previous_total_balance = sum([get_effective_balance(state, i) for i in get_active_validator_indices(state.validator_registry, previous_epoch)])`. +* Let `previous_total_balance = total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch))`. * Validators that made an attestation during the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_attestations]`. * Validators targeting the previous justified slot: * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.data.justified_epoch == state.previous_justified_epoch]`. * Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_justified_attestations]`. - * Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(state, i) for i in previous_epoch_justified_attester_indices])`. + * Let `previous_epoch_justified_attesting_balance = total_balance(state, previous_epoch_justified_attester_indices)`. * Validators justifying the epoch boundary block at the start of the previous epoch: * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(previous_epoch))]`. * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_boundary_attestations]`. - * Let `previous_epoch_boundary_attesting_balance = sum([get_effective_balance(state, i) for i in previous_epoch_boundary_attester_indices])`. + * Let `previous_epoch_boundary_attesting_balance = total_balance(state, previous_epoch_boundary_attester_indices)`. * Validators attesting to the expected beacon chain head during the previous epoch: * Let `previous_epoch_head_attestations = [a for a in previous_epoch_attestations if a.data.beacon_block_root == get_block_root(state, a.data.slot)]`. * Let `previous_epoch_head_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_head_attestations]`. - * Let `previous_epoch_head_attesting_balance = sum([get_effective_balance(state, i) for i in previous_epoch_head_attester_indices])`. + * Let `previous_epoch_head_attesting_balance = total_balance(state, previous_epoch_head_attester_indices)`. **Note**: `previous_total_balance` and `previous_epoch_boundary_attesting_balance` balance might be marginally different than the actual balances during previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. @@ -1864,10 +1875,9 @@ For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_s * Let `shard_block_root` be `state.latest_crosslinks[shard].shard_block_root` * Let `attesting_validator_indices(crosslink_committee, shard_block_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_attestations + previous_epoch_attestations if a.data.shard == shard and a.data.shard_block_root == shard_block_root]`. -* Let `winning_root(crosslink_committee)` be equal to the value of `shard_block_root` such that `sum([get_effective_balance(state, i) for i in attesting_validator_indices(crosslink_committee, shard_block_root)])` is maximized (ties broken by favoring lower `shard_block_root` values). +* Let `winning_root(crosslink_committee)` be equal to the value of `shard_block_root` such that `total_balance(state, attesting_validator_indices(crosslink_committee, shard_block_root))` is maximized (ties broken by favoring lower `shard_block_root` values). * Let `attesting_validators(crosslink_committee)` be equal to `attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))` for convenience. -* Let `total_attesting_balance(crosslink_committee) = sum([get_effective_balance(state, i) for i in attesting_validators(crosslink_committee)])`. -* Let `total_balance(crosslink_committee) = sum([get_effective_balance(state, i) for i in crosslink_committee])`. +* Let `total_attesting_balance(crosslink_committee) = total_balance(state, attesting_validators(crosslink_committee))`. Define the following helpers to process attestation inclusion rewards and inclusion distance reward/penalty. For every attestation `a` in `previous_epoch_attestations`: @@ -1997,7 +2007,7 @@ def update_validator_registry(state: BeaconState) -> None: # The active validators active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators - total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) + total_balance = total_balance(state, active_validator_indices) # The maximum balance churn in Gwei (for deposits and exits separately) max_balance_churn = max( From 8e16d122be108af8b92f31ca5a014bb9ae99fa97 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Feb 2019 11:42:12 +0100 Subject: [PATCH 19/57] Update specs/core/0_beacon-chain.md Co-Authored-By: decanus --- 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 b6c4d51f3c..e838b23c14 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1031,7 +1031,7 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: ### `total_balance` ```python -def total_balance(state: State, validators: [ValidatorIndex]) -> Gwei: +def total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: """ Return the combined effective balance of an array of validators. """ From 5c56751cc9cf51e37329c33a5a9c0bd4d87bbfbf Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Sun, 3 Feb 2019 11:43:33 +0100 Subject: [PATCH 20/57] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e838b23c14..71426790ed 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -76,7 +76,7 @@ - [`is_power_of_two`](#is_power_of_two) - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) - [`get_effective_balance`](#get_effective_balance) - - [`total_balance`](#total_balance) + - [`get_total_balance`](#get_total_balance) - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - [`get_bitfield_bit`](#get_bitfield_bit) @@ -1028,10 +1028,10 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: return min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT) ``` -### `total_balance` +### `get_total_balance` ```python -def total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: +def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: """ Return the combined effective balance of an array of validators. """ @@ -1843,31 +1843,31 @@ The steps below happen when `(state.slot + 1) % EPOCH_LENGTH == 0`. [Validators](#dfn-Validator) attesting during the current epoch: -* Let `current_total_balance = total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch))`. +* Let `current_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, current_epoch))`. * Let `current_epoch_attestations = [a for a in state.latest_attestations if current_epoch == slot_to_epoch(a.data.slot)]`. (Note: this is the set of attestations of slots in the epoch `current_epoch`, _not_ attestations that got included in the chain during the epoch `current_epoch`.) * Validators justifying the epoch boundary block at the start of the current epoch: * Let `current_epoch_boundary_attestations = [a for a in current_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(current_epoch)) and a.data.justified_epoch == state.justified_epoch]`. * Let `current_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_boundary_attestations]`. - * Let `current_epoch_boundary_attesting_balance = total_balance(state, current_epoch_boundary_attester_indices)`. + * Let `current_epoch_boundary_attesting_balance = get_total_balance(state, current_epoch_boundary_attester_indices)`. [Validators](#dfn-Validator) attesting during the previous epoch: -* Let `previous_total_balance = total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch))`. +* Let `previous_total_balance = get_total_balance(state, get_active_validator_indices(state.validator_registry, previous_epoch))`. * Validators that made an attestation during the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if previous_epoch == slot_to_epoch(a.data.slot)]`. * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_attestations]`. * Validators targeting the previous justified slot: * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.data.justified_epoch == state.previous_justified_epoch]`. * Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_justified_attestations]`. - * Let `previous_epoch_justified_attesting_balance = total_balance(state, previous_epoch_justified_attester_indices)`. + * Let `previous_epoch_justified_attesting_balance = get_total_balance(state, previous_epoch_justified_attester_indices)`. * Validators justifying the epoch boundary block at the start of the previous epoch: * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_attestations if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(previous_epoch))]`. * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_boundary_attestations]`. - * Let `previous_epoch_boundary_attesting_balance = total_balance(state, previous_epoch_boundary_attester_indices)`. + * Let `previous_epoch_boundary_attesting_balance = get_total_balance(state, previous_epoch_boundary_attester_indices)`. * Validators attesting to the expected beacon chain head during the previous epoch: * Let `previous_epoch_head_attestations = [a for a in previous_epoch_attestations if a.data.beacon_block_root == get_block_root(state, a.data.slot)]`. * Let `previous_epoch_head_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in previous_epoch_head_attestations]`. - * Let `previous_epoch_head_attesting_balance = total_balance(state, previous_epoch_head_attester_indices)`. + * Let `previous_epoch_head_attesting_balance = get_total_balance(state, previous_epoch_head_attester_indices)`. **Note**: `previous_total_balance` and `previous_epoch_boundary_attesting_balance` balance might be marginally different than the actual balances during previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. @@ -1875,9 +1875,9 @@ For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_s * Let `shard_block_root` be `state.latest_crosslinks[shard].shard_block_root` * Let `attesting_validator_indices(crosslink_committee, shard_block_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.aggregation_bitfield) for a in current_epoch_attestations + previous_epoch_attestations if a.data.shard == shard and a.data.shard_block_root == shard_block_root]`. -* Let `winning_root(crosslink_committee)` be equal to the value of `shard_block_root` such that `total_balance(state, attesting_validator_indices(crosslink_committee, shard_block_root))` is maximized (ties broken by favoring lower `shard_block_root` values). +* Let `winning_root(crosslink_committee)` be equal to the value of `shard_block_root` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, shard_block_root))` is maximized (ties broken by favoring lower `shard_block_root` values). * Let `attesting_validators(crosslink_committee)` be equal to `attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))` for convenience. -* Let `total_attesting_balance(crosslink_committee) = total_balance(state, attesting_validators(crosslink_committee))`. +* Let `total_attesting_balance(crosslink_committee) = get_total_balance(state, attesting_validators(crosslink_committee))`. Define the following helpers to process attestation inclusion rewards and inclusion distance reward/penalty. For every attestation `a` in `previous_epoch_attestations`: @@ -1916,7 +1916,7 @@ Finally, update the following: For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: -* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * total_balance(crosslink_committee)`. +* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. #### Rewards and penalties @@ -1964,7 +1964,7 @@ For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_s * Let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. * For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`: - * If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // total_balance(crosslink_committee))`. + * If `index in attesting_validators(crosslink_committee)`, `state.validator_balances[index] += base_reward(state, index) * total_attesting_balance(crosslink_committee) // get_total_balance(state, crosslink_committee))`. * If `index not in attesting_validators(crosslink_committee)`, `state.validator_balances[index] -= base_reward(state, index)`. #### Ejections @@ -2007,7 +2007,7 @@ def update_validator_registry(state: BeaconState) -> None: # The active validators active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators - total_balance = total_balance(state, active_validator_indices) + total_balance = get_total_balance(state, active_validator_indices) # The maximum balance churn in Gwei (for deposits and exits separately) max_balance_churn = max( From 3a6da9839a64a7edc951bef541b47a65554bfed8 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 3 Feb 2019 14:27:09 +0100 Subject: [PATCH 21/57] Added a note for underflow. Thanks Hsiao Wei! : ) Co-Authored-By: terenc3t --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 5c1d158b51..b8815388d3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -646,6 +646,7 @@ def slot_to_epoch(slot: SlotNumber) -> EpochNumber: def get_previous_epoch(state: BeaconState) -> EpochNumber: """` Return the previous epoch of the given ``state``. + If the current epoch is ``GENESIS_EPOCH``, return ``GENESIS_EPOCH``. """ if slot_to_epoch(state.slot) > GENESIS_EPOCH: return slot_to_epoch(state.slot) - 1 From 197fa188936fb21289e86a7e4c88ff5b61815e93 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 3 Feb 2019 14:26:07 -0600 Subject: [PATCH 22/57] Zpad is already in #543 --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 1820768b31..6976e9cbed 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -385,7 +385,7 @@ Return the hash of the serialization of the value. #### List/Vectors -First, we define some helpers and then the Merkle tree function. `def zpad(input: bytes, length: int) -> bytes: return input + b'\x00' * (length - len(input))` is a helper that extends the given `bytes` value to the desired `length` by adding zero bytes on the right. +First, we define the Merkle tree function. ```python # Merkle tree hash of a list of homogenous, non-empty items From 8c5868cbe8603c61b54126599aa1fcdbf7243f52 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 3 Feb 2019 15:20:49 -0800 Subject: [PATCH 23/57] Extend `deposit` API with note about bounds on acceptable values --- 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 ad904332b0..02804ab6ff 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1386,7 +1386,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. +* `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. ## On startup From 8755dc34d51ac88c3cd35b76cc8ef9544abb294c Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 3 Feb 2019 15:22:03 -0800 Subject: [PATCH 24/57] Add direct link to validator registration 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 02804ab6ff..016d773e4b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1378,7 +1378,7 @@ When sufficiently many full deposits have been made the deposit contract emits t ### Vyper code -The source for the Vyper contract lives in a separate repository at [https://github.com/ethereum/deposit_contract](https://github.com/ethereum/deposit_contract). +The source for the Vyper contract lives in a [separate repository](https://github.com/ethereum/deposit_contract) at [https://github.com/ethereum/deposit_contract/blob/master/deposit_contract/contracts/validator_registration.v.py](https://github.com/ethereum/deposit_contract/blob/master/deposit_contract/contracts/validator_registration.v.py). Note: to save ~10x on gas this contract uses a somewhat unintuitive progressive Merkle root calculation algo that requires only O(log(n)) storage. See https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py for an implementation of the same algo in python tested for correctness. From 147ee2f991f513c0962f5b610f431f86b92a0f88 Mon Sep 17 00:00:00 2001 From: Taras Bobrovytsky Date: Mon, 4 Feb 2019 01:49:16 -0600 Subject: [PATCH 25/57] Fix get_total_balance() --- 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 7888766890..cc78c8b718 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1049,7 +1049,7 @@ def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> G """ Return the combined effective balance of an array of validators. """ - sum([get_effective_balance(state, i) for i in validators]) + return sum([get_effective_balance(state, i) for i in validators]) ``` ### `get_fork_version` From 867dea3473733946d0eb51b8e3132eb3ccacfb2c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 5 Feb 2019 19:16:10 +0800 Subject: [PATCH 26/57] Fix `verify_bitfield` --- specs/core/0_beacon-chain.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cc78c8b718..ed5d1b3b34 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1099,9 +1099,11 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: if len(bitfield) != (committee_size + 7) // 8: return False - for i in range(committee_size + 1, committee_size - committee_size % 8 + 8): - if get_bitfield_bit(bitfield, i) == 0b1: - return False + # Check if `bitfield` has padding zeros + if committee_size % 8 != 0: + for i in range(committee_size, committee_size - committee_size % 8 + 8): + if get_bitfield_bit(bitfield, i) == 0b1: + return False return True ``` From e5788f5751a9a0be34d02ed72ab26ad3dddd12f6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Feb 2019 01:11:00 +0800 Subject: [PATCH 27/57] Fix typo: it's `bls_verify_multiple` in `verify_slashable_attestation` (#574) --- specs/core/0_beacon-chain.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cc78c8b718..ccc9bf3af2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1137,7 +1137,7 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas else: custody_bit_1_indices.append(validator_index) - return bls_verify( + return bls_verify_multiple( pubkeys=[ bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_indices]), bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]), @@ -1147,11 +1147,7 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)), ], signature=slashable_attestation.aggregate_signature, - domain=get_domain( - state.fork, - slot_to_epoch(vote_data.data.slot), - DOMAIN_ATTESTATION, - ), + domain=get_domain(state.fork, slot_to_epoch(vote_data.data.slot), DOMAIN_ATTESTATION), ) ``` From 1d76ad65ec4dcde8666d57344057d9060ed30b7f Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 5 Feb 2019 11:49:52 -0800 Subject: [PATCH 28/57] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ed5d1b3b34..6b41e6c8aa 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1100,10 +1100,9 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: return False # Check if `bitfield` has padding zeros - if committee_size % 8 != 0: - for i in range(committee_size, committee_size - committee_size % 8 + 8): - if get_bitfield_bit(bitfield, i) == 0b1: - return False + for i in range(committee_size, len(bitfield) * 8): + if get_bitfield_bit(bitfield, i) == 0b1: + return False return True ``` From 180c8a0e728cf2f3c21e2ea10a0d439efa9fd792 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 5 Feb 2019 11:51:06 -0800 Subject: [PATCH 29/57] 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 6b41e6c8aa..77196312b9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1099,7 +1099,7 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: if len(bitfield) != (committee_size + 7) // 8: return False - # Check if `bitfield` has padding zeros + # Check `bitfield` is padded with zero bits only for i in range(committee_size, len(bitfield) * 8): if get_bitfield_bit(bitfield, i) == 0b1: return False From c58410e6ce9904c6619cd925b64fbd04c00b9a89 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 06:48:46 -0600 Subject: [PATCH 30/57] Introduce swap-or-not shuffle See #563 for discussion. --- specs/core/0_beacon-chain.md | 63 +++++++++--------------------------- 1 file changed, 16 insertions(+), 47 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 44b88d126b..3cb1b459fb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -697,53 +697,22 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: - """ - Return the shuffled ``values`` with ``seed`` as entropy. - """ - values_count = len(values) - - # Entropy is consumed from the seed in 3-byte (24 bit) chunks. - rand_bytes = 3 - # The highest possible result of the RNG. - rand_max = 2 ** (rand_bytes * 8) - 1 - - # The range of the RNG places an upper-bound on the size of the list that - # may be shuffled. It is a logic error to supply an oversized list. - assert values_count < rand_max - - output = [x for x in values] - source = seed - index = 0 - while index < values_count - 1: - # Re-hash the `source` to obtain a new pattern of bytes. - source = hash(source) - # Iterate through the `source` bytes in 3-byte chunks. - for position in range(0, 32 - (32 % rand_bytes), rand_bytes): - # Determine the number of indices remaining in `values` and exit - # once the last index is reached. - remaining = values_count - index - if remaining == 1: - break - - # Read 3-bytes of `source` as a 24-bit big-endian integer. - sample_from_source = int.from_bytes(source[position:position + rand_bytes], 'big') - - # Sample values greater than or equal to `sample_max` will cause - # modulo bias when mapped into the `remaining` range. - sample_max = rand_max - rand_max % remaining - - # Perform a swap if the consumed entropy will not cause modulo bias. - if sample_from_source < sample_max: - # Select a replacement index for the current index. - replacement_position = (sample_from_source % remaining) + index - # Swap the current index with the replacement index. - output[index], output[replacement_position] = output[replacement_position], output[index] - index += 1 - else: - # The sample causes modulo bias. A new sample should be read. - pass - - return output + indices = list(range(len(values))) + for round in range(90): + hashvalues = b''.join([ + hash(seed + round.to_bytes(1, 'big') + i.to_bytes(4, 'big')) + for i in range((n + 255) // 256) + ]) + pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n + + def permute(pos): + flip = (pivot - pos) % n + maxpos = max(pos, flip) + bit = (hashvalues[maxpos // 8] >> (maxpos % 8)) % 2 + return flip if bit else pos + + indices = [permute(v) for v in indices] + return [v[index] for index in indices] ``` ### `split` From 8f37c5c0f8804d37dde2e513e6eeaa91b8b7879e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 10:34:19 -0600 Subject: [PATCH 31/57] Update fork choice rule (#571) --- specs/core/0_beacon-chain.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 44b88d126b..9015a4970e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -13,6 +13,7 @@ - [Constants](#constants) - [Misc](#misc) - [Deposit contract](#deposit-contract) + - [Gwei values](#gwei-values) - [Initial values](#initial-values) - [Time parameters](#time-parameters) - [State list lengths](#state-list-lengths) @@ -179,7 +180,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | - | - | :-: | | `SHARD_COUNT` | `2**10` (= 1,024) | shards | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | [validators](#dfn-validator) | -| `EJECTION_BALANCE` | `2**4 * 1e9` (= 16,000,000,000) | Gwei | | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | votes | @@ -189,12 +189,19 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be ### Deposit contract +| Name | Value | +| - | - | +| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** | +| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | + +### Gwei values + | Name | Value | Unit | | - | - | :-: | -| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** | -| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | - | | `MIN_DEPOSIT_AMOUNT` | `2**0 * 1e9` (= 1,000,000,000) | Gwei | | `MAX_DEPOSIT_AMOUNT` | `2**5 * 1e9` (= 32,000,000,000) | Gwei | +| `FORK_CHOICE_BALANCE_INCREMENT` | `2**0 * 1e9` (= 1,000,000,000) | Gwei | +| `EJECTION_BALANCE` | `2**4 * 1e9` (= 16,000,000,000) | Gwei | ### Initial values @@ -1555,8 +1562,8 @@ def get_ancestor(store: Store, block: BeaconBlock, slot: SlotNumber) -> BeaconBl return get_ancestor(store, store.get_parent(block), slot) ``` -* Let `get_latest_attestation(store: Store, validator: Validator) -> Attestation` be the attestation with the highest slot number in `store` from `validator`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first. -* Let `get_latest_attestation_target(store: Store, validator: Validator) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, validator)`. +* Let `get_latest_attestation(store: Store, validator_index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `validator_index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first. +* Let `get_latest_attestation_target(store: Store, validator_index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, validator_index)`. * Let `get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]` returns the child blocks of the given `block`. * Let `justified_head_state` be the resulting `BeaconState` object from processing the chain up to the `justified_head`. * The `head` is `lmd_ghost(store, justified_head_state, justified_head)` where the function `lmd_ghost` is defined below. Note that the implementation below is suboptimal; there are implementations that compute the head in time logarithmic in slot count. @@ -1567,21 +1574,18 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) Execute the LMD-GHOST algorithm to find the head ``BeaconBlock``. """ validators = start_state.validator_registry - active_validators = [ - validators[i] - for i in get_active_validator_indices(validators, start_state.slot) - ] + active_validator_indices = get_active_validator_indices(validators, start_state.slot) attestation_targets = [ - get_latest_attestation_target(store, validator) - for validator in active_validators + (validator_index, get_latest_attestation_target(store, validator_index)) + for validator_index in active_validator_indices ] def get_vote_count(block: BeaconBlock) -> int: - return len([ - target - for target in attestation_targets + return sum( + get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT + for validator_index, target in attestation_targets if get_ancestor(store, target, block.slot) == block - ]) + ) head = start_block while 1: From 37b41a2ce645b644a56c473cba2ded3a49689139 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Feb 2019 18:33:11 -0600 Subject: [PATCH 32/57] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3cb1b459fb..5471d76a18 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -704,7 +704,6 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: for i in range((n + 255) // 256) ]) pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n - def permute(pos): flip = (pivot - pos) % n maxpos = max(pos, flip) From 4ec721f3b7d4459c3db601d9a3bd53511a1dfd31 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 6 Feb 2019 18:33:22 -0600 Subject: [PATCH 33/57] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 5471d76a18..2e453b061b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -697,6 +697,13 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: + """ + Return the shuffled ``values`` with ``seed`` as entropy. + + Utilizes 'swap or not' shuffling found in + https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf + See the 'generalized domain' algorithm on page 3. + """ indices = list(range(len(values))) for round in range(90): hashvalues = b''.join([ From 6a5b7540da501c976d1e0ec76b44725bb96f7f37 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Feb 2019 18:33:29 -0600 Subject: [PATCH 34/57] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- 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 2e453b061b..1426bb4e8c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -718,7 +718,7 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: return flip if bit else pos indices = [permute(v) for v in indices] - return [v[index] for index in indices] + return [values[index] for index in indices] ``` ### `split` From 47b00f38dda4336ada69b166f8f288b840c628d3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 18:34:05 -0600 Subject: [PATCH 35/57] n -> len(values) --- 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 1426bb4e8c..60bacba63b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -708,7 +708,7 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: for round in range(90): hashvalues = b''.join([ hash(seed + round.to_bytes(1, 'big') + i.to_bytes(4, 'big')) - for i in range((n + 255) // 256) + for i in range((len(values) + 255) // 256) ]) pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n def permute(pos): From b3db7b03942d2c666952fb94fe01774d15089105 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 6 Feb 2019 20:32:05 -0800 Subject: [PATCH 36/57] big to little in shuffle --- 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 60bacba63b..641ad02ca7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -707,10 +707,10 @@ def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: indices = list(range(len(values))) for round in range(90): hashvalues = b''.join([ - hash(seed + round.to_bytes(1, 'big') + i.to_bytes(4, 'big')) + hash(seed + round.to_bytes(1, 'little') + i.to_bytes(4, 'little')) for i in range((len(values) + 255) // 256) ]) - pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'big')), 'big') % n + pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % n def permute(pos): flip = (pivot - pos) % n maxpos = max(pos, flip) From 65255e53c46ac8d2ee520f2845e9869193ae783c Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 23:29:24 -0600 Subject: [PATCH 37/57] shuffle -> get_permuted_index --- specs/core/0_beacon-chain.md | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 641ad02ca7..dd58482b35 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -59,7 +59,7 @@ - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`shuffle`](#shuffle) + - [`get_permuted_index`](#get_permuted_index) - [`split`](#split) - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) @@ -693,32 +693,27 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` -### `shuffle` +### `get_permuted_index` ```python -def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: +def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: """ - Return the shuffled ``values`` with ``seed`` as entropy. + Return a pseudorandom permutation of `0...list_size-1` with ``seed`` as entropy. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ - indices = list(range(len(values))) for round in range(90): - hashvalues = b''.join([ - hash(seed + round.to_bytes(1, 'little') + i.to_bytes(4, 'little')) - for i in range((len(values) + 255) // 256) - ]) - pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % n - def permute(pos): - flip = (pivot - pos) % n - maxpos = max(pos, flip) - bit = (hashvalues[maxpos // 8] >> (maxpos % 8)) % 2 - return flip if bit else pos - - indices = [permute(v) for v in indices] - return [values[index] for index in indices] + pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % list_size + + flip = (pivot - index) % list_size + hash_pos = max(index, flip) + h = hash(seed + round.to_bytes(1, 'little') + (hash_pos // 256).to_bytes(4, 'little')) + byte = h[(hash_pos % 256) // 8] + bit = (byte >> (hash_pos % 8)) % 2 + index = flip if bit else index + return index ``` ### `split` @@ -768,7 +763,10 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle - shuffled_active_validator_indices = shuffle(active_validator_indices, seed) + shuffled_active_validator_indices = [ + active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] + for i in active_validator_indices + ] # Split the shuffled list into committees_per_epoch pieces return split(shuffled_active_validator_indices, committees_per_epoch) From 92514716fb77de05637a560ec3f62a0ed5209dc6 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 6 Feb 2019 23:32:20 -0600 Subject: [PATCH 38/57] 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 dd58482b35..2c0fce8c59 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -698,7 +698,7 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: """ - Return a pseudorandom permutation of `0...list_size-1` with ``seed`` as entropy. + Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf From 70e482be28a10ca0d37de34fb3a0b5cd94108ad3 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 7 Feb 2019 19:14:58 +0800 Subject: [PATCH 39/57] Add vbuterin's optimization and some formatting --- specs/core/0_beacon-chain.md | 44 +++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2c0fce8c59..3da5203007 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -59,7 +59,7 @@ - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`get_permuted_index`](#get_permuted_index) + - [`shuffle`](#shuffle) - [`split`](#split) - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) @@ -693,27 +693,38 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` -### `get_permuted_index` +### `shuffle` ```python -def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: +def shuffle(list_size: int, seed: Bytes32) -> List[int]: """ - Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. - + Return shuffled indices in a pseudorandom permutation `0...list_size-1` with ``seed`` as entropy. + Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ + indices = list(range(list_size)) + power_of_two_numbers = [1, 2, 4, 8, 16, 32, 64, 128] for round in range(90): - pivot = int.from_bytes(hash(seed + round.to_bytes(1, 'little')), 'little') % list_size - - flip = (pivot - index) % list_size - hash_pos = max(index, flip) - h = hash(seed + round.to_bytes(1, 'little') + (hash_pos // 256).to_bytes(4, 'little')) - byte = h[(hash_pos % 256) // 8] - bit = (byte >> (hash_pos % 8)) % 2 - index = flip if bit else index - return index + hash_bytes = b''.join([ + hash(seed + int_to_bytes1(round) + int_to_bytes4(i)) + for i in range((list_size + 255) // 256) + ]) + + pivot = int.from_bytes(hash(seed + int_to_bytes1(round)), 'little') % list_size + for i in range(list_size): + flip = (pivot - indices[i]) % list_size + hash_position = indices[i] if indices[i] > flip else flip + byte = hash_bytes[hash_position // 8] + mask = power_of_two_numbers[hash_position % 8] + if byte & mask: + indices[i] = flip + else: + # not swap + pass + + return indices ``` ### `split` @@ -763,9 +774,10 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle + shuffled_indices = shuffle(len(active_validator_indices), seed) shuffled_active_validator_indices = [ - active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] - for i in active_validator_indices + active_validator_indices[i] + for i in shuffle(len(active_validator_indices), seed) ] # Split the shuffled list into committees_per_epoch pieces From aa9f9fc9be337ad072e855e5a4132735eea7636b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 7 Feb 2019 19:18:39 +0800 Subject: [PATCH 40/57] amend --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3da5203007..6a4ac6ce56 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -774,7 +774,6 @@ def get_shuffling(seed: Bytes32, committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) # Shuffle - shuffled_indices = shuffle(len(active_validator_indices), seed) shuffled_active_validator_indices = [ active_validator_indices[i] for i in shuffle(len(active_validator_indices), seed) From 3f3472087af00ddb54210feb7f82cc7f6e22d966 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 10:37:01 -0800 Subject: [PATCH 41/57] change message to message_hash in bls spec addresses #572 --- specs/bls_signature.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/specs/bls_signature.md b/specs/bls_signature.md index fd9bae58e4..b0490b7aef 100644 --- a/specs/bls_signature.md +++ b/specs/bls_signature.md @@ -69,10 +69,10 @@ We require: G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -def hash_to_G2(message: bytes32, domain: uint64) -> [uint384]: +def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]: # Initial candidate x coordinate - x_re = int.from_bytes(hash(message + bytes8(domain) + b'\x01'), 'big') - x_im = int.from_bytes(hash(message + bytes8(domain) + b'\x02'), 'big') + x_re = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x01'), 'big') + x_im = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x02'), 'big') x_coordinate = Fq2([x_re, x_im]) # x = x_re + i * x_im # Test candidate y coordinates until a one is found @@ -128,17 +128,17 @@ g = Fq2([g_x, g_y]) ### `bls_verify` -Let `bls_verify(pubkey: Bytes48, message: Bytes32, signature: Bytes96, domain: uint64) -> bool`: +Let `bls_verify(pubkey: Bytes48, message_hash: Bytes32, signature: Bytes96, domain: uint64) -> bool`: * Verify that `pubkey` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify that `e(pubkey, hash_to_G2(message, domain)) == e(g, signature)`. +* Verify that `e(pubkey, hash_to_G2(message_hash, domain)) == e(g, signature)`. ### `bls_verify_multiple` -Let `bls_verify_multiple(pubkeys: List[Bytes48], messages: List[Bytes32], signature: Bytes96, domain: uint64) -> bool`: +Let `bls_verify_multiple(pubkeys: List[Bytes48], message_hashes: List[Bytes32], signature: Bytes96, domain: uint64) -> bool`: * Verify that each `pubkey` in `pubkeys` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify that `len(pubkeys)` equals `len(messages)` and denote the length `L`. -* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(g, signature)`. +* Verify that `len(pubkeys)` equals `len(message_hashes)` and denote the length `L`. +* Verify that `e(pubkeys[0], hash_to_G2(message_hashes[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(message_hashes[L-1], domain)) == e(g, signature)`. From d4901be1984e44a61d410bda9d4592caa25cf470 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 11:48:36 -0700 Subject: [PATCH 42/57] get_next_epoch_committee_assignments returns slot and is_proposer --- specs/validator/0_beacon-chain-validator.md | 26 +++++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 3df3d758d7..093f29f5bc 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -344,12 +344,22 @@ Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is determi `get_crosslink_committees_at_slot` is designed to be able to query slots in the next epoch. When querying slots in the next epoch there are two options -- with and without a `registry_change` -- which is the optional third parameter of the function. The following helper can be used to get the potential crosslink committees in the next epoch for a given `validator_index`. This function returns a list of 2 shard committee tuples. ```python -def get_next_epoch_crosslink_committees(state: BeaconState, - validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: +def get_next_epoch_committee_assignments( + state: BeaconState, + validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]]: + """ + Return a list of the two possible committee assignments for ``validator_index`` at the next epoch. + Possible committee ``assignment`` is of the form (List[ValidatorIndex], ShardNumber, SlotNumber, bool). + * ``assignment[0]`` is the list of validators in the committee + * ``assignment[1]`` is the shard to which the committee is assigned + * ``assignment[2]`` is the slot at which the committee is assigned + * ``assignment[3]`` is a bool signalling if the validator is expected to propose + a beacon block at the assigned slot. + """ current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) - potential_committees = [] + potential_assignments = [] for registry_change in [False, True]: for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): crosslink_committees = get_crosslink_committees_at_slot( @@ -363,13 +373,19 @@ def get_next_epoch_crosslink_committees(state: BeaconState, if validator_index in committee[0] ] if len(selected_committees) > 0: - potential_assignments.append(selected_committees) + assignment = selected_committees[0] + assignment += (slot,) + first_committee_at_slot = crosslink_committees[0] + is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index + assignment += (is_proposer,) + + potential_assignments.append(assignment) break return potential_assignments ``` -`get_next_epoch_crosslink_committees` should be called at the beginning of each epoch to plan for the next epoch. A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for a future shuffling involves noting at which slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). +`get_next_epoch_committee_assignments` should be called at the beginning of each epoch to plan for the next epoch. A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for a future shuffling involves noting at which slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). ## How to avoid slashing From e4f5efadb79dcfe6f83b71a3bf339fd11b224f0a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 04:03:13 +0800 Subject: [PATCH 43/57] Fix `first_committee_at_slot` --- specs/validator/0_beacon-chain-validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 093f29f5bc..4d8d5eadfa 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -368,14 +368,14 @@ def get_next_epoch_committee_assignments( registry_change=registry_change, ) selected_committees = [ - committee # type: Tuple[List[ValidatorIndex], ShardNumber] + committee # Tuple[List[ValidatorIndex], ShardNumber] for committee in crosslink_committees if validator_index in committee[0] ] if len(selected_committees) > 0: assignment = selected_committees[0] assignment += (slot,) - first_committee_at_slot = crosslink_committees[0] + first_committee_at_slot = crosslink_committees[0][0] # List[ValidatorIndex] is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index assignment += (is_proposer,) From 859bf6248467eb8f5a23c28ac721f605185ccfaf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 05:08:25 +0800 Subject: [PATCH 44/57] Revert and refactor --- specs/core/0_beacon-chain.md | 41 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6a4ac6ce56..38d233b23a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -59,7 +59,7 @@ - [`get_epoch_start_slot`](#get_epoch_start_slot) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - - [`shuffle`](#shuffle) + - [`get_permuted_index`](#get_permuted_index) - [`split`](#split) - [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_shuffling`](#get_shuffling) @@ -693,38 +693,29 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` -### `shuffle` +### `get_permuted_index` ```python -def shuffle(list_size: int, seed: Bytes32) -> List[int]: +def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> List[int]: """ - Return shuffled indices in a pseudorandom permutation `0...list_size-1` with ``seed`` as entropy. + Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. + + Note that ``round_count`` is ``90`` in protocol and parameterized for the shuffling tests. Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ - indices = list(range(list_size)) - power_of_two_numbers = [1, 2, 4, 8, 16, 32, 64, 128] - for round in range(90): - hash_bytes = b''.join([ - hash(seed + int_to_bytes1(round) + int_to_bytes4(i)) - for i in range((list_size + 255) // 256) - ]) - + for round in range(round_count): pivot = int.from_bytes(hash(seed + int_to_bytes1(round)), 'little') % list_size - for i in range(list_size): - flip = (pivot - indices[i]) % list_size - hash_position = indices[i] if indices[i] > flip else flip - byte = hash_bytes[hash_position // 8] - mask = power_of_two_numbers[hash_position % 8] - if byte & mask: - indices[i] = flip - else: - # not swap - pass + flip = (pivot - index) % list_size + position = max(index, flip) + source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) + byte = source[(position % 256) // 8] + bit = (byte >> (position % 8)) % 2 + index = flip if bit else index - return indices + return index ``` ### `split` @@ -775,8 +766,8 @@ def get_shuffling(seed: Bytes32, # Shuffle shuffled_active_validator_indices = [ - active_validator_indices[i] - for i in shuffle(len(active_validator_indices), seed) + active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] + for i in active_validator_indices ] # Split the shuffled list into committees_per_epoch pieces From cf7ebe9ad31d1b4831f8a26353ac70c1fd60d27c Mon Sep 17 00:00:00 2001 From: mratsim Date: Thu, 7 Feb 2019 22:09:41 +0100 Subject: [PATCH 45/57] `message` to `message_hash` in the rest of the spec (followup https://github.com/ethereum/eth2.0-specs/pull/580) --- specs/core/0_beacon-chain.md | 280 ++++++++++---------- specs/validator/0_beacon-chain-validator.md | 102 +++---- 2 files changed, 191 insertions(+), 191 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b0433ce044..e6cf019f40 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -6,139 +6,139 @@ - [Ethereum 2.0 Phase 0 -- The Beacon Chain](#ethereum-20-phase-0----the-beacon-chain) - - [Table of contents](#table-of-contents) - - [Introduction](#introduction) - - [Notation](#notation) - - [Terminology](#terminology) - - [Constants](#constants) - - [Misc](#misc) - - [Deposit contract](#deposit-contract) - - [Gwei values](#gwei-values) - - [Initial values](#initial-values) - - [Time parameters](#time-parameters) - - [State list lengths](#state-list-lengths) - - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status flags](#status-flags) - - [Max operations per block](#max-operations-per-block) - - [Signature domains](#signature-domains) - - [Data structures](#data-structures) - - [Beacon chain operations](#beacon-chain-operations) - - [Proposer slashings](#proposer-slashings) - - [`ProposerSlashing`](#proposerslashing) - - [Attester slashings](#attester-slashings) - - [`AttesterSlashing`](#attesterslashing) - - [`SlashableAttestation`](#slashableattestation) - - [Attestations](#attestations) - - [`Attestation`](#attestation) - - [`AttestationData`](#attestationdata) - - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - - [Deposits](#deposits) - - [`Deposit`](#deposit) - - [`DepositData`](#depositdata) - - [`DepositInput`](#depositinput) - - [Exits](#exits) - - [`Exit`](#exit) - - [Beacon chain blocks](#beacon-chain-blocks) - - [`BeaconBlock`](#beaconblock) - - [`BeaconBlockBody`](#beaconblockbody) - - [`ProposalSignedData`](#proposalsigneddata) - - [Beacon chain state](#beacon-chain-state) - - [`BeaconState`](#beaconstate) - - [`Validator`](#validator) - - [`Crosslink`](#crosslink) - - [`PendingAttestation`](#pendingattestation) - - [`Fork`](#fork) - - [`Eth1Data`](#eth1data) - - [`Eth1DataVote`](#eth1datavote) - - [Custom Types](#custom-types) - - [Helper functions](#helper-functions) - - [`hash`](#hash) - - [`hash_tree_root`](#hash_tree_root) - - [`slot_to_epoch`](#slot_to_epoch) - - [`get_previous_epoch`](#get_previous_epoch) - - [`get_current_epoch`](#get_current_epoch) - - [`get_epoch_start_slot`](#get_epoch_start_slot) - - [`is_active_validator`](#is_active_validator) - - [`get_active_validator_indices`](#get_active_validator_indices) - - [`shuffle`](#shuffle) - - [`split`](#split) - - [`get_epoch_committee_count`](#get_epoch_committee_count) - - [`get_shuffling`](#get_shuffling) - - [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count) - - [`get_current_epoch_committee_count`](#get_current_epoch_committee_count) - - [`get_next_epoch_committee_count`](#get_next_epoch_committee_count) - - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) - - [`get_block_root`](#get_block_root) - - [`get_randao_mix`](#get_randao_mix) - - [`get_active_index_root`](#get_active_index_root) - - [`generate_seed`](#generate_seed) - - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - - [`merkle_root`](#merkle_root) - - [`get_attestation_participants`](#get_attestation_participants) - - [`is_power_of_two`](#is_power_of_two) - - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) - - [`get_effective_balance`](#get_effective_balance) - - [`get_total_balance`](#get_total_balance) - - [`get_fork_version`](#get_fork_version) - - [`get_domain`](#get_domain) - - [`get_bitfield_bit`](#get_bitfield_bit) - - [`verify_bitfield`](#verify_bitfield) - - [`verify_slashable_attestation`](#verify_slashable_attestation) - - [`is_double_vote`](#is_double_vote) - - [`is_surround_vote`](#is_surround_vote) - - [`integer_squareroot`](#integer_squareroot) - - [`get_entry_exit_effect_epoch`](#get_entry_exit_effect_epoch) - - [`bls_verify`](#bls_verify) - - [`bls_verify_multiple`](#bls_verify_multiple) - - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - - [`validate_proof_of_possession`](#validate_proof_of_possession) - - [`process_deposit`](#process_deposit) - - [Routines for updating validator status](#routines-for-updating-validator-status) - - [`activate_validator`](#activate_validator) - - [`initiate_validator_exit`](#initiate_validator_exit) - - [`exit_validator`](#exit_validator) - - [`penalize_validator`](#penalize_validator) - - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) - - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - - [Deposit arguments](#deposit-arguments) - - [Withdrawal credentials](#withdrawal-credentials) - - [`Deposit` logs](#deposit-logs) - - [`ChainStart` log](#chainstart-log) - - [Vyper code](#vyper-code) - - [On startup](#on-startup) - - [Beacon chain processing](#beacon-chain-processing) - - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Per-slot processing](#per-slot-processing) - - [Slot](#slot) - - [Block roots](#block-roots) - - [Per-block processing](#per-block-processing) - - [Slot](#slot-1) - - [Proposer signature](#proposer-signature) - - [RANDAO](#randao) - - [Eth1 data](#eth1-data) - - [Operations](#operations) - - [Proposer slashings](#proposer-slashings-1) - - [Attester slashings](#attester-slashings-1) - - [Attestations](#attestations-1) - - [Deposits](#deposits-1) - - [Exits](#exits-1) - - [Per-epoch processing](#per-epoch-processing) - - [Helpers](#helpers) - - [Eth1 data](#eth1-data-1) - - [Justification](#justification) - - [Crosslinks](#crosslinks) - - [Rewards and penalties](#rewards-and-penalties) - - [Justification and finalization](#justification-and-finalization) - - [Attestation inclusion](#attestation-inclusion) - - [Crosslinks](#crosslinks-1) - - [Ejections](#ejections) - - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) - - [Final updates](#final-updates) - - [State root verification](#state-root-verification) + - [Table of contents](#table-of-contents) + - [Introduction](#introduction) + - [Notation](#notation) + - [Terminology](#terminology) + - [Constants](#constants) + - [Misc](#misc) + - [Deposit contract](#deposit-contract) + - [Gwei values](#gwei-values) + - [Initial values](#initial-values) + - [Time parameters](#time-parameters) + - [State list lengths](#state-list-lengths) + - [Reward and penalty quotients](#reward-and-penalty-quotients) + - [Status flags](#status-flags) + - [Max operations per block](#max-operations-per-block) + - [Signature domains](#signature-domains) + - [Data structures](#data-structures) + - [Beacon chain operations](#beacon-chain-operations) + - [Proposer slashings](#proposer-slashings) + - [`ProposerSlashing`](#proposerslashing) + - [Attester slashings](#attester-slashings) + - [`AttesterSlashing`](#attesterslashing) + - [`SlashableAttestation`](#slashableattestation) + - [Attestations](#attestations) + - [`Attestation`](#attestation) + - [`AttestationData`](#attestationdata) + - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) + - [Deposits](#deposits) + - [`Deposit`](#deposit) + - [`DepositData`](#depositdata) + - [`DepositInput`](#depositinput) + - [Exits](#exits) + - [`Exit`](#exit) + - [Beacon chain blocks](#beacon-chain-blocks) + - [`BeaconBlock`](#beaconblock) + - [`BeaconBlockBody`](#beaconblockbody) + - [`ProposalSignedData`](#proposalsigneddata) + - [Beacon chain state](#beacon-chain-state) + - [`BeaconState`](#beaconstate) + - [`Validator`](#validator) + - [`Crosslink`](#crosslink) + - [`PendingAttestation`](#pendingattestation) + - [`Fork`](#fork) + - [`Eth1Data`](#eth1data) + - [`Eth1DataVote`](#eth1datavote) + - [Custom Types](#custom-types) + - [Helper functions](#helper-functions) + - [`hash`](#hash) + - [`hash_tree_root`](#hashtreeroot) + - [`slot_to_epoch`](#slottoepoch) + - [`get_previous_epoch`](#getpreviousepoch) + - [`get_current_epoch`](#getcurrentepoch) + - [`get_epoch_start_slot`](#getepochstartslot) + - [`is_active_validator`](#isactivevalidator) + - [`get_active_validator_indices`](#getactivevalidatorindices) + - [`shuffle`](#shuffle) + - [`split`](#split) + - [`get_epoch_committee_count`](#getepochcommitteecount) + - [`get_shuffling`](#getshuffling) + - [`get_previous_epoch_committee_count`](#getpreviousepochcommitteecount) + - [`get_current_epoch_committee_count`](#getcurrentepochcommitteecount) + - [`get_next_epoch_committee_count`](#getnextepochcommitteecount) + - [`get_crosslink_committees_at_slot`](#getcrosslinkcommitteesatslot) + - [`get_block_root`](#getblockroot) + - [`get_randao_mix`](#getrandaomix) + - [`get_active_index_root`](#getactiveindexroot) + - [`generate_seed`](#generateseed) + - [`get_beacon_proposer_index`](#getbeaconproposerindex) + - [`merkle_root`](#merkleroot) + - [`get_attestation_participants`](#getattestationparticipants) + - [`is_power_of_two`](#ispoweroftwo) + - [`int_to_bytes1`, `int_to_bytes2`, ...](#inttobytes1-inttobytes2) + - [`get_effective_balance`](#geteffectivebalance) + - [`get_total_balance`](#gettotalbalance) + - [`get_fork_version`](#getforkversion) + - [`get_domain`](#getdomain) + - [`get_bitfield_bit`](#getbitfieldbit) + - [`verify_bitfield`](#verifybitfield) + - [`verify_slashable_attestation`](#verifyslashableattestation) + - [`is_double_vote`](#isdoublevote) + - [`is_surround_vote`](#issurroundvote) + - [`integer_squareroot`](#integersquareroot) + - [`get_entry_exit_effect_epoch`](#getentryexiteffectepoch) + - [`bls_verify`](#blsverify) + - [`bls_verify_multiple`](#blsverifymultiple) + - [`bls_aggregate_pubkeys`](#blsaggregatepubkeys) + - [`validate_proof_of_possession`](#validateproofofpossession) + - [`process_deposit`](#processdeposit) + - [Routines for updating validator status](#routines-for-updating-validator-status) + - [`activate_validator`](#activatevalidator) + - [`initiate_validator_exit`](#initiatevalidatorexit) + - [`exit_validator`](#exitvalidator) + - [`penalize_validator`](#penalizevalidator) + - [`prepare_validator_for_withdrawal`](#preparevalidatorforwithdrawal) + - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) + - [Deposit arguments](#deposit-arguments) + - [Withdrawal credentials](#withdrawal-credentials) + - [`Deposit` logs](#deposit-logs) + - [`ChainStart` log](#chainstart-log) + - [Vyper code](#vyper-code) + - [On startup](#on-startup) + - [Beacon chain processing](#beacon-chain-processing) + - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) + - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Per-slot processing](#per-slot-processing) + - [Slot](#slot) + - [Block roots](#block-roots) + - [Per-block processing](#per-block-processing) + - [Slot](#slot-1) + - [Proposer signature](#proposer-signature) + - [RANDAO](#randao) + - [Eth1 data](#eth1-data) + - [Operations](#operations) + - [Proposer slashings](#proposer-slashings-1) + - [Attester slashings](#attester-slashings-1) + - [Attestations](#attestations-1) + - [Deposits](#deposits-1) + - [Exits](#exits-1) + - [Per-epoch processing](#per-epoch-processing) + - [Helpers](#helpers) + - [Eth1 data](#eth1-data-1) + - [Justification](#justification) + - [Crosslinks](#crosslinks) + - [Rewards and penalties](#rewards-and-penalties) + - [Justification and finalization](#justification-and-finalization) + - [Attestation inclusion](#attestation-inclusion) + - [Crosslinks](#crosslinks-1) + - [Ejections](#ejections) + - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) + - [Final updates](#final-updates) + - [State root verification](#state-root-verification) - [References](#references) - - [Normative](#normative) - - [Informative](#informative) + - [Normative](#normative) + - [Informative](#informative) - [Copyright](#copyright) @@ -1052,7 +1052,7 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: ### `get_total_balance` ```python -def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: +def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei: """ Return the combined effective balance of an array of validators. """ @@ -1150,7 +1150,7 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_indices]), bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]), ], - messages=[ + message_hashes=[ hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b0)), hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)), ], @@ -1245,7 +1245,7 @@ def validate_proof_of_possession(state: BeaconState, return bls_verify( pubkey=pubkey, - message=hash_tree_root(proof_of_possession_data), + message_hash=hash_tree_root(proof_of_possession_data), signature=proof_of_possession, domain=get_domain( state.fork, @@ -1633,12 +1633,12 @@ Below are the processing steps that happen at every `block`. * Let `block_without_signature_root` be the `hash_tree_root` of `block` where `block.signature` is set to `EMPTY_SIGNATURE`. * Let `proposal_root = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_without_signature_root))`. -* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, message=proposal_root, signature=block.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, message_hash=proposal_root, signature=block.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. #### RANDAO * Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=int_to_bytes32(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=int_to_bytes32(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. * Set `state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = xor(get_randao_mix(state, get_current_epoch(state)), hash(block.randao_reveal))`. #### Eth1 data @@ -1659,8 +1659,8 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. * Verify that `proposer.penalized_epoch > get_current_epoch(state)`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot), DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. ##### Attester slashings @@ -1772,7 +1772,7 @@ For each `exit` in `block.body.exits`: * Verify that `validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))`. * Verify that `get_current_epoch(state) >= exit.epoch`. * Let `exit_message = hash_tree_root(Exit(epoch=exit.epoch, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. -* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. +* Verify that `bls_verify(pubkey=validator.pubkey, message_hash=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. ### Per-epoch processing diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 4d8d5eadfa..b8db2117c9 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -7,53 +7,53 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Ethereum 2.0 Phase 0 -- Honest Validator](#ethereum-20-phase-0----honest-validator) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [Prerequisites](#prerequisites) - - [Constants](#constants) - - [Misc](#misc) - - [Becoming a validator](#becoming-a-validator) - - [Initialization](#initialization) - - [BLS public key](#bls-public-key) - - [BLS withdrawal key](#bls-withdrawal-key) - - [Submit deposit](#submit-deposit) - - [Process deposit](#process-deposit) - - [Validator index](#validator-index) - - [Activation](#activation) - - [Beacon chain responsibilities](#beacon-chain-responsibilities) - - [Block proposal](#block-proposal) - - [Block header](#block-header) - - [Slot](#slot) - - [Parent root](#parent-root) - - [State root](#state-root) - - [Randao reveal](#randao-reveal) - - [Eth1 Data](#eth1-data) - - [Signature](#signature) - - [Block body](#block-body) - - [Proposer slashings](#proposer-slashings) - - [Attester slashings](#attester-slashings) - - [Attestations](#attestations) - - [Deposits](#deposits) - - [Exits](#exits) - - [Attestations](#attestations-1) - - [Attestation data](#attestation-data) - - [Slot](#slot-1) - - [Shard](#shard) - - [Beacon block root](#beacon-block-root) - - [Epoch boundary root](#epoch-boundary-root) - - [Shard block root](#shard-block-root) - - [Latest crosslink root](#latest-crosslink-root) - - [Justified epoch](#justified-epoch) - - [Justified block root](#justified-block-root) - - [Construct attestation](#construct-attestation) - - [Data](#data) - - [Aggregation bitfield](#aggregation-bitfield) - - [Custody bitfield](#custody-bitfield) - - [Aggregate signature](#aggregate-signature) - - [Responsibility lookahead](#responsibility-lookahead) - - [How to avoid slashing](#how-to-avoid-slashing) - - [Proposer slashing](#proposer-slashing) - - [Attester slashing](#attester-slashing) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Prerequisites](#prerequisites) + - [Constants](#constants) + - [Misc](#misc) + - [Becoming a validator](#becoming-a-validator) + - [Initialization](#initialization) + - [BLS public key](#bls-public-key) + - [BLS withdrawal key](#bls-withdrawal-key) + - [Submit deposit](#submit-deposit) + - [Process deposit](#process-deposit) + - [Validator index](#validator-index) + - [Activation](#activation) + - [Beacon chain responsibilities](#beacon-chain-responsibilities) + - [Block proposal](#block-proposal) + - [Block header](#block-header) + - [Slot](#slot) + - [Parent root](#parent-root) + - [State root](#state-root) + - [Randao reveal](#randao-reveal) + - [Eth1 Data](#eth1-data) + - [Signature](#signature) + - [Block body](#block-body) + - [Proposer slashings](#proposer-slashings) + - [Attester slashings](#attester-slashings) + - [Attestations](#attestations) + - [Deposits](#deposits) + - [Exits](#exits) + - [Attestations](#attestations-1) + - [Attestation data](#attestation-data) + - [Slot](#slot-1) + - [Shard](#shard) + - [Beacon block root](#beacon-block-root) + - [Epoch boundary root](#epoch-boundary-root) + - [Shard block root](#shard-block-root) + - [Latest crosslink root](#latest-crosslink-root) + - [Justified epoch](#justified-epoch) + - [Justified block root](#justified-block-root) + - [Construct attestation](#construct-attestation) + - [Data](#data) + - [Aggregation bitfield](#aggregation-bitfield) + - [Custody bitfield](#custody-bitfield) + - [Aggregate signature](#aggregate-signature) + - [Responsibility lookahead](#responsibility-lookahead) + - [How to avoid slashing](#how-to-avoid-slashing) + - [Proposer slashing](#proposer-slashing) + - [Attester slashing](#attester-slashing) @@ -95,7 +95,7 @@ The validator constructs their `withdrawal_credentials` via the following: ### Submit deposit -In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`. +In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`. To submit a deposit: @@ -166,7 +166,7 @@ Set `block.randao_reveal = epoch_signature` where `epoch_signature` is defined a ```python epoch_signature = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=int_to_bytes32(slot_to_epoch(block.slot)), + message_hash=int_to_bytes32(slot_to_epoch(block.slot)), domain=get_domain( fork=fork, # `fork` is the fork object at the slot `block.slot` epoch=slot_to_epoch(block.slot), @@ -205,7 +205,7 @@ proposal_root = hash_tree_root(proposal_data) signed_proposal_data = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=proposal_root, + message_hash=proposal_root, domain=get_domain( fork=fork, # `fork` is the fork object at the slot `block.slot` epoch=slot_to_epoch(block.slot), @@ -321,7 +321,7 @@ attestation_message_to_sign = hash_tree_root(attestation_data_and_custody_bit) signed_attestation_data = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message=attestation_message_to_sign, + message_hash=attestation_message_to_sign, domain=get_domain( fork=fork, # `fork` is the fork object at the slot, `attestation_data.slot` epoch=slot_to_epoch(attestation_data.slot), From 911e4f104bde97f4361e78c7e14c9d09d6c6b5da Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 05:12:58 +0800 Subject: [PATCH 46/57] Add `bytes_to_int` --- specs/core/0_beacon-chain.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 38d233b23a..c7103b79e6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -76,6 +76,7 @@ - [`get_attestation_participants`](#get_attestation_participants) - [`is_power_of_two`](#is_power_of_two) - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) + - [`bytes_to_int`](#bytes_to_int) - [`get_effective_balance`](#get_effective_balance) - [`get_total_balance`](#get_total_balance) - [`get_fork_version`](#get_fork_version) @@ -707,7 +708,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: i See the 'generalized domain' algorithm on page 3. """ for round in range(round_count): - pivot = int.from_bytes(hash(seed + int_to_bytes1(round)), 'little') % list_size + pivot = bytes_to_int(hash(seed + int_to_bytes1(round))) % list_size flip = (pivot - index) % list_size position = max(index, flip) source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) @@ -1007,6 +1008,13 @@ def is_power_of_two(value: int) -> bool: `int_to_bytes1(x): return x.to_bytes(1, 'big')`, `int_to_bytes2(x): return x.to_bytes(2, 'big')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96. +### `bytes_to_int` + +```python +def bytes_to_int(data: bytes) -> int: + return int.from_bytes(data, 'little') +``` + ### `get_effective_balance` ```python From 89b9894328b6a2060c3fbe61d2079ac400ca1208 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 8 Feb 2019 05:15:42 +0800 Subject: [PATCH 47/57] Fix type hinting --- 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 c7103b79e6..7167b93a61 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -697,7 +697,7 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ### `get_permuted_index` ```python -def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> List[int]: +def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> int: """ Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. From fd3d4a5105db74016483c1d1abbf81c932889b7f Mon Sep 17 00:00:00 2001 From: mratsim Date: Thu, 7 Feb 2019 22:15:55 +0100 Subject: [PATCH 48/57] Don't change TOC --- specs/core/0_beacon-chain.md | 264 +++++++++++++++++------------------ 1 file changed, 132 insertions(+), 132 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e6cf019f40..d4e90a57be 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -6,139 +6,139 @@ - [Ethereum 2.0 Phase 0 -- The Beacon Chain](#ethereum-20-phase-0----the-beacon-chain) - - [Table of contents](#table-of-contents) - - [Introduction](#introduction) - - [Notation](#notation) - - [Terminology](#terminology) - - [Constants](#constants) - - [Misc](#misc) - - [Deposit contract](#deposit-contract) - - [Gwei values](#gwei-values) - - [Initial values](#initial-values) - - [Time parameters](#time-parameters) - - [State list lengths](#state-list-lengths) - - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status flags](#status-flags) - - [Max operations per block](#max-operations-per-block) - - [Signature domains](#signature-domains) - - [Data structures](#data-structures) - - [Beacon chain operations](#beacon-chain-operations) - - [Proposer slashings](#proposer-slashings) - - [`ProposerSlashing`](#proposerslashing) - - [Attester slashings](#attester-slashings) - - [`AttesterSlashing`](#attesterslashing) - - [`SlashableAttestation`](#slashableattestation) - - [Attestations](#attestations) - - [`Attestation`](#attestation) - - [`AttestationData`](#attestationdata) - - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - - [Deposits](#deposits) - - [`Deposit`](#deposit) - - [`DepositData`](#depositdata) - - [`DepositInput`](#depositinput) - - [Exits](#exits) - - [`Exit`](#exit) - - [Beacon chain blocks](#beacon-chain-blocks) - - [`BeaconBlock`](#beaconblock) - - [`BeaconBlockBody`](#beaconblockbody) - - [`ProposalSignedData`](#proposalsigneddata) - - [Beacon chain state](#beacon-chain-state) - - [`BeaconState`](#beaconstate) - - [`Validator`](#validator) - - [`Crosslink`](#crosslink) - - [`PendingAttestation`](#pendingattestation) - - [`Fork`](#fork) - - [`Eth1Data`](#eth1data) - - [`Eth1DataVote`](#eth1datavote) - - [Custom Types](#custom-types) - - [Helper functions](#helper-functions) - - [`hash`](#hash) - - [`hash_tree_root`](#hashtreeroot) - - [`slot_to_epoch`](#slottoepoch) - - [`get_previous_epoch`](#getpreviousepoch) - - [`get_current_epoch`](#getcurrentepoch) - - [`get_epoch_start_slot`](#getepochstartslot) - - [`is_active_validator`](#isactivevalidator) - - [`get_active_validator_indices`](#getactivevalidatorindices) - - [`shuffle`](#shuffle) - - [`split`](#split) - - [`get_epoch_committee_count`](#getepochcommitteecount) - - [`get_shuffling`](#getshuffling) - - [`get_previous_epoch_committee_count`](#getpreviousepochcommitteecount) - - [`get_current_epoch_committee_count`](#getcurrentepochcommitteecount) - - [`get_next_epoch_committee_count`](#getnextepochcommitteecount) - - [`get_crosslink_committees_at_slot`](#getcrosslinkcommitteesatslot) - - [`get_block_root`](#getblockroot) - - [`get_randao_mix`](#getrandaomix) - - [`get_active_index_root`](#getactiveindexroot) - - [`generate_seed`](#generateseed) - - [`get_beacon_proposer_index`](#getbeaconproposerindex) - - [`merkle_root`](#merkleroot) - - [`get_attestation_participants`](#getattestationparticipants) - - [`is_power_of_two`](#ispoweroftwo) - - [`int_to_bytes1`, `int_to_bytes2`, ...](#inttobytes1-inttobytes2) - - [`get_effective_balance`](#geteffectivebalance) - - [`get_total_balance`](#gettotalbalance) - - [`get_fork_version`](#getforkversion) - - [`get_domain`](#getdomain) - - [`get_bitfield_bit`](#getbitfieldbit) - - [`verify_bitfield`](#verifybitfield) - - [`verify_slashable_attestation`](#verifyslashableattestation) - - [`is_double_vote`](#isdoublevote) - - [`is_surround_vote`](#issurroundvote) - - [`integer_squareroot`](#integersquareroot) - - [`get_entry_exit_effect_epoch`](#getentryexiteffectepoch) - - [`bls_verify`](#blsverify) - - [`bls_verify_multiple`](#blsverifymultiple) - - [`bls_aggregate_pubkeys`](#blsaggregatepubkeys) - - [`validate_proof_of_possession`](#validateproofofpossession) - - [`process_deposit`](#processdeposit) - - [Routines for updating validator status](#routines-for-updating-validator-status) - - [`activate_validator`](#activatevalidator) - - [`initiate_validator_exit`](#initiatevalidatorexit) - - [`exit_validator`](#exitvalidator) - - [`penalize_validator`](#penalizevalidator) - - [`prepare_validator_for_withdrawal`](#preparevalidatorforwithdrawal) - - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - - [Deposit arguments](#deposit-arguments) - - [Withdrawal credentials](#withdrawal-credentials) - - [`Deposit` logs](#deposit-logs) - - [`ChainStart` log](#chainstart-log) - - [Vyper code](#vyper-code) - - [On startup](#on-startup) - - [Beacon chain processing](#beacon-chain-processing) - - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Per-slot processing](#per-slot-processing) - - [Slot](#slot) - - [Block roots](#block-roots) - - [Per-block processing](#per-block-processing) - - [Slot](#slot-1) - - [Proposer signature](#proposer-signature) - - [RANDAO](#randao) - - [Eth1 data](#eth1-data) - - [Operations](#operations) - - [Proposer slashings](#proposer-slashings-1) - - [Attester slashings](#attester-slashings-1) - - [Attestations](#attestations-1) - - [Deposits](#deposits-1) - - [Exits](#exits-1) - - [Per-epoch processing](#per-epoch-processing) - - [Helpers](#helpers) - - [Eth1 data](#eth1-data-1) - - [Justification](#justification) - - [Crosslinks](#crosslinks) - - [Rewards and penalties](#rewards-and-penalties) - - [Justification and finalization](#justification-and-finalization) - - [Attestation inclusion](#attestation-inclusion) - - [Crosslinks](#crosslinks-1) - - [Ejections](#ejections) - - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) - - [Final updates](#final-updates) - - [State root verification](#state-root-verification) + - [Table of contents](#table-of-contents) + - [Introduction](#introduction) + - [Notation](#notation) + - [Terminology](#terminology) + - [Constants](#constants) + - [Misc](#misc) + - [Deposit contract](#deposit-contract) + - [Gwei values](#gwei-values) + - [Initial values](#initial-values) + - [Time parameters](#time-parameters) + - [State list lengths](#state-list-lengths) + - [Reward and penalty quotients](#reward-and-penalty-quotients) + - [Status flags](#status-flags) + - [Max operations per block](#max-operations-per-block) + - [Signature domains](#signature-domains) + - [Data structures](#data-structures) + - [Beacon chain operations](#beacon-chain-operations) + - [Proposer slashings](#proposer-slashings) + - [`ProposerSlashing`](#proposerslashing) + - [Attester slashings](#attester-slashings) + - [`AttesterSlashing`](#attesterslashing) + - [`SlashableAttestation`](#slashableattestation) + - [Attestations](#attestations) + - [`Attestation`](#attestation) + - [`AttestationData`](#attestationdata) + - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) + - [Deposits](#deposits) + - [`Deposit`](#deposit) + - [`DepositData`](#depositdata) + - [`DepositInput`](#depositinput) + - [Exits](#exits) + - [`Exit`](#exit) + - [Beacon chain blocks](#beacon-chain-blocks) + - [`BeaconBlock`](#beaconblock) + - [`BeaconBlockBody`](#beaconblockbody) + - [`ProposalSignedData`](#proposalsigneddata) + - [Beacon chain state](#beacon-chain-state) + - [`BeaconState`](#beaconstate) + - [`Validator`](#validator) + - [`Crosslink`](#crosslink) + - [`PendingAttestation`](#pendingattestation) + - [`Fork`](#fork) + - [`Eth1Data`](#eth1data) + - [`Eth1DataVote`](#eth1datavote) + - [Custom Types](#custom-types) + - [Helper functions](#helper-functions) + - [`hash`](#hash) + - [`hash_tree_root`](#hash_tree_root) + - [`slot_to_epoch`](#slot_to_epoch) + - [`get_previous_epoch`](#get_previous_epoch) + - [`get_current_epoch`](#get_current_epoch) + - [`get_epoch_start_slot`](#get_epoch_start_slot) + - [`is_active_validator`](#is_active_validator) + - [`get_active_validator_indices`](#get_active_validator_indices) + - [`shuffle`](#shuffle) + - [`split`](#split) + - [`get_epoch_committee_count`](#get_epoch_committee_count) + - [`get_shuffling`](#get_shuffling) + - [`get_previous_epoch_committee_count`](#get_previous_epoch_committee_count) + - [`get_current_epoch_committee_count`](#get_current_epoch_committee_count) + - [`get_next_epoch_committee_count`](#get_next_epoch_committee_count) + - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) + - [`get_block_root`](#get_block_root) + - [`get_randao_mix`](#get_randao_mix) + - [`get_active_index_root`](#get_active_index_root) + - [`generate_seed`](#generate_seed) + - [`get_beacon_proposer_index`](#get_beacon_proposer_index) + - [`merkle_root`](#merkle_root) + - [`get_attestation_participants`](#get_attestation_participants) + - [`is_power_of_two`](#is_power_of_two) + - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) + - [`get_effective_balance`](#get_effective_balance) + - [`get_total_balance`](#get_total_balance) + - [`get_fork_version`](#get_fork_version) + - [`get_domain`](#get_domain) + - [`get_bitfield_bit`](#get_bitfield_bit) + - [`verify_bitfield`](#verify_bitfield) + - [`verify_slashable_attestation`](#verify_slashable_attestation) + - [`is_double_vote`](#is_double_vote) + - [`is_surround_vote`](#is_surround_vote) + - [`integer_squareroot`](#integer_squareroot) + - [`get_entry_exit_effect_epoch`](#get_entry_exit_effect_epoch) + - [`bls_verify`](#bls_verify) + - [`bls_verify_multiple`](#bls_verify_multiple) + - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) + - [`validate_proof_of_possession`](#validate_proof_of_possession) + - [`process_deposit`](#process_deposit) + - [Routines for updating validator status](#routines-for-updating-validator-status) + - [`activate_validator`](#activate_validator) + - [`initiate_validator_exit`](#initiate_validator_exit) + - [`exit_validator`](#exit_validator) + - [`penalize_validator`](#penalize_validator) + - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) + - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) + - [Deposit arguments](#deposit-arguments) + - [Withdrawal credentials](#withdrawal-credentials) + - [`Deposit` logs](#deposit-logs) + - [`ChainStart` log](#chainstart-log) + - [Vyper code](#vyper-code) + - [On startup](#on-startup) + - [Beacon chain processing](#beacon-chain-processing) + - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) + - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Per-slot processing](#per-slot-processing) + - [Slot](#slot) + - [Block roots](#block-roots) + - [Per-block processing](#per-block-processing) + - [Slot](#slot-1) + - [Proposer signature](#proposer-signature) + - [RANDAO](#randao) + - [Eth1 data](#eth1-data) + - [Operations](#operations) + - [Proposer slashings](#proposer-slashings-1) + - [Attester slashings](#attester-slashings-1) + - [Attestations](#attestations-1) + - [Deposits](#deposits-1) + - [Exits](#exits-1) + - [Per-epoch processing](#per-epoch-processing) + - [Helpers](#helpers) + - [Eth1 data](#eth1-data-1) + - [Justification](#justification) + - [Crosslinks](#crosslinks) + - [Rewards and penalties](#rewards-and-penalties) + - [Justification and finalization](#justification-and-finalization) + - [Attestation inclusion](#attestation-inclusion) + - [Crosslinks](#crosslinks-1) + - [Ejections](#ejections) + - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) + - [Final updates](#final-updates) + - [State root verification](#state-root-verification) - [References](#references) - - [Normative](#normative) - - [Informative](#informative) + - [Normative](#normative) + - [Informative](#informative) - [Copyright](#copyright) From ca098f8cfa5c0ab0daa38c784ee4e563fd1c9fdf Mon Sep 17 00:00:00 2001 From: mratsim Date: Thu, 7 Feb 2019 22:19:04 +0100 Subject: [PATCH 49/57] Prevent changing another TOC --- specs/validator/0_beacon-chain-validator.md | 94 ++++++++++----------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index b8db2117c9..744df690fb 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -7,53 +7,53 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Ethereum 2.0 Phase 0 -- Honest Validator](#ethereum-20-phase-0----honest-validator) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [Prerequisites](#prerequisites) - - [Constants](#constants) - - [Misc](#misc) - - [Becoming a validator](#becoming-a-validator) - - [Initialization](#initialization) - - [BLS public key](#bls-public-key) - - [BLS withdrawal key](#bls-withdrawal-key) - - [Submit deposit](#submit-deposit) - - [Process deposit](#process-deposit) - - [Validator index](#validator-index) - - [Activation](#activation) - - [Beacon chain responsibilities](#beacon-chain-responsibilities) - - [Block proposal](#block-proposal) - - [Block header](#block-header) - - [Slot](#slot) - - [Parent root](#parent-root) - - [State root](#state-root) - - [Randao reveal](#randao-reveal) - - [Eth1 Data](#eth1-data) - - [Signature](#signature) - - [Block body](#block-body) - - [Proposer slashings](#proposer-slashings) - - [Attester slashings](#attester-slashings) - - [Attestations](#attestations) - - [Deposits](#deposits) - - [Exits](#exits) - - [Attestations](#attestations-1) - - [Attestation data](#attestation-data) - - [Slot](#slot-1) - - [Shard](#shard) - - [Beacon block root](#beacon-block-root) - - [Epoch boundary root](#epoch-boundary-root) - - [Shard block root](#shard-block-root) - - [Latest crosslink root](#latest-crosslink-root) - - [Justified epoch](#justified-epoch) - - [Justified block root](#justified-block-root) - - [Construct attestation](#construct-attestation) - - [Data](#data) - - [Aggregation bitfield](#aggregation-bitfield) - - [Custody bitfield](#custody-bitfield) - - [Aggregate signature](#aggregate-signature) - - [Responsibility lookahead](#responsibility-lookahead) - - [How to avoid slashing](#how-to-avoid-slashing) - - [Proposer slashing](#proposer-slashing) - - [Attester slashing](#attester-slashing) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Prerequisites](#prerequisites) + - [Constants](#constants) + - [Misc](#misc) + - [Becoming a validator](#becoming-a-validator) + - [Initialization](#initialization) + - [BLS public key](#bls-public-key) + - [BLS withdrawal key](#bls-withdrawal-key) + - [Submit deposit](#submit-deposit) + - [Process deposit](#process-deposit) + - [Validator index](#validator-index) + - [Activation](#activation) + - [Beacon chain responsibilities](#beacon-chain-responsibilities) + - [Block proposal](#block-proposal) + - [Block header](#block-header) + - [Slot](#slot) + - [Parent root](#parent-root) + - [State root](#state-root) + - [Randao reveal](#randao-reveal) + - [Eth1 Data](#eth1-data) + - [Signature](#signature) + - [Block body](#block-body) + - [Proposer slashings](#proposer-slashings) + - [Attester slashings](#attester-slashings) + - [Attestations](#attestations) + - [Deposits](#deposits) + - [Exits](#exits) + - [Attestations](#attestations-1) + - [Attestation data](#attestation-data) + - [Slot](#slot-1) + - [Shard](#shard) + - [Beacon block root](#beacon-block-root) + - [Epoch boundary root](#epoch-boundary-root) + - [Shard block root](#shard-block-root) + - [Latest crosslink root](#latest-crosslink-root) + - [Justified epoch](#justified-epoch) + - [Justified block root](#justified-block-root) + - [Construct attestation](#construct-attestation) + - [Data](#data) + - [Aggregation bitfield](#aggregation-bitfield) + - [Custody bitfield](#custody-bitfield) + - [Aggregate signature](#aggregate-signature) + - [Responsibility lookahead](#responsibility-lookahead) + - [How to avoid slashing](#how-to-avoid-slashing) + - [Proposer slashing](#proposer-slashing) + - [Attester slashing](#attester-slashing) From 086df84bdc6805d1c0ad1e1c4ff953fa1bd3802b Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 7 Feb 2019 19:34:54 -0600 Subject: [PATCH 50/57] Attestation data contains latest crosslink, not just latest crosslink data The reason to do this is that it makes it calculable from inside an attestation how many epochs the attestation spans over, which is needed for proof of custody reasons. It's a relatively small change and so arguably easier to do now than to do as a patch in phase 1. Note that this changes the meaning of latest_crosslink.epoch, from the epoch when the latest crosslink was included to the epoch that the latest crosslink was for. This affects the line: * `state.latest_crosslinks[shard].epoch > state.validator_registry_update_epoch` for every shard number `shard` in `[(state.current_epoch_start_shard + i) % SHARD_COUNT for i in range(get_current_epoch_committee_count(state))]` (that is, for every shard in the current committees) But this may actually make it _more_ correct, as it means that in the case where >512 shards are processed per epoch, and so a committee from the previous epoch could get finalized in the current epoch, that would no longer count toward every shard having received a "new" crosslink. --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f9c155c533..b98bdf3b83 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -362,8 +362,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'epoch_boundary_root': 'bytes32', # Shard block's hash of root 'shard_block_root': 'bytes32', - # Last crosslink's hash of root - 'latest_crosslink_root': 'bytes32', + # Last crosslink + 'latest_crosslink': Crosslink, # Last justified epoch in the beacon state 'justified_epoch': 'uint64', # Hash of the last justified beacon block @@ -1688,7 +1688,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. -* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[attestation.data.shard].shard_block_root`. +* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))` * Verify bitfields and aggregate signature: ```python @@ -1860,7 +1860,7 @@ Finally, update the following: For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch))`, let `crosslink_committees_at_slot = get_crosslink_committees_at_slot(state, slot)`. For every `(crosslink_committee, shard)` in `crosslink_committees_at_slot`, compute: -* Set `state.latest_crosslinks[shard] = Crosslink(epoch=current_epoch, shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. +* Set `state.latest_crosslinks[shard] = Crosslink(epoch=slot_to_epoch(slot), shard_block_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. #### Rewards and penalties From f797826ee29767ef3133a97ec1f28d347553ede7 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 21:51:56 -0600 Subject: [PATCH 51/57] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- 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 7167b93a61..50daad513d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -708,7 +708,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: i See the 'generalized domain' algorithm on page 3. """ for round in range(round_count): - pivot = bytes_to_int(hash(seed + int_to_bytes1(round))) % list_size + pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size flip = (pivot - index) % list_size position = max(index, flip) source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256)) From 1c6ccac8fc1f02079f49bf9fd715b64764c304e0 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 7 Feb 2019 21:55:33 -0600 Subject: [PATCH 52/57] SHUFFLE_ROUND_COUNT as global constant --- specs/core/0_beacon-chain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 50daad513d..a7a21683cd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -185,6 +185,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `MAX_INDICES_PER_SLASHABLE_VOTE` | `2**12` (= 4,096) | votes | | `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals | +| `SHUFFLE_ROUND_COUNT` | 90 | - | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -697,17 +698,15 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ### `get_permuted_index` ```python -def get_permuted_index(index: int, list_size: int, seed: Bytes32, round_count: int=90) -> int: +def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: """ Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy. - Note that ``round_count`` is ``90`` in protocol and parameterized for the shuffling tests. - Utilizes 'swap or not' shuffling found in https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf See the 'generalized domain' algorithm on page 3. """ - for round in range(round_count): + for round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size flip = (pivot - index) % list_size position = max(index, flip) From f0cbacb828baa6ed454bea5d5efe52b0a73b67f5 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 20:22:28 -0800 Subject: [PATCH 53/57] add missing . --- 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 b98bdf3b83..f668393490 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1688,7 +1688,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + EPOCH_LENGTH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if attestation.data.slot >= get_epoch_start_slot(get_current_epoch(state)) else state.previous_justified_epoch`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, get_epoch_start_slot(attestation.data.justified_epoch))`. -* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))` +* Verify that either (i) `state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink` or (ii) `state.latest_crosslinks[attestation.data.shard] == Crosslink(shard_block_root=attestation.data.shard_block_root, epoch=slot_to_epoch(attestation.data.slot))`. * Verify bitfields and aggregate signature: ```python From 6d9581281dba8c8d2cf127ddd07e6202759c405c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 7 Feb 2019 21:25:47 -0700 Subject: [PATCH 54/57] change latest_crosslink_root to latest_crosslink in validator guide --- specs/validator/0_beacon-chain-validator.md | 6 +- wire-api.md | 93 +++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 wire-api.md diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 744df690fb..d1246c6fa2 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -42,7 +42,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Beacon block root](#beacon-block-root) - [Epoch boundary root](#epoch-boundary-root) - [Shard block root](#shard-block-root) - - [Latest crosslink root](#latest-crosslink-root) + - [Latest crosslink](#latest-crosslink) - [Justified epoch](#justified-epoch) - [Justified block root](#justified-block-root) - [Construct attestation](#construct-attestation) @@ -270,9 +270,9 @@ Set `attestation_data.shard_block_root = ZERO_HASH`. _Note:_ This is a stub for phase 0. -##### Latest crosslink root +##### Latest crosslink -Set `attestation_data.latest_crosslink_root = state.latest_crosslinks[shard].shard_block_root` where `state` is the beacon state at `head` and `shard` is the validator's assigned shard. +Set `attestation_data.latest_crosslink = state.latest_crosslinks[shard]` where `state` is the beacon state at `head` and `shard` is the validator's assigned shard. ##### Justified epoch diff --git a/wire-api.md b/wire-api.md new file mode 100644 index 0000000000..0eff650bd9 --- /dev/null +++ b/wire-api.md @@ -0,0 +1,93 @@ +# Phase 0 Wire API [WIP] + +This is the minimal wire API required for Phase 0 of Eth2.0. Note that this is _not_ the wire protocol but the interface right above. Once we settle on the API required, we can specify the underlying protocol. + +All API methods are specified as the plural `list` version, assuming that if singular objects are sent or requested that the input will just be a list of length 1. + +"Bad form" is any action that is not explicitly against the protocol but is not in the best interest of one's peers or the protocol in general. Messages/requests that are considered bad form may reduce the reputation of the sending node and may result in being dropped. + +## Network topology + +Ethereum 2.0 network topology consists of a pubsub mapping of peers to "topics". These topics along with peer mappings effectively form subnets. + +The primary topics of core protocol consideration are: +* `beacon`: All messages for the beacon chain are mapped to topic `beacon`. +* `shard-{number}` for all integers, `number` in `range(SHARD_SUBNET_COUNT)`: Messages for a given shard defined by `shard_number` are mapped to topic `shard-{shard_number % SHARD_SUBNET_COUNT}`. + +We use `discv5` to discover peers of select topics, and we use `gossipsub`, a libp2p routing protocol, to route messages of a particular topic to the subnet in question. + +Note: attempting to broadcast or request messages about a topic not subscribed to by the peer is considered bad form. For example, running `send_attestations(attestations)` where one or more of the attestations have `attestation.data.shard == 5` to a peer not subscribed to `shard-5` might result in that peer dropping the node. + +## Dependencies + +This document depends on: +* [SSZ spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) +* [Phase 0 spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md) + +## API + +### Sync + +The following is a basic sync protocol akin to eth1.0. _This is very likely to change pending input from those intimately familiar with the pain points of 1.0 sync_. + +`status` message is sent in the initial handshake between two peers. After handshake and status exchange, the peer with higher `latest_finalized_epoch` or, if epochs are equal, the higher `best_slot` sends a list of `beacon_block_roots` via `send_beacon_block_roots`. + +Status handshake fields: +* `protocol_version` +* `network_id` +* `latest_finalized_root` +* `latest_finalized_epoch` +* `best_root` +* `best_slot` + +### Beacon Blocks + +Supported pubsub topics: +* `beacon` + +The following definitions are used in the API: +* `block_header`: a serialized `BeaconBlock` in which `BeaconBlock.body` is the `hash_tree_root` of the associated `BeaconBlockBody`. +* `block_body`: a serialied `BeaconBlockBody`. +* `block_root`: the `hash_tree_root` of a `BeaconBlock`. + +API: +* `send_beacon_block_roots(block_roots)`: Sends list of `block_roots` to peer. +* `send_beacon_block_headers(block_headers)`: Sends list of `block_headers` to peer. +* `request_beacon_block_headers(block_roots)`: Requests the associated `block_headers` for the given `block_roots` from peer. +* `send_beacon_block_bodies(block_bodies)`: Sends list of `block_bodies` to peer. +* `request_beacon_block_bodies(block_roots)`: Requests the associated `block_bodies` for the given `block_roots` from peer. + +Notes: +* It is assumed that both the associated `BeaconBlock` and `BeaconBlockBody` can be looked up via `block_root`. + +### Attestations + +Supported pubsub topics: +* `beacon` +* all `shard-{number}` topics + +The following definitions are used in the API: +* `attestation`: a serialized `Attestation` with full serialized `AttestationData` for `Attestation.data`. + +API: +* `send_attestations(attestations)`: Sends list of `attestations` to peer. + +Notes: +* It is expected that an attestation is only broadcast to either `beacon` topic or `shard-{attestation.data.shard}` topic. Broadcasting to mismatched shard topics is considered bad form. +* It is expected that only aggregate attestations are broadcast to the `beacon` topic. Repeated broadcasting of attestations with a signle signer to the `beacon` topic is considered bad form. +* There is a shard subnet design decision here. Due to the likelihood of `attestation.data` to be highly repeated across a committee during a given slot, it could be valuable to just pass the `attestation` with a `root` in the `attestation.data` field. If the recipient does not already have an `AttestationData` for the received `root`, then the recipient would explicitly request the root. This reduces the total data passed by 184 bytes in the case that the recipient has already received the `attestation.data` but increases the rounds of communication when they haven't. +* We do not currently specify a getter method for an attestation by its `root`. Due to the diverse ways attestations might both be aggregated and stored, it is not feasible to reliably lookup via a `root`. The attestations that a client cares about are (1) those that made it on-chain into a `BeaconBlock` and (2) the most recent set of attestations being actively broadcast on the wire. We might provide a `request_attestations(slot)` or `request_attestations(epoch)` but do not provide it in this minimal API specification. + +### Exits + +Supported pubsub topics: +* `beacon` + +The following definitions are used in the API: +* `exit`: a serialized `Exit`. + +API: +* `send_exit(exit)`: Sends `exit` to peer. + +Notes: +* We do not specify a getter for an exit by its `root`. Standard usage is for \ No newline at end of file From 92471046a1879361d5c87443da0890db0e7d7846 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 08:28:41 -0700 Subject: [PATCH 55/57] remove wire protocol doc --- wire-api.md | 93 ----------------------------------------------------- 1 file changed, 93 deletions(-) delete mode 100644 wire-api.md diff --git a/wire-api.md b/wire-api.md deleted file mode 100644 index 0eff650bd9..0000000000 --- a/wire-api.md +++ /dev/null @@ -1,93 +0,0 @@ -# Phase 0 Wire API [WIP] - -This is the minimal wire API required for Phase 0 of Eth2.0. Note that this is _not_ the wire protocol but the interface right above. Once we settle on the API required, we can specify the underlying protocol. - -All API methods are specified as the plural `list` version, assuming that if singular objects are sent or requested that the input will just be a list of length 1. - -"Bad form" is any action that is not explicitly against the protocol but is not in the best interest of one's peers or the protocol in general. Messages/requests that are considered bad form may reduce the reputation of the sending node and may result in being dropped. - -## Network topology - -Ethereum 2.0 network topology consists of a pubsub mapping of peers to "topics". These topics along with peer mappings effectively form subnets. - -The primary topics of core protocol consideration are: -* `beacon`: All messages for the beacon chain are mapped to topic `beacon`. -* `shard-{number}` for all integers, `number` in `range(SHARD_SUBNET_COUNT)`: Messages for a given shard defined by `shard_number` are mapped to topic `shard-{shard_number % SHARD_SUBNET_COUNT}`. - -We use `discv5` to discover peers of select topics, and we use `gossipsub`, a libp2p routing protocol, to route messages of a particular topic to the subnet in question. - -Note: attempting to broadcast or request messages about a topic not subscribed to by the peer is considered bad form. For example, running `send_attestations(attestations)` where one or more of the attestations have `attestation.data.shard == 5` to a peer not subscribed to `shard-5` might result in that peer dropping the node. - -## Dependencies - -This document depends on: -* [SSZ spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) -* [Phase 0 spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md) - -## API - -### Sync - -The following is a basic sync protocol akin to eth1.0. _This is very likely to change pending input from those intimately familiar with the pain points of 1.0 sync_. - -`status` message is sent in the initial handshake between two peers. After handshake and status exchange, the peer with higher `latest_finalized_epoch` or, if epochs are equal, the higher `best_slot` sends a list of `beacon_block_roots` via `send_beacon_block_roots`. - -Status handshake fields: -* `protocol_version` -* `network_id` -* `latest_finalized_root` -* `latest_finalized_epoch` -* `best_root` -* `best_slot` - -### Beacon Blocks - -Supported pubsub topics: -* `beacon` - -The following definitions are used in the API: -* `block_header`: a serialized `BeaconBlock` in which `BeaconBlock.body` is the `hash_tree_root` of the associated `BeaconBlockBody`. -* `block_body`: a serialied `BeaconBlockBody`. -* `block_root`: the `hash_tree_root` of a `BeaconBlock`. - -API: -* `send_beacon_block_roots(block_roots)`: Sends list of `block_roots` to peer. -* `send_beacon_block_headers(block_headers)`: Sends list of `block_headers` to peer. -* `request_beacon_block_headers(block_roots)`: Requests the associated `block_headers` for the given `block_roots` from peer. -* `send_beacon_block_bodies(block_bodies)`: Sends list of `block_bodies` to peer. -* `request_beacon_block_bodies(block_roots)`: Requests the associated `block_bodies` for the given `block_roots` from peer. - -Notes: -* It is assumed that both the associated `BeaconBlock` and `BeaconBlockBody` can be looked up via `block_root`. - -### Attestations - -Supported pubsub topics: -* `beacon` -* all `shard-{number}` topics - -The following definitions are used in the API: -* `attestation`: a serialized `Attestation` with full serialized `AttestationData` for `Attestation.data`. - -API: -* `send_attestations(attestations)`: Sends list of `attestations` to peer. - -Notes: -* It is expected that an attestation is only broadcast to either `beacon` topic or `shard-{attestation.data.shard}` topic. Broadcasting to mismatched shard topics is considered bad form. -* It is expected that only aggregate attestations are broadcast to the `beacon` topic. Repeated broadcasting of attestations with a signle signer to the `beacon` topic is considered bad form. -* There is a shard subnet design decision here. Due to the likelihood of `attestation.data` to be highly repeated across a committee during a given slot, it could be valuable to just pass the `attestation` with a `root` in the `attestation.data` field. If the recipient does not already have an `AttestationData` for the received `root`, then the recipient would explicitly request the root. This reduces the total data passed by 184 bytes in the case that the recipient has already received the `attestation.data` but increases the rounds of communication when they haven't. -* We do not currently specify a getter method for an attestation by its `root`. Due to the diverse ways attestations might both be aggregated and stored, it is not feasible to reliably lookup via a `root`. The attestations that a client cares about are (1) those that made it on-chain into a `BeaconBlock` and (2) the most recent set of attestations being actively broadcast on the wire. We might provide a `request_attestations(slot)` or `request_attestations(epoch)` but do not provide it in this minimal API specification. - -### Exits - -Supported pubsub topics: -* `beacon` - -The following definitions are used in the API: -* `exit`: a serialized `Exit`. - -API: -* `send_exit(exit)`: Sends `exit` to peer. - -Notes: -* We do not specify a getter for an exit by its `root`. Standard usage is for \ No newline at end of file From 334d47714df2b3f41b82ad2adea2aa6636369c3e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 10:35:57 -0700 Subject: [PATCH 56/57] fix a couple of nitpicks before release --- specs/core/0_beacon-chain.md | 7 ++++--- specs/validator/0_beacon-chain-validator.md | 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 21a991c42e..ebc12324c4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -658,9 +658,10 @@ def get_previous_epoch(state: BeaconState) -> EpochNumber: Return the previous epoch of the given ``state``. If the current epoch is ``GENESIS_EPOCH``, return ``GENESIS_EPOCH``. """ - if slot_to_epoch(state.slot) > GENESIS_EPOCH: - return slot_to_epoch(state.slot) - 1 - return slot_to_epoch(state.slot) + current_epoch = get_current_epoch(state) + if current_epoch == GENESIS_EPOCH: + return GENESIS_EPOCH + return current_epoch - 1 ``` ### `get_current_epoch` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index d1246c6fa2..5b8a93af91 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -385,7 +385,7 @@ def get_next_epoch_committee_assignments( return potential_assignments ``` -`get_next_epoch_committee_assignments` should be called at the beginning of each epoch to plan for the next epoch. A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for a future shuffling involves noting at which slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). +`get_next_epoch_committee_assignments` should be called at the start of each epoch to get potential assignments for the next epoch (slots during `current_epoch + 1`). A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). ## How to avoid slashing From 663d38e9c8382e110022de6fcc4ca191ecadd40e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 8 Feb 2019 11:54:17 -0700 Subject: [PATCH 57/57] simplify get_next_epoch_committee_assignment by adding registry_change arg --- specs/validator/0_beacon-chain-validator.md | 56 ++++++++++----------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 5b8a93af91..93c3fb4083 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -341,15 +341,16 @@ There are three possibilities for the shuffling at the next epoch: Either (2) or (3) occurs if (1) fails. The choice between (2) and (3) is deterministic based upon `epochs_since_last_registry_update`. -`get_crosslink_committees_at_slot` is designed to be able to query slots in the next epoch. When querying slots in the next epoch there are two options -- with and without a `registry_change` -- which is the optional third parameter of the function. The following helper can be used to get the potential crosslink committees in the next epoch for a given `validator_index`. This function returns a list of 2 shard committee tuples. +`get_crosslink_committees_at_slot` is designed to be able to query slots in the next epoch. When querying slots in the next epoch there are two options -- with and without a `registry_change` -- which is the optional third parameter of the function. The following helper can be used to get the potential crosslink committee assignments in the next epoch for a given `validator_index` and `registry_change`. ```python -def get_next_epoch_committee_assignments( +def get_next_epoch_committee_assignment( state: BeaconState, - validator_index: ValidatorIndex) -> List[Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]]: + validator_index: ValidatorIndex, + registry_change: bool) -> Tuple[List[ValidatorIndex], ShardNumber, SlotNumber, bool]: """ - Return a list of the two possible committee assignments for ``validator_index`` at the next epoch. - Possible committee ``assignment`` is of the form (List[ValidatorIndex], ShardNumber, SlotNumber, bool). + Return the committee assignment in the next epoch for ``validator_index`` and ``registry_change``. + ``assignment`` returned is a tuple of the following form: * ``assignment[0]`` is the list of validators in the committee * ``assignment[1]`` is the shard to which the committee is assigned * ``assignment[2]`` is the slot at which the committee is assigned @@ -359,33 +360,28 @@ def get_next_epoch_committee_assignments( current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 next_epoch_start_slot = get_epoch_start_slot(next_epoch) - potential_assignments = [] - for registry_change in [False, True]: - for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): - crosslink_committees = get_crosslink_committees_at_slot( - state, - slot, - registry_change=registry_change, - ) - selected_committees = [ - committee # Tuple[List[ValidatorIndex], ShardNumber] - for committee in crosslink_committees - if validator_index in committee[0] - ] - if len(selected_committees) > 0: - assignment = selected_committees[0] - assignment += (slot,) - first_committee_at_slot = crosslink_committees[0][0] # List[ValidatorIndex] - is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index - assignment += (is_proposer,) - - potential_assignments.append(assignment) - break - - return potential_assignments + for slot in range(next_epoch_start_slot, next_epoch_start_slot + EPOCH_LENGTH): + crosslink_committees = get_crosslink_committees_at_slot( + state, + slot, + registry_change=registry_change, + ) + selected_committees = [ + committee # Tuple[List[ValidatorIndex], ShardNumber] + for committee in crosslink_committees + if validator_index in committee[0] + ] + if len(selected_committees) > 0: + validators = selected_committees[0][0] + shard = selected_committees[0][1] + first_committee_at_slot = crosslink_committees[0][0] # List[ValidatorIndex] + is_proposer = first_committee_at_slot[slot % len(first_committee_at_slot)] == validator_index + + assignment = (validators, shard, slot, is_proposer) + return assignment ``` -`get_next_epoch_committee_assignments` should be called at the start of each epoch to get potential assignments for the next epoch (slots during `current_epoch + 1`). A validator should always plan for both values of `registry_change` as a possibility unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). +`get_next_epoch_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (slots during `current_epoch + 1`). A validator should always plan for assignments from both values of `registry_change` unless the validator can concretely eliminate one of the options. Planning for future assignments involves noting at which future slot one might have to attest and propose and also which shard one should begin syncing (in phase 1+). ## How to avoid slashing