From 33a05109eaa79db5f8d3b6fd5c9ce42fa33422b1 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 7 Mar 2019 23:56:03 +0100 Subject: [PATCH 01/23] Fair proposer sampling I think we want `first_committee[epoch % len(first_committee)]` as opposed to `first_committee[slot % len(first_committee)]`. The reason is that if the shuffling happens infrequently and `len(first_committee)` is a multiple of `SLOTS_PER_EPOCH` then the proposers will not be sampled fairly. Taking this logic further, we may want to avoiding always picking the proposer from `first_committee`, e.g.: ``` validators_at_slot = [] for crosslink_committee, _ in get_crosslink_committees_at_slot(state, slot, registry_change): validators_at_slot.append(crosslink_committee) return validators_at_slot[epoch % len(validators_at_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 898fc3c86..dfce48f96 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1002,7 +1002,7 @@ def get_beacon_proposer_index(state: BeaconState, assert previous_epoch <= epoch <= next_epoch first_committee, _ = get_crosslink_committees_at_slot(state, slot, registry_change)[0] - return first_committee[slot % len(first_committee)] + return first_committee[epoch % len(first_committee)] ``` ### `merkle_root` From 1f147486fccf4557c5cfd74f4a4f47a244e6393c Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Mar 2019 09:57:09 +0100 Subject: [PATCH 02/23] Semantic fork versions and signature domains Fix #706 --- specs/core/0_beacon-chain.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 898fc3c86..cfa4782a5 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -268,12 +268,12 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | Name | Value | | - | - | -| `DOMAIN_BEACON_BLOCK` | `0` | -| `DOMAIN_RANDAO` | `1` | -| `DOMAIN_ATTESTATION` | `2` | -| `DOMAIN_DEPOSIT` | `3` | -| `DOMAIN_VOLUNTARY_EXIT` | `4` | -| `DOMAIN_TRANSFER` | `5` | +| `DOMAIN_BEACON_BLOCK` | `int_to_bytes4(0)` | +| `DOMAIN_RANDAO` | `int_to_bytes4(1)` | +| `DOMAIN_ATTESTATION` | `int_to_bytes4(2)` | +| `DOMAIN_DEPOSIT` | `int_to_bytes4(3)` | +| `DOMAIN_VOLUNTARY_EXIT` | `int_to_bytes4(4)` | +| `DOMAIN_TRANSFER` | `int_to_bytes4(5)` | ## Data structures @@ -288,9 +288,9 @@ The types are defined topologically to aid in facilitating an executable version ```python { # Previous fork version - 'previous_version': 'uint64', + 'previous_version': 'bytes4', # Current fork version - 'current_version': 'uint64', + 'current_version': 'bytes4', # Fork epoch number 'epoch': 'uint64', } @@ -1107,7 +1107,7 @@ def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> G ```python def get_fork_version(fork: Fork, - epoch: Epoch) -> int: + epoch: Epoch) -> bytes8: """ Return the fork version of the given ``epoch``. """ @@ -1122,12 +1122,11 @@ def get_fork_version(fork: Fork, ```python def get_domain(fork: Fork, epoch: Epoch, - domain_type: int) -> int: + domain_type: bytes4) -> bytes8: """ Get the domain number that represents the fork meta and signature domain. """ - fork_version = get_fork_version(fork, epoch) - return fork_version * 2**32 + domain_type + return get_fork_version(fork, epoch) + domain_type ``` ### `get_bitfield_bit` From a51d7d5db591ada9b7ae01ba37fe4fecf6ca0653 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Mar 2019 10:50:45 +0100 Subject: [PATCH 03/23] 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 cfa4782a5..7b7c58ab0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1107,7 +1107,7 @@ def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> G ```python def get_fork_version(fork: Fork, - epoch: Epoch) -> bytes8: + epoch: Epoch) -> bytes4: """ Return the fork version of the given ``epoch``. """ From 3aedf1226a5913b3545f35cefcca058a9dfdf9a1 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Mar 2019 17:36:58 +0100 Subject: [PATCH 04/23] Update 0_beacon-chain.md Did I get the type casting OK @hwwhww? --- 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 7b7c58ab0..9efc73f1a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1122,11 +1122,11 @@ def get_fork_version(fork: Fork, ```python def get_domain(fork: Fork, epoch: Epoch, - domain_type: bytes4) -> bytes8: + domain_type: bytes4) -> uint64: """ Get the domain number that represents the fork meta and signature domain. """ - return get_fork_version(fork, epoch) + domain_type + return bytes_to_int(get_fork_version(fork, epoch) + domain_type)) ``` ### `get_bitfield_bit` From ecd93468a233ba54af5a7a1492a195c4b3a18db1 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 8 Mar 2019 17:38:19 +0100 Subject: [PATCH 05/23] 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 9efc73f1a..900708e7a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1122,7 +1122,7 @@ def get_fork_version(fork: Fork, ```python def get_domain(fork: Fork, epoch: Epoch, - domain_type: bytes4) -> uint64: + domain_type: bytes4) -> int: """ Get the domain number that represents the fork meta and signature domain. """ From 02428ec2520febb41f5be19fb6434d270d49586a Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 10 Mar 2019 13:25:57 +0100 Subject: [PATCH 06/23] Do not check withdrawal credentials for existing validators We should not invalidate blocks that contain a deposit with an inconsistent withdrawal credential as that would stall the chain. --- specs/core/0_beacon-chain.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c88155f77..25e8085de 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1341,10 +1341,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: state.validator_balances.append(amount) else: # Increase balance by deposit amount - index = validator_pubkeys.index(pubkey) - assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - - state.validator_balances[index] += amount + state.validator_balances[validator_pubkeys.index(pubkey)] += amount ``` ### Routines for updating validator status From 0704297480b5706927999e4c337071a0e8d4abe4 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 11 Mar 2019 17:28:39 +0100 Subject: [PATCH 07/23] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 25e8085de..5d824f288 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1303,21 +1303,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: # create an invalid Merkle branch, it may admit an invalid deposit # object, and we need to be able to skip over it state.deposit_index += 1 - - # Verify the proof of possession - proof_is_valid = bls_verify( - pubkey=deposit_input.pubkey, - message_hash=signed_root(deposit_input), - signature=deposit_input.proof_of_possession, - domain=get_domain( - state.fork, - get_current_epoch(state), - DOMAIN_DEPOSIT, - ) - ) - - if not proof_is_valid: - return validator_pubkeys = [v.pubkey for v in state.validator_registry] pubkey = deposit_input.pubkey @@ -1325,6 +1310,19 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: withdrawal_credentials = deposit_input.withdrawal_credentials if pubkey not in validator_pubkeys: + # Verify the proof of possession + if not bls_verify( + pubkey=deposit_input.pubkey, + message_hash=signed_root(deposit_input), + signature=deposit_input.proof_of_possession, + domain=get_domain( + state.fork, + get_current_epoch(state), + DOMAIN_DEPOSIT, + ) + ): + return + # Add new validator validator = Validator( pubkey=pubkey, From f06a3b82e7f97f12f1f70e07f5a09b854755b8a1 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 11 Mar 2019 14:38:10 -0600 Subject: [PATCH 08/23] cache current and previous justified root in state --- specs/core/0_beacon-chain.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ceca50962..0a86db3f9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -613,6 +613,8 @@ The types are defined topologically to aid in facilitating an executable version 'current_epoch_attestations': [PendingAttestation], 'previous_justified_epoch': 'uint64', 'justified_epoch': 'uint64', + 'previous_justified_root': 'bytes32', + 'justified_root': 'bytes32', 'justification_bitfield': 'uint64', 'finalized_epoch': 'uint64', @@ -1546,6 +1548,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], current_epoch_attestations=[], previous_justified_epoch=GENESIS_EPOCH, justified_epoch=GENESIS_EPOCH, + previous_justified_root=ZERO_HASH, + justified_root=ZERO_HASH, justification_bitfield=0, finalized_epoch=GENESIS_EPOCH, @@ -1830,7 +1834,9 @@ def update_justification_and_finalization(state: BeaconState) -> None: # Rotate justified epochs state.previous_justified_epoch = state.justified_epoch + state.previous_justified_root = state.justified_root state.justified_epoch = new_justified_epoch + state.justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) ``` #### Crosslinks @@ -2374,16 +2380,15 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert state.slot <= attestation.data.slot + SLOTS_PER_EPOCH # Can't submit attestations too quickly assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot - # Verify that the justified epoch is correct, case 1: current epoch attestations + # Verify that the justified epoch/root is correct if slot_to_epoch(attestation.data.slot) >= get_current_epoch(state): + # Case 1: current epoch attestations assert attestation.data.justified_epoch == state.justified_epoch - # Case 2: previous epoch attestations + assert attestation.data.justified_block_root == state.justified_root else: + # Case 2: previous epoch attestations assert attestation.data.justified_epoch == state.previous_justified_epoch - # Check that the justified block root is correct - assert attestation.data.justified_block_root == get_block_root( - state, get_epoch_start_slot(attestation.data.justified_epoch) - ) + assert attestation.data.justified_block_root == state.previous_justified_root # Check that the crosslink data is valid acceptable_crosslink_data = { # Case 1: Latest crosslink matches the one in the state From 3916643ef6dc663f4c0da91efd86e351721bdf7d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 11 Mar 2019 18:23:17 -0600 Subject: [PATCH 09/23] only update justified epoch/root if changed --- specs/core/0_beacon-chain.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0a86db3f9..47dd765af 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1835,8 +1835,9 @@ def update_justification_and_finalization(state: BeaconState) -> None: # Rotate justified epochs state.previous_justified_epoch = state.justified_epoch state.previous_justified_root = state.justified_root - state.justified_epoch = new_justified_epoch - state.justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) + if new_justified_epoch != state.justified_epoch: + state.justified_epoch = new_justified_epoch + state.justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) ``` #### Crosslinks From 578bf02b6f76339b8a4fb21249326b4c46f7b2f4 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Mar 2019 10:17:34 +0000 Subject: [PATCH 10/23] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 73 +++++++++++++++++------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 47dd765af..7eb9752bb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -335,22 +335,19 @@ The types are defined topologically to aid in facilitating an executable version ```python { - # Slot number + # LMD GHOST vote 'slot': 'uint64', - # Shard number - 'shard': 'uint64', - # Root of the signed beacon block 'beacon_block_root': 'bytes32', - # Root of the ancestor at the epoch boundary - 'epoch_boundary_root': 'bytes32', - # Data from the shard since the last attestation + + # FFG vote + 'source_epoch': 'uint64', + 'source_root': 'bytes32', + 'target_root': 'bytes32', + + # Crosslink vote + 'shard': 'uint64', + 'previous_crosslink': Crosslink, 'crosslink_data_root': 'bytes32', - # Last crosslink - 'latest_crosslink': Crosslink, - # Last justified epoch in the beacon state - 'justified_epoch': 'uint64', - # Hash of the last justified beacon block - 'justified_block_root': 'bytes32', } ``` @@ -612,9 +609,9 @@ The types are defined topologically to aid in facilitating an executable version 'previous_epoch_attestations': [PendingAttestation], 'current_epoch_attestations': [PendingAttestation], 'previous_justified_epoch': 'uint64', - 'justified_epoch': 'uint64', + 'current_justified_epoch': 'uint64', 'previous_justified_root': 'bytes32', - 'justified_root': 'bytes32', + 'current_justified_root': 'bytes32', 'justification_bitfield': 'uint64', 'finalized_epoch': 'uint64', @@ -1225,8 +1222,8 @@ def is_surround_vote(attestation_data_1: AttestationData, """ Check if ``attestation_data_1`` surrounds ``attestation_data_2``. """ - source_epoch_1 = attestation_data_1.justified_epoch - source_epoch_2 = attestation_data_2.justified_epoch + source_epoch_1 = attestation_data_1.source_epoch + source_epoch_2 = attestation_data_2.source_epoch target_epoch_1 = slot_to_epoch(attestation_data_1.slot) target_epoch_2 = slot_to_epoch(attestation_data_2.slot) @@ -1547,9 +1544,9 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], previous_epoch_attestations=[], current_epoch_attestations=[], previous_justified_epoch=GENESIS_EPOCH, - justified_epoch=GENESIS_EPOCH, + current_justified_epoch=GENESIS_EPOCH, previous_justified_root=ZERO_HASH, - justified_root=ZERO_HASH, + current_justified_root=ZERO_HASH, justification_bitfield=0, finalized_epoch=GENESIS_EPOCH, @@ -1733,7 +1730,7 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: return [ a for a in state.current_epoch_attestations - if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state))) + if a.data.target_root == get_block_root(state, get_epoch_start_slot(get_current_epoch(state))) ] ``` @@ -1741,7 +1738,7 @@ def get_current_epoch_boundary_attestations(state: BeaconState) -> List[PendingA def get_previous_epoch_boundary_attestations(state: BeaconState) -> List[PendingAttestation]: return [ a for a in state.previous_epoch_attestations - if a.data.epoch_boundary_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) + if a.data.target_root == get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) ] ``` @@ -1759,7 +1756,7 @@ def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[Pe def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations valid_attestations = [ - a for a in all_attestations if a.data.latest_crosslink == state.latest_crosslinks[shard] + a for a in all_attestations if a.data.previous_crosslink == state.latest_crosslinks[shard] ] all_roots = [a.data.crosslink_data_root for a in valid_attestations] @@ -1802,7 +1799,7 @@ Run the following function: ```python def update_justification_and_finalization(state: BeaconState) -> None: - new_justified_epoch = state.justified_epoch + new_justified_epoch = state.current_justified_epoch # Rotate the justification bitfield up one epoch to make room for the current epoch state.justification_bitfield <<= 1 # If the previous epoch gets justified, fill the second last bit @@ -1826,18 +1823,18 @@ def update_justification_and_finalization(state: BeaconState) -> None: if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2: state.finalized_epoch = state.previous_justified_epoch # The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source - if (bitfield >> 0) % 8 == 0b111 and state.justified_epoch == current_epoch - 2: - state.finalized_epoch = state.justified_epoch + if (bitfield >> 0) % 8 == 0b111 and state.current_justified_epoch == current_epoch - 2: + state.finalized_epoch = state.current_justified_epoch # The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source - if (bitfield >> 0) % 4 == 0b11 and state.justified_epoch == current_epoch - 1: - state.finalized_epoch = state.justified_epoch + if (bitfield >> 0) % 4 == 0b11 and state.current_justified_epoch == current_epoch - 1: + state.finalized_epoch = state.current_justified_epoch # Rotate justified epochs - state.previous_justified_epoch = state.justified_epoch - state.previous_justified_root = state.justified_root - if new_justified_epoch != state.justified_epoch: - state.justified_epoch = new_justified_epoch - state.justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) + state.previous_justified_epoch = state.current_justified_epoch + state.previous_justified_root = state.current_justified_root + if new_justified_epoch != state.current_justified_epoch: + state.current_justified_epoch = new_justified_epoch + state.current_justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) ``` #### Crosslinks @@ -2381,19 +2378,19 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert state.slot <= attestation.data.slot + SLOTS_PER_EPOCH # Can't submit attestations too quickly assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot - # Verify that the justified epoch/root is correct + # Verify that the justified epoch and root is correct if slot_to_epoch(attestation.data.slot) >= get_current_epoch(state): # Case 1: current epoch attestations - assert attestation.data.justified_epoch == state.justified_epoch - assert attestation.data.justified_block_root == state.justified_root + assert attestation.data.source_epoch == state.current_justified_epoch + assert attestation.data.source_root == state.current_justified_root else: # Case 2: previous epoch attestations - assert attestation.data.justified_epoch == state.previous_justified_epoch - assert attestation.data.justified_block_root == state.previous_justified_root + assert attestation.data.source_epoch == state.previous_justified_epoch + assert attestation.data.source_root == state.previous_justified_root # Check that the crosslink data is valid acceptable_crosslink_data = { # Case 1: Latest crosslink matches the one in the state - attestation.data.latest_crosslink, + attestation.data.previous_crosslink, # Case 2: State has already been updated, state's latest crosslink matches the crosslink # the attestation is trying to create Crosslink( From addf7b77ab13cfae82ae72d5a20986fb865ffd27 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Mar 2019 10:26:34 +0000 Subject: [PATCH 11/23] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7eb9752bb..319f826af 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -614,6 +614,7 @@ The types are defined topologically to aid in facilitating an executable version 'current_justified_root': 'bytes32', 'justification_bitfield': 'uint64', 'finalized_epoch': 'uint64', + 'finalized_root': 'bytes32', # Recent state 'latest_crosslinks': [Crosslink, SHARD_COUNT], @@ -1828,6 +1829,7 @@ def update_justification_and_finalization(state: BeaconState) -> None: # The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source if (bitfield >> 0) % 4 == 0b11 and state.current_justified_epoch == current_epoch - 1: state.finalized_epoch = state.current_justified_epoch + state.finalized_root = get_block_root(state, get_epoch_start_slot(finalized_epoch)) # Rotate justified epochs state.previous_justified_epoch = state.current_justified_epoch From 2e6c5171175bbb9f4d01ab229afda3246d7b6be4 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Mar 2019 11:59:08 +0000 Subject: [PATCH 12/23] CC0 1.0 Universal for repo --- LICENSE | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + From 64ba3a31073346d278b66f42bc656ce214580043 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Mar 2019 12:24:37 +0000 Subject: [PATCH 13/23] Epoch-based proposer slashing See #675 item 25. --- 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 ceca50962..cfb87e91e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2306,7 +2306,7 @@ def process_proposer_slashing(state: BeaconState, """ proposer = state.validator_registry[proposer_slashing.proposer_index] # Verify that the slot is the same - assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot + assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot) # But the headers are different assert proposer_slashing.header_1 != proposer_slashing.header_2 # Proposer is not yet slashed From d8a3048f2c119687e92072fe797fa1d377b3948f Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 12 Mar 2019 12:36:09 +0000 Subject: [PATCH 14/23] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 900708e7a..b2696892f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -268,12 +268,12 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | Name | Value | | - | - | -| `DOMAIN_BEACON_BLOCK` | `int_to_bytes4(0)` | -| `DOMAIN_RANDAO` | `int_to_bytes4(1)` | -| `DOMAIN_ATTESTATION` | `int_to_bytes4(2)` | -| `DOMAIN_DEPOSIT` | `int_to_bytes4(3)` | -| `DOMAIN_VOLUNTARY_EXIT` | `int_to_bytes4(4)` | -| `DOMAIN_TRANSFER` | `int_to_bytes4(5)` | +| `DOMAIN_BEACON_BLOCK` | `0` | +| `DOMAIN_RANDAO` | `1` | +| `DOMAIN_ATTESTATION` | `2` | +| `DOMAIN_DEPOSIT` | `3` | +| `DOMAIN_VOLUNTARY_EXIT` | `4` | +| `DOMAIN_TRANSFER` | `5` | ## Data structures @@ -1107,7 +1107,7 @@ def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> G ```python def get_fork_version(fork: Fork, - epoch: Epoch) -> bytes4: + epoch: Epoch) -> bytes: """ Return the fork version of the given ``epoch``. """ @@ -1122,11 +1122,11 @@ def get_fork_version(fork: Fork, ```python def get_domain(fork: Fork, epoch: Epoch, - domain_type: bytes4) -> int: + domain_type: int) -> int: """ Get the domain number that represents the fork meta and signature domain. """ - return bytes_to_int(get_fork_version(fork, epoch) + domain_type)) + return bytes_to_int(get_fork_version(fork, epoch) + int_to_bytes4(domain_type)) ``` ### `get_bitfield_bit` From 0f120415b5f4d7da9dbd88ecb38bc80952f8b30e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 12 Mar 2019 16:49:04 +0100 Subject: [PATCH 15/23] Update specs/core/0_beacon-chain.md Co-Authored-By: JustinDrake --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cfb87e91e..9571c23db 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2305,7 +2305,7 @@ def process_proposer_slashing(state: BeaconState, Note that this function mutates ``state``. """ proposer = state.validator_registry[proposer_slashing.proposer_index] - # Verify that the slot is the same + # Verify that the epoch is the same assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot) # But the headers are different assert proposer_slashing.header_1 != proposer_slashing.header_2 From 25f6647ef2a5a01a9000e658b276b609fb03479d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 12 Mar 2019 11:07:20 -0600 Subject: [PATCH 16/23] minor formatting --- specs/core/0_beacon-chain.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a76da5ec3..b4554e0fd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1288,7 +1288,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: serialized_deposit_data = serialize(deposit.deposit_data) # Deposits must be processed in order assert deposit.index == state.deposit_index - + # Verify the Merkle branch merkle_branch_is_valid = verify_merkle_branch( leaf=hash(serialized_deposit_data), @@ -1298,7 +1298,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: root=state.latest_eth1_data.deposit_root, ) assert merkle_branch_is_valid - + # Increment the next deposit index we are expecting. Note that this # needs to be done here because while the deposit contract will never # create an invalid Merkle branch, it may admit an invalid deposit @@ -1312,7 +1312,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: if pubkey not in validator_pubkeys: # Verify the proof of possession - if not bls_verify( + proof_is_valid = bls_verify( pubkey=deposit_input.pubkey, message_hash=signed_root(deposit_input), signature=deposit_input.proof_of_possession, @@ -1321,7 +1321,8 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: get_current_epoch(state), DOMAIN_DEPOSIT, ) - ): + ) + if not proof_is_valid: return # Add new validator From a68b050053bcb08b5c0faa14348ecc88773dd137 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 12 Mar 2019 11:35:59 -0600 Subject: [PATCH 17/23] set fork values in genesis as bytes4 --- 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 21bf3e7a6..bb3d5269c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1517,8 +1517,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], slot=GENESIS_SLOT, genesis_time=genesis_time, fork=Fork( - previous_version=GENESIS_FORK_VERSION, - current_version=GENESIS_FORK_VERSION, + previous_version=int_to_bytes4(GENESIS_FORK_VERSION), + current_version=int_to_bytes4(GENESIS_FORK_VERSION), epoch=GENESIS_EPOCH, ), From e8a5cd074766c3dfe6d424e8a08572bdae9a7445 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 12 Mar 2019 12:32:11 -0600 Subject: [PATCH 18/23] fix up for tests --- specs/core/0_beacon-chain.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 82505a220..bd0187a9f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1545,6 +1545,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], current_justified_root=ZERO_HASH, justification_bitfield=0, finalized_epoch=GENESIS_EPOCH, + finalized_root=ZERO_HASH, # Recent state latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)], @@ -1796,6 +1797,8 @@ Run the following function: ```python def update_justification_and_finalization(state: BeaconState) -> None: new_justified_epoch = state.current_justified_epoch + new_finalized_epoch = state.finalized_epoch + # Rotate the justification bitfield up one epoch to make room for the current epoch state.justification_bitfield <<= 1 # If the previous epoch gets justified, fill the second last bit @@ -1814,24 +1817,26 @@ def update_justification_and_finalization(state: BeaconState) -> None: current_epoch = get_current_epoch(state) # The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source if (bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == current_epoch - 3: - state.finalized_epoch = state.previous_justified_epoch + new_finalized_epoch = state.previous_justified_epoch # The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2: - state.finalized_epoch = state.previous_justified_epoch + new_finalized_epoch = state.previous_justified_epoch # The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source if (bitfield >> 0) % 8 == 0b111 and state.current_justified_epoch == current_epoch - 2: - state.finalized_epoch = state.current_justified_epoch + new_finalized_epoch = state.current_justified_epoch # The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source if (bitfield >> 0) % 4 == 0b11 and state.current_justified_epoch == current_epoch - 1: - state.finalized_epoch = state.current_justified_epoch - state.finalized_root = get_block_root(state, get_epoch_start_slot(finalized_epoch)) + new_finalized_epoch = state.current_justified_epoch - # Rotate justified epochs + # Update state jusification/finality fields state.previous_justified_epoch = state.current_justified_epoch state.previous_justified_root = state.current_justified_root if new_justified_epoch != state.current_justified_epoch: state.current_justified_epoch = new_justified_epoch state.current_justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) + if new_finalized_epoch != state.finalized_epoch: + state.finalized_epoch = new_finalized_epoch + state.finalized_root = get_block_root(state, get_epoch_start_slot(new_finalized_epoch)) ``` #### Crosslinks From b40236685c1fd8bf016761dfa861e05686dc6d1f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 13 Mar 2019 09:04:12 -0600 Subject: [PATCH 19/23] phase 1 nitpicks --- specs/core/1_shard-data-chains.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index db68591e7..d8efe0c85 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -556,7 +556,7 @@ def verify_custody_subkey_reveal(pubkey: bytes48, ```python def verify_signed_challenge_message(message: Any, pubkey: bytes48) -> bool: return bls_verify( - message_hash=signed_root(message, 'signature'), + message_hash=signed_root(message), pubkey=pubkey, signature=message.signature, domain=get_domain(state, get_current_epoch(state), DOMAIN_CUSTODY_INTERACTIVE) @@ -607,8 +607,8 @@ Verify that `len(block.body.branch_challenges) <= MAX_BRANCH_CHALLENGES`. For each `challenge` in `block.body.branch_challenges`, run: ```python -def process_branch_challenge(challenge: BranchChallenge, - state: BeaconState): +def process_branch_challenge(state: BeaconState, + challenge: BranchChallenge): # Check that it's not too late to challenge assert slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY assert state.validator_registry[responder_index].exit_epoch >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY @@ -643,8 +643,8 @@ Verify that `len(block.body.branch_responses) <= MAX_BRANCH_RESPONSES`. For each `response` in `block.body.branch_responses`, if `response.responding_to_custody_challenge == False`, run: ```python -def process_branch_exploration_response(response: BranchResponse, - state: BeaconState): +def process_branch_exploration_response(state: BeaconState, + response: BranchResponse): challenge = get_branch_challenge_record_by_id(response.challenge_id) assert verify_merkle_branch( leaf=response.data, @@ -664,8 +664,8 @@ def process_branch_exploration_response(response: BranchResponse, If `response.responding_to_custody_challenge == True`, run: ```python -def process_branch_custody_response(response: BranchResponse, - state: BeaconState): +def process_branch_custody_response(state: BeaconState, + response: BranchResponse): challenge = get_custody_challenge_record_by_id(response.challenge_id) responder = state.validator_registry[challenge.responder_index] # Verify we're not too late @@ -718,8 +718,8 @@ Verify that `len(block.body.interactive_custody_challenge_initiations) <= MAX_IN For each `initiation` in `block.body.interactive_custody_challenge_initiations`, use the following function to process it: ```python -def process_initiation(initiation: InteractiveCustodyChallengeInitiation, - state: BeaconState): +def process_initiation(state: BeaconState, + initiation: InteractiveCustodyChallengeInitiation): challenger = state.validator_registry[initiation.challenger_index] responder = state.validator_registry[initiation.responder_index] # Verify the signature @@ -771,8 +771,8 @@ Verify that `len(block.body.interactive_custody_challenge_responses) <= MAX_INTE For each `response` in `block.body.interactive_custody_challenge_responses`, use the following function to process it: ```python -def process_response(response: InteractiveCustodyChallengeResponse, - state: State): +def process_response(state: BeaconState, + response: InteractiveCustodyChallengeResponse): challenge = get_custody_challenge_record_by_id(state, response.challenge_id) responder = state.validator_registry[challenge.responder_index] # Check that the right number of hashes was provided @@ -804,8 +804,8 @@ Verify that `len(block.body.interactive_custody_challenge_continuations) <= MAX_ For each `continuation` in `block.body.interactive_custody_challenge_continuations`, use the following function to process it: ```python -def process_continuation(continuation: InteractiveCustodyChallengeContinuation, - state: State): +def process_continuation(state: BeaconState, + continuation: InteractiveCustodyChallengeContinuation): challenge = get_custody_challenge_record_by_id(state, continuation.challenge_id) challenger = state.validator_registry[challenge.challenger_index] responder = state.validator_registry[challenge.responder_index] From cdd59ae2309dddbe211418e03e9ccc459b4a7ddb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 13 Mar 2019 09:11:35 -0600 Subject: [PATCH 20/23] add return types to phase 1 functions Co-Authored-By: djrtwo --- specs/core/1_shard-data-chains.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index d8efe0c85..1713c6cbf 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -608,7 +608,7 @@ For each `challenge` in `block.body.branch_challenges`, run: ```python def process_branch_challenge(state: BeaconState, - challenge: BranchChallenge): + challenge: BranchChallenge) -> None: # Check that it's not too late to challenge assert slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY assert state.validator_registry[responder_index].exit_epoch >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY @@ -644,7 +644,7 @@ For each `response` in `block.body.branch_responses`, if `response.responding_to ```python def process_branch_exploration_response(state: BeaconState, - response: BranchResponse): + response: BranchResponse) -> None: challenge = get_branch_challenge_record_by_id(response.challenge_id) assert verify_merkle_branch( leaf=response.data, @@ -665,7 +665,7 @@ If `response.responding_to_custody_challenge == True`, run: ```python def process_branch_custody_response(state: BeaconState, - response: BranchResponse): + response: BranchResponse) -> None: challenge = get_custody_challenge_record_by_id(response.challenge_id) responder = state.validator_registry[challenge.responder_index] # Verify we're not too late @@ -719,7 +719,7 @@ For each `initiation` in `block.body.interactive_custody_challenge_initiations`, ```python def process_initiation(state: BeaconState, - initiation: InteractiveCustodyChallengeInitiation): + initiation: InteractiveCustodyChallengeInitiation) -> None: challenger = state.validator_registry[initiation.challenger_index] responder = state.validator_registry[initiation.responder_index] # Verify the signature @@ -772,7 +772,7 @@ For each `response` in `block.body.interactive_custody_challenge_responses`, use ```python def process_response(state: BeaconState, - response: InteractiveCustodyChallengeResponse): + response: InteractiveCustodyChallengeResponse) -> None: challenge = get_custody_challenge_record_by_id(state, response.challenge_id) responder = state.validator_registry[challenge.responder_index] # Check that the right number of hashes was provided @@ -805,7 +805,7 @@ For each `continuation` in `block.body.interactive_custody_challenge_continuatio ```python def process_continuation(state: BeaconState, - continuation: InteractiveCustodyChallengeContinuation): + continuation: InteractiveCustodyChallengeContinuation) -> None: challenge = get_custody_challenge_record_by_id(state, continuation.challenge_id) challenger = state.validator_registry[challenge.challenger_index] responder = state.validator_registry[challenge.responder_index] From 0e837c3386bbb919610665fe70ca747fb4fb9afe Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 13 Mar 2019 12:17:21 -0600 Subject: [PATCH 21/23] update v-guide to v0.5.0 --- specs/validator/0_beacon-chain-validator.md | 78 ++++++++++----------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 0c95fb446..7293675f1 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -101,8 +101,7 @@ In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW To submit a deposit: * Pack the validator's [initialization parameters](#initialization) into `deposit_input`, a [`DepositInput`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#depositinput) SSZ object. -* Set `deposit_input.proof_of_possession = EMPTY_SIGNATURE`. -* Let `proof_of_possession` be the result of `bls_sign` of the `hash_tree_root(deposit_input)` with `domain=DOMAIN_DEPOSIT`. +* Let `proof_of_possession` be the result of `bls_sign` of the `signed_root(deposit_input)` with `domain=DOMAIN_DEPOSIT`. * Set `deposit_input.proof_of_possession = proof_of_possession`. * Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`. * Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit_input)` as the singular `bytes` input along with a deposit `amount` in Gwei. @@ -121,11 +120,12 @@ Once a validator has been processed and added to the beacon state's `validator_r In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes). -The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given epoch. Usage is as follows: +The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given shuffling epoch. Note that the `BeaconState` contains a field `current_shuffling_epoch` which dictates from which epoch the current active validators are taken. Usage is as follows: ```python +shuffling_epoch = state.current_shuffling_epoch validator = state.validator_registry[validator_index] -is_active = is_active_validator(validator, epoch) +is_active = is_active_validator(validator, shuffling_epoch) ``` Once a validator is activated, the validator is assigned [responsibilities](#beacon-chain-responsibilities) until exited. @@ -138,7 +138,7 @@ A validator has two primary responsibilities to the beacon chain -- [proposing b ### Block proposal -A validator is expected to propose a [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function). +A validator is expected to propose a [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function). There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks). @@ -152,13 +152,13 @@ _Note:_ there might be "skipped" slots between the `parent` and `block`. These s ##### Parent root -Set `block.parent_root = hash_tree_root(parent)`. +Set `block.previous_block_root = hash_tree_root(parent)`. ##### State root Set `block.state_root = hash_tree_root(state)` of the resulting `state` of the `parent -> block` state transition. -_Note_: To calculate `state_root`, the validator should first run the state transition function on an unsigned `block` containing a stub for the `state_root`. It is useful to be able to run a state transition function that does _not_ validate signatures for this purpose. +_Note_: To calculate `state_root`, the validator should first run the state transition function on an unsigned `block` containing a stub for the `state_root`. It is useful to be able to run a state transition function that does _not_ validate signatures or state root for this purpose. ##### Randao reveal @@ -166,8 +166,8 @@ 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_hash=int_to_bytes32(slot_to_epoch(block.slot)), + privkey=validator.privkey, # privkey stored locally, not in state + message_hash=hash_tree_root(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), @@ -194,23 +194,16 @@ epoch_signature = bls_sign( ##### Signature -Set `block.signature = signed_proposal_data` where `signed_proposal_data` is defined as: +Set `block.signature = block_signature` where `block_signature` is defined as: ```python -proposal_data = ProposalSignedData( - slot=slot, - shard=BEACON_CHAIN_SHARD_NUMBER, - block_root=hash_tree_root(block), # where `block.sigature == EMPTY_SIGNATURE -) -proposal_root = hash_tree_root(proposal_data) - -signed_proposal_data = bls_sign( +block_signature = bls_sign( privkey=validator.privkey, # privkey store locally, not in state - message_hash=proposal_root, + message_hash=signed_root(block), domain=get_domain( fork=fork, # `fork` is the fork object at the slot `block.slot` epoch=slot_to_epoch(block.slot), - domain_type=DOMAIN_PROPOSAL, + domain_type=DOMAIN_BEACON_BLOCK, ) ) ``` @@ -227,12 +220,14 @@ Up to `MAX_ATTESTER_SLASHINGS` [`AttesterSlashing`](https://github.com/ethereum/ ##### Attestations -Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1). To maximize profit, the validator should attempt to create aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain. +Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1). To maximize profit, the validator should attempt to gather aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain. ##### Deposits Up to `MAX_DEPOSITS` [`Deposit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) objects can be included in the `block`. These deposits are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits-1). +The `proof` for each deposit must be constructed against the deposit root contained in `state.latest_eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `latest_eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation. + ##### Voluntary exits Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1). @@ -247,9 +242,12 @@ A validator should create and broadcast the attestation halfway through the `slo First the validator should construct `attestation_data`, an [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot. +* Let `head_block` be the result of running the fork choice during the assigned slot. +* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot. + ##### Slot -Set `attestation_data.slot = slot` where `slot` is the current slot of which the validator is a member of a committee. +Set `attestation_data.slot = head_state.slot`. ##### Shard @@ -257,15 +255,15 @@ Set `attestation_data.shard = shard` where `shard` is the shard associated with ##### Beacon block root -Set `attestation_data.beacon_block_root = hash_tree_root(head)` where `head` is the validator's view of the `head` block of the beacon chain during `slot`. +Set `attestation_data.beacon_block_root = hash_tree_root(head_block)`. -##### Epoch boundary root +##### Target root -Set `attestation_data.epoch_boundary_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary in the chain defined by `head` -- i.e. the `BeaconBlock` where `block.slot == get_epoch_start_slot(slot_to_epoch(head.slot))`. +Set `attestation_data.target_root = hash_tree_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary. _Note:_ This can be looked up in the state using: -* Let `epoch_start_slot = get_epoch_start_slot(slot_to_epoch(head.slot))`. -* Set `epoch_boundary_root = hash_tree_root(head) if epoch_start_slot == head.slot else get_block_root(state, epoch_start_slot)`. +* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`. +* Set `epoch_boundary = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`. ##### Crosslink data root @@ -275,17 +273,15 @@ _Note:_ This is a stub for phase 0. ##### Latest crosslink -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. +Set `attestation_data.previous_crosslink = head_state.latest_crosslinks[shard]`. -##### Justified epoch +##### Source epoch -Set `attestation_data.justified_epoch = state.justified_epoch` where `state` is the beacon state at `head`. +Set `attestation_data.source_epoch = head_state.justified_epoch`. -##### Justified block root +##### Source root -Set `attestation_data.justified_block_root = hash_tree_root(justified_block)` where `justified_block` is the block at the slot `get_epoch_start_slot(state.justified_epoch)` in the chain defined by `head`. - -_Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(state.justified_epoch))`. +Set `attestation_data.source_root = head_state.current_justified_root`. #### Construct attestation @@ -320,11 +316,11 @@ attestation_data_and_custody_bit = AttestationDataAndCustodyBit( data=attestation.data, custody_bit=0b0, ) -attestation_message_to_sign = hash_tree_root(attestation_data_and_custody_bit) +attestation_message = hash_tree_root(attestation_data_and_custody_bit) signed_attestation_data = bls_sign( - privkey=validator.privkey, # privkey store locally, not in state - message_hash=attestation_message_to_sign, + privkey=validator.privkey, # privkey stored locally, not in state + message_hash=attestation_message, domain=get_domain( fork=fork, # `fork` is the fork object at the slot, `attestation_data.slot` epoch=slot_to_epoch(attestation_data.slot), @@ -402,12 +398,12 @@ _Note_: Signed data must be within a sequential `Fork` context to conflict. Mess ### Proposer slashing -To avoid "proposer slashings", a validator must not sign two conflicting [`ProposalSignedData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposalsigneddata) where conflicting is defined as having the same `slot` and `shard` but a different `block_root`. In phase 0, proposals are only made for the beacon chain (`shard == BEACON_CHAIN_SHARD_NUMBER`). +To avoid "proposer slashings", a validator must not sign two conflicting [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposalsigneddata) where conflicting is defined as two distinct blocks within the same epoch. -_In phase 0, as long as the validator does not sign two different beacon chain proposals for the same slot, the validator is safe against proposer slashings._ +_In phase 0, as long as the validator does not sign two different beacon blocks for the same epoch, the validator is safe against proposer slashings._ Specifically, when signing an `BeaconBlock`, a validator should perform the following steps in the following order: -1. Save a record to hard disk that an beacon block has been signed for the `slot=slot` and `shard=BEACON_CHAIN_SHARD_NUMBER`. +1. Save a record to hard disk that an beacon block has been signed for the `epoch=slot_to_epoch(block.slot)`. 2. Generate and broadcast the block. If the software crashes at some point within this routine, then when the validator comes back online the hard disk has the record of the _potentially_ signed/broadcast block and can effectively avoid slashing. @@ -417,7 +413,7 @@ If the software crashes at some point within this routine, then when the validat To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_surround_vote). Specifically, when signing an `Attestation`, a validator should perform the following steps in the following order: -1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.justified_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`. +1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.source_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`. 2. Generate and broadcast attestation. If the software crashes at some point within this routine, then when the validator comes back online the hard disk has the record of the _potentially_ signed/broadcast attestation and can effectively avoid slashing. From 9774a3d5811396c609ffa1b74d0f6e4f3a642d02 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Wed, 13 Mar 2019 17:01:47 -0700 Subject: [PATCH 22/23] Helper function returns correct type of `Gwei` instead of indices --- 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 bd0187a9f..daa1bc108 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1719,7 +1719,7 @@ def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestat ``` ```python -def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: +def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> Gwei: return get_total_balance(state, get_attesting_indices(state, attestations)) ``` From c30018a71657b62f6c088f19eb85a721f0641305 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 13 Mar 2019 18:45:52 -0700 Subject: [PATCH 23/23] Update 0_beacon-chain-validator.md --- specs/validator/0_beacon-chain-validator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 7293675f1..be3008227 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -40,11 +40,11 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Slot](#slot-1) - [Shard](#shard) - [Beacon block root](#beacon-block-root) - - [Epoch boundary root](#epoch-boundary-root) + - [Target root](#target-root) - [Crosslink data root](#crosslink-data-root) - [Latest crosslink](#latest-crosslink) - - [Justified epoch](#justified-epoch) - - [Justified block root](#justified-block-root) + - [Source epoch](#source-epoch) + - [Source root](#source-root) - [Construct attestation](#construct-attestation) - [Data](#data) - [Aggregation bitfield](#aggregation-bitfield)