From ab140c2f1c92fa5821a332e89d56393f4499ef86 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 15 Feb 2019 00:23:03 +0000 Subject: [PATCH 01/47] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 151 ++++++++++++++--------------------- 1 file changed, 62 insertions(+), 89 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a48d881fa5..c38b237bc4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -43,7 +43,8 @@ - [Beacon chain blocks](#beacon-chain-blocks) - [`BeaconBlock`](#beaconblock) - [`BeaconBlockBody`](#beaconblockbody) - - [`ProposalSignedData`](#proposalsigneddata) + - [`Proposal`](#proposal) + - [`RandaoReveal`](#randaoreveal) - [Beacon chain state](#beacon-chain-state) - [`BeaconState`](#beaconstate) - [`Validator`](#validator) @@ -94,7 +95,6 @@ - [`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) @@ -117,7 +117,7 @@ - [Block roots](#block-roots) - [Per-block processing](#per-block-processing) - [Slot](#slot-1) - - [Proposer signature](#proposer-signature) + - [Block signature](#block-signature) - [RANDAO](#randao) - [Eth1 data](#eth1-data) - [Transactions](#transactions) @@ -299,14 +299,10 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git { # Proposer index 'proposer_index': 'uint64', - # First proposal data - 'proposal_data_1': ProposalSignedData, - # First proposal signature - 'proposal_signature_1': 'bytes96', - # Second proposal data - 'proposal_data_2': ProposalSignedData, - # Second proposal signature - 'proposal_signature_2': 'bytes96', + # First proposal + 'proposal_1': Proposal, + # Second proposal + 'proposal_2': Proposal, } ``` @@ -363,9 +359,9 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'slot': 'uint64', # Shard number 'shard': 'uint64', - # Hash of root of the signed beacon block + # Root of the signed beacon block 'beacon_block_root': 'bytes32', - # Hash of root of the ancestor at the epoch boundary + # Root of the ancestor at the epoch boundary 'epoch_boundary_root': 'bytes32', # Shard block's hash of root 'shard_block_root': 'bytes32', @@ -474,16 +470,17 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { - ## Header ## - 'slot': 'uint64', + # Header + 'slot': Slot, 'parent_root': 'bytes32', 'state_root': 'bytes32', 'randao_reveal': 'bytes96', 'eth1_data': Eth1Data, - 'signature': 'bytes96', - ## Body ## + # Body 'body': BeaconBlockBody, + # Signature + 'signature': 'bytes96', } ``` @@ -500,7 +497,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` -#### `ProposalSignedData` +#### `Proposal` ```python { @@ -508,8 +505,21 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'slot': 'uint64', # Shard number (`BEACON_CHAIN_SHARD_NUMBER` for beacon chain) 'shard': 'uint64', - # Block's hash of root + # Block root 'block_root': 'bytes32', + # Signature + 'signature': 'bytes96', +} +``` + +#### `RandaoReveal` + +```python +{ + # Epoch number + 'epoch': 'uint64', + # RANDAO reveal + 'randao_reveal': 'bytes96', } ``` @@ -670,6 +680,10 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere `def hash_tree_root(object: SSZSerializable) -> Bytes32` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#tree-hash). +### `signed_root` + +`def signed_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#signed-roots) to compute signed messages. + ### `slot_to_epoch` ```python @@ -1240,60 +1254,33 @@ def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: `bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, defined in the [BLS Signature spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#bls_aggregate_pubkeys). -### `validate_proof_of_possession` +### `process_deposit` + +Used to add a [validator](#dfn-validator) or top up an existing [validator](#dfn-validator)'s balance by some `deposit` amount: ```python -def validate_proof_of_possession(state: BeaconState, - pubkey: BLSPubkey, - proof_of_possession: BLSSignature, - withdrawal_credentials: Bytes32) -> bool: +def process_deposit(state: BeaconState, deposit: Deposit) -> None: """ - Verify the given ``proof_of_possession``. + Process a deposit from Ethereum 1.0. + Note that this function mutates ``state``. """ - proof_of_possession_data = DepositInput( - pubkey=pubkey, - withdrawal_credentials=withdrawal_credentials, - proof_of_possession=EMPTY_SIGNATURE, - ) + deposit_input = deposit.deposit_data.deposit_input - return bls_verify( - pubkey=pubkey, - message_hash=hash_tree_root(proof_of_possession_data), - signature=proof_of_possession, + assert bls_verify( + pubkey=deposit_input.pubkey, + message_hash=signed_root(deposit_input, "proof_of_possession"), + signature=deposit_input.proof_of_possession, domain=get_domain( state.fork, get_current_epoch(state), DOMAIN_DEPOSIT, ) ) -``` - -### `process_deposit` - -Used to add a [validator](#dfn-validator) or top up an existing [validator](#dfn-validator)'s balance by some `deposit` amount: - -```python -def process_deposit(state: BeaconState, - pubkey: BLSPubkey, - amount: Gwei, - proof_of_possession: BLSSignature, - withdrawal_credentials: Bytes32) -> None: - """ - Process a deposit from Ethereum 1.0. - Note that this function mutates ``state``. - """ - # Validate the given `proof_of_possession` - proof_is_valid = validate_proof_of_possession( - state, - pubkey, - proof_of_possession, - withdrawal_credentials, - ) - - if not proof_is_valid: - return validator_pubkeys = [v.pubkey for v in state.validator_registry] + pubkey = deposit_input.pubkey + amount = deposit.deposit_data.amount + withdrawal_credentials = deposit_input.withdrawal_credentials if pubkey not in validator_pubkeys: # Add new validator @@ -1520,13 +1507,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], # Process genesis deposits for deposit in genesis_validator_deposits: - process_deposit( - state=state, - pubkey=deposit.deposit_data.deposit_input.pubkey, - amount=deposit.deposit_data.amount, - proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, - withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, - ) + process_deposit(state, deposit) # Process genesis activations for validator_index, _ in enumerate(state.validator_registry): @@ -1652,16 +1633,16 @@ Below are the processing steps that happen at every `block`. * Verify that `block.slot == state.slot`. -#### Proposer signature +#### Block signature -* 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_hash=proposal_root, signature=block.signature, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_PROPOSAL))`. +* Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. +* Let `proposal = Proposal(block.slot, BEACON_CHAIN_SHARD_NUMBER, signed_root(block, "signature"), block.signature)`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposal, "signature"), signature=proposal.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_hash=int_to_bytes32(get_current_epoch(state)), signature=block.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. +* Let `randao_reveal = RandaoReveal(epoch=get_current_epoch(state), randao_reveal=block.randao_reveal)`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(randao_reveal, "randao_reveal"), signature=randao_reveal.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 @@ -1678,12 +1659,12 @@ Verify that `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`. For each `proposer_slashing` in `block.body.proposer_slashings`: * Let `proposer = state.validator_registry[proposer_slashing.proposer_index]`. -* Verify that `proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot`. -* 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_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot`. +* Verify that `proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard`. +* Verify that `proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root`. * Verify that `proposer.slashed_epoch > get_current_epoch(state)`. -* 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))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_1, "signature"), signature=proposer_slashing.proposal_1.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_1.slot), DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_2, "signature"), signature=proposer_slashing.proposal_2.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_2.slot), DOMAIN_PROPOSAL))`. * Run `slash_validator(state, proposer_slashing.proposer_index)`. ##### Attester slashings @@ -1777,13 +1758,7 @@ def verify_merkle_branch(leaf: Bytes32, branch: List[Bytes32], depth: int, index * Run the following: ```python -process_deposit( - state=state, - pubkey=deposit.deposit_data.deposit_input.pubkey, - amount=deposit.deposit_data.amount, - proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, - withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, -) +process_deposit(state, deposit) ``` * Set `state.deposit_index += 1`. @@ -1797,8 +1772,7 @@ For each `exit` in `block.body.voluntary_exits`: * Let `validator = state.validator_registry[exit.validator_index]`. * 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_hash=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. +* Verify that `bls_verify(pubkey=validator.pubkey, message_hash=signed_root(exit, "signature"), signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. ##### Transfers @@ -1815,8 +1789,7 @@ For each `transfer` in `block.body.transfers`: * Verify that `state.slot == transfer.slot`. * Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch`. * Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. -* Let `transfer_message = hash_tree_root(Transfer(from=transfer.from, to=transfer.to, amount=transfer.amount, fee=transfer.fee, slot=transfer.slot, signature=EMPTY_SIGNATURE))`. -* Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=transfer_message, signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. +* Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=signed_root(transfer, "signature"), signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. * Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. * Set `state.validator_balances[transfer.to] += transfer.amount`. * Set `state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee`. From 888b06ce4a042c10c3c869f46759e0d685be74c8 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 15 Feb 2019 00:24:46 +0000 Subject: [PATCH 02/47] 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 c38b237bc4..bc9e9bcfcd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -471,7 +471,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { # Header - 'slot': Slot, + 'slot': 'uint64', 'parent_root': 'bytes32', 'state_root': 'bytes32', 'randao_reveal': 'bytes96', From 267f8405702d1b2f808264a4c33456d7a67ffa53 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 15 Feb 2019 11:57:23 +1100 Subject: [PATCH 03/47] Add explicit assert to get_permutated_index There is an implicit assert in `int_to_bytes4`, however I think it's rather hidden. --- 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 a48d881fa5..ee8eceee9a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -745,6 +745,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: See the 'generalized domain' algorithm on page 3. """ assert index < list_size + assert list_size < 2**24 for round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size From 2f327f36d8f6975ca5b3a842e39f252f93ba6e97 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 15 Feb 2019 12:50:56 +1100 Subject: [PATCH 04/47] Improve asset in get_permutated_index --- 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 ee8eceee9a..0932e8ef00 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -745,7 +745,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: See the 'generalized domain' algorithm on page 3. """ assert index < list_size - assert list_size < 2**24 + assert list_size <= 2**24 for round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size From 2207fd17f5003f80fbebf6930dfd594c907dcbd0 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 15 Feb 2019 13:37:36 +0000 Subject: [PATCH 05/47] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bc9e9bcfcd..782e7a72b9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -44,7 +44,6 @@ - [`BeaconBlock`](#beaconblock) - [`BeaconBlockBody`](#beaconblockbody) - [`Proposal`](#proposal) - - [`RandaoReveal`](#randaoreveal) - [Beacon chain state](#beacon-chain-state) - [`BeaconState`](#beaconstate) - [`Validator`](#validator) @@ -512,17 +511,6 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git } ``` -#### `RandaoReveal` - -```python -{ - # Epoch number - 'epoch': 'uint64', - # RANDAO reveal - 'randao_reveal': 'bytes96', -} -``` - ### Beacon chain state #### `BeaconState` @@ -1641,8 +1629,7 @@ Below are the processing steps that happen at every `block`. #### RANDAO -* Let `randao_reveal = RandaoReveal(epoch=get_current_epoch(state), randao_reveal=block.randao_reveal)`. -* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(randao_reveal, "randao_reveal"), signature=randao_reveal.randao_reveal, domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=hash_tree_root(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 From 2e6bf87bdf5b8dc34e6f19e947a67d40e2dcc71e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 16 Feb 2019 17:27:47 +1100 Subject: [PATCH 06/47] Fix max list size for get_permutated_index --- 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 0932e8ef00..1ae282fa1e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -745,7 +745,7 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: See the 'generalized domain' algorithm on page 3. """ assert index < list_size - assert list_size <= 2**24 + assert list_size <= 2**40 for round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % list_size From b92cd9be03b16db86caf5c9498df8a5c3bf5d848 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 16 Feb 2019 17:55:27 +1100 Subject: [PATCH 07/47] Add fix to `get_shuffling` Ensures it does not try to shuffle out of range of the `active_validator_indices` list. --- 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 ce13c6105f..80bb126352 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -807,7 +807,7 @@ def get_shuffling(seed: Bytes32, # Shuffle shuffled_active_validator_indices = [ active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] - for i in active_validator_indices + for i in range(len(active_validator_indices)) ] # Split the shuffled list into committees_per_epoch pieces From 464947dbd4b7a0f210e3a88862a91a6329b39667 Mon Sep 17 00:00:00 2001 From: d-yokoi Date: Sat, 16 Feb 2019 18:11:29 +0900 Subject: [PATCH 08/47] docs: fix typo in 0_beacon-chain.md (#640) --- 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 ce13c6105f..af6f789760 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -181,7 +181,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. ### Misc | Name | Value | -| - | - | :-: | +| - | - | | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | From f2e547e62909d98181eeebf22e6323ff5ca3e103 Mon Sep 17 00:00:00 2001 From: d-yokoi Date: Sat, 16 Feb 2019 23:12:00 +0900 Subject: [PATCH 09/47] docs: fix typo in 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 af6f789760..544d792448 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1666,7 +1666,7 @@ Below are the processing steps that happen at every `block`. #### Eth1 data -* 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`. +* If there exists an `eth1_data_vote` in `state.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)`. #### Transactions From 1d95c1482c26b454ac73fc01e985c14702ba69b4 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 16 Feb 2019 21:11:48 +0000 Subject: [PATCH 10/47] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 80bb126352..832b8e523d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -795,23 +795,16 @@ def get_shuffling(seed: Bytes32, validators: List[Validator], epoch: Epoch) -> List[List[ValidatorIndex]] """ - Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. - Return a list of ``committees_per_epoch`` committees where each - committee is itself a list of validator indices. + Shuffle (seeded by ``seed`` and ``epoch``) active validators and split into crosslink committees. + Return a list of committees (each a list of validator indices). """ - + # Shuffle active validator indices active_validator_indices = get_active_validator_indices(validators, epoch) + length = len(active_validator_indices) + shuffled_indices = [active_validator_indices[get_permuted_index(i, length, seed)] for i in range(length)] - committees_per_epoch = get_epoch_committee_count(len(active_validator_indices)) - - # Shuffle - shuffled_active_validator_indices = [ - active_validator_indices[get_permuted_index(i, len(active_validator_indices), seed)] - for i in range(len(active_validator_indices)) - ] - - # Split the shuffled list into committees_per_epoch pieces - return split(shuffled_active_validator_indices, committees_per_epoch) + # Split the shuffled active validator indices + return split(shuffled_indices, get_epoch_committee_count(length)) ``` **Invariant**: if `get_shuffling(seed, validators, epoch)` returns some value `x` for some `epoch <= get_current_epoch(state) + ACTIVATION_EXIT_DELAY`, it should return the same value `x` for the same `seed` and `epoch` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. From 4baa13050e35e2d3314e7f16ffc48be535bf7530 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 16 Feb 2019 21:13:46 +0000 Subject: [PATCH 11/47] 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 832b8e523d..3a26c85241 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -795,7 +795,7 @@ def get_shuffling(seed: Bytes32, validators: List[Validator], epoch: Epoch) -> List[List[ValidatorIndex]] """ - Shuffle (seeded by ``seed`` and ``epoch``) active validators and split into crosslink committees. + Shuffle active validators and split into crosslink committees. Return a list of committees (each a list of validator indices). """ # Shuffle active validator indices From 59ade930d6384434b2e606e98422c54b9fc9576e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 16 Feb 2019 15:44:27 -0600 Subject: [PATCH 12/47] Signature hashing proposal (#625) If this is accepted, then we can replace all uses of signing in the protocol, which are currently done in a relatively inconsistent way (see proposer signatures, attester signatures, shard proposer signatures, exit message signatures.....) could be unified. --- specs/simple-serialize.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index c71654b677..6021619a60 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -24,11 +24,12 @@ deserializing objects and data types. - [bytesN](#bytesn-1) - [List/Vectors](#listvectors-1) - [Container](#container-1) - + [Tree Hash](#tree-hash) + + [Tree Hash](#tree-hash) - [`uint8`..`uint256`, `bool`, `bytes1`..`bytes32`](#uint8uint256-bool-bytes1bytes32) - [`uint264`..`uintN`, `bytes33`..`bytesN`](#uint264uintn-bytes33bytesn) - [List/Vectors](#listvectors-2) - [Container](#container-2) + + [Signed Roots](#signed-roots) * [Implementations](#implementations) ## About @@ -396,6 +397,14 @@ Recursively tree hash the values in the container in the same order as the field return merkle_hash([hash_tree_root_internal(getattr(x, field)) for field in value.fields]) ``` +### Signed roots + +Let `field_name` be a field name in an SSZ container `container`. We define `truncate(container, field_name)` to be the `container` with the fields from `field_name` onwards truncated away. That is, `truncate(container, field_name) = [getattr(container, field)) for field in value.fields[:i]]` where `i = value.fields.index(field_name)`. + +When `field_name` maps to a signature (e.g. a BLS12-381 signature of type `Bytes96`) the convention is that the corresponding signed message be `signed_root(container, field_name) = hash_tree_root(truncate(container, field_name))`. For example if `container = {"foo": sub_object_1, "bar": sub_object_2, "signature": bytes96, "baz": sub_object_3}` then `signed_root(container, "signature") = merkle_hash([hash_tree_root(sub_object_1), hash_tree_root(sub_object_2)])`. + +Note that this convention means that fields after the signature are _not_ signed over. If there are multiple signatures in `container` then those are expected to be signing over the fields in the order specified. If multiple signatures of the same value are expected the convention is that the signature field be an array of signatures. + ## Implementations | Language | Implementation | Description | From eea413bcc5bfc083cd33eddd2eaa3ad2a545398b Mon Sep 17 00:00:00 2001 From: Cayman Date: Sun, 17 Feb 2019 15:53:59 -0700 Subject: [PATCH 13/47] Fix typos (#648) --- 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 544d792448..7ce4939e88 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1797,7 +1797,7 @@ For each `exit` in `block.body.voluntary_exits`: * Let `validator = state.validator_registry[exit.validator_index]`. * 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))`. +* Let `exit_message = hash_tree_root(VoluntaryExit(epoch=exit.epoch, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. * 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)`. @@ -2098,7 +2098,7 @@ def process_exit_queue(state: BeaconState) -> None: #### Final updates -* Set `state.latest_active_index_roots[(next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, next_epoch + ACTIVATION_EXIT_DELAY))`. +* Set `state.latest_active_index_roots[(next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY))`. * Set `state.latest_slashed_balances[(next_epoch) % LATEST_SLASHED_EXIT_LENGTH] = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH]`. * Set `state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch)`. * Remove any `attestation` in `state.latest_attestations` such that `slot_to_epoch(attestation.data.slot) < current_epoch`. From 5fbbb4f18117e0313bfb13303f29b219111dd900 Mon Sep 17 00:00:00 2001 From: Cayman Date: Sun, 17 Feb 2019 21:11:01 -0700 Subject: [PATCH 14/47] Clarify merkle_root documentation (#646) This function is used to get the merkle root of some block roots which are themselves hashes, so the leaves do not need to be rehashed. Unfortunately, it's easy to assume that a merkle_root function would hash its leaves, so it should be clarified. --- 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 7ce4939e88..116745dccb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -995,6 +995,7 @@ def get_beacon_proposer_index(state: BeaconState, def merkle_root(values: List[Bytes32]) -> Bytes32: """ Merkleize ``values`` (where ``len(values)`` is a power of two) and return the Merkle root. + Note that the leaves are not hashed. """ o = [0] * len(values) + values for i in range(len(values) - 1, 0, -1): From f0b562054fa526322956500f3a6e19ed9007ce5d Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 19 Feb 2019 00:19:31 -0800 Subject: [PATCH 15/47] Update 0_beacon-chain-validator.md (#650) --- specs/validator/0_beacon-chain-validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 2211e2e419..6fdeafb49c 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -186,7 +186,7 @@ epoch_signature = bls_sign( * Let `block_hash` be the block hash of the `ETH1_FOLLOW_DISTANCE`'th ancestor of the head of the canonical eth1.0 chain. * Let `deposit_root` be the deposit root of the eth1.0 deposit contract in the post-state of the block referenced by `block_hash` * If `D` is nonempty: - * Let `best_vote` be the member of `D` that has the highest `vote.eth1_data.vote_count`, breaking ties by favoring block hashes with higher associated block height. + * Let `best_vote` be the member of `D` that has the highest `vote.vote_count`, breaking ties by favoring block hashes with higher associated block height. * Let `block_hash = best_vote.eth1_data.block_hash`. * Let `deposit_root = best_vote.eth1_data.deposit_root`. * Set `block.eth1_data = Eth1Data(deposit_root=deposit_root, block_hash=block_hash)`. From 2540f55c1388abd47748beffd86a046f2c12499a Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 19 Feb 2019 05:26:35 -0600 Subject: [PATCH 16/47] Updated phase 1: branch and early subkey reveal challenges (#587) * Updated phase 1: branch challenges * Removed unnecessary line * Added early subkey reveal slashing * Revealing during the active period is still revealing early * Added.... * Machinery for publishing old subkeys * Inability to withdraw until you published all subkeys * After a validator exits the queue there's still a minimum 1-day delay before they can withdraw (in the future this delay will be used as an opportunity to start a PoC challenge game) * Update 1_shard-data-chains.md * formatting * minor edits * Added masking scheme for reveals Secure under the aggregate extraction infeasibility assumption described on pages 11-12 of https://crypto.stanford.edu/~dabo/pubs/papers/aggreg.pdf * Added rewards going to challengers * Add ToC and reorg the constant tables * Remove tags * fix constant formatting * normalize domain constants in phase 1 * Update 1_shard-data-chains.md * Update 1_shard-data-chains.md * Update 1_shard-data-chains.md * Added transition logic * Fix ToC * Fix ToC * Adjusted for #615 * Added more helpers * epoch -> slot * fix some type hints * clean up `get_attestation_merkle_depth` --- specs/core/1_shard-data-chains.md | 389 +++++++++++++++++++++++++++++- 1 file changed, 376 insertions(+), 13 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index cbb84aa0e8..363437ae52 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -1,9 +1,57 @@ # Ethereum 2.0 Phase 1 -- Shard Data Chains -###### tags: `spec`, `eth2.0`, `casper`, `sharding` - **NOTICE**: This document is a work-in-progress for researchers and implementers. It reflects recent spec changes and takes precedence over the [Python proof-of-concept implementation](https://github.com/ethereum/beacon_chain). +## Table of contents + + + +- [Ethereum 2.0 Phase 1 -- Shard Data Chains](#ethereum-20-phase-1----shard-data-chains) + - [Table of contents](#table-of-contents) + - [Introduction](#introduction) + - [Terminology](#terminology) + - [Constants](#constants) + - [Misc](#misc) + - [Time parameters](#time-parameters) + - [Max operations per block](#max-operations-per-block) + - [Signature domains](#signature-domains) + - [Helper functions](#helper-functions) + - [get_split_offset](#get_split_offset) + - [get_shuffled_committee](#get_shuffled_committee) + - [get_persistent_committee](#get_persistent_committee) + - [get_shard_proposer_index](#get_shard_proposer_index) + - [Data Structures](#data-structures) + - [Shard chain blocks](#shard-chain-blocks) + - [Shard block processing](#shard-block-processing) + - [Verifying shard block data](#verifying-shard-block-data) + - [Verifying a crosslink](#verifying-a-crosslink) + - [Shard block fork choice rule](#shard-block-fork-choice-rule) +- [Updates to the beacon chain](#updates-to-the-beacon-chain) + - [Data structures](#data-structures) + - [`Validator`](#validator) + - [`BeaconBlockBody`](#beaconblockbody) + - [`BranchChallenge`](#branchchallenge) + - [`BranchResponse`](#branchresponse) + - [`BranchChallengeRecord`](#branchchallengerecord) + - [`SubkeyReveal`](#subkeyreveal) + - [Helpers](#helpers) + - [`get_attestation_merkle_depth`](#get_attestation_merkle_depth) + - [`epoch_to_custody_period`](#epoch_to_custody_period) + - [`slot_to_custody_period`](#slot_to_custody_period) + - [`get_current_custody_period`](#get_current_custody_period) + - [`verify_custody_subkey_reveal`](#verify_custody_subkey_reveal) + - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) + - [`penalize_validator`](#penalize_validator) + - [Per-slot processing](#per-slot-processing) + - [Operations](#operations) + - [Branch challenges](#branch-challenges) + - [Branch responses](#branch-responses) + - [Subkey reveals](#subkey-reveals) + - [Per-epoch processing](#per-epoch-processing) + - [One-time phase 1 initiation transition](#one-time-phase-1-initiation-transition) + + + ### Introduction This document represents the specification for Phase 1 of Ethereum 2.0 -- Shard Data Chains. Phase 1 depends on the implementation of [Phase 0 -- The Beacon Chain](0_beacon-chain.md). @@ -16,19 +64,39 @@ Ethereum 2.0 consists of a central beacon chain along with `SHARD_COUNT` shard c Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md#constants) in addition to the following: -| Constant | Value | Unit | Approximation | -|-------------------------------|------------------|--------|---------------| -| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | | -| `SHARD_BLOCK_SIZE` | 2**14 (= 16,384) | bytes | | -| `CROSSLINK_LOOKBACK` | 2**5 (= 32) | slots | | +#### Misc + +| Name | Value | Unit | +|-------------------------------|------------------|--------| +| `SHARD_CHUNK_SIZE` | 2**5 (= 32) | bytes | +| `SHARD_BLOCK_SIZE` | 2**14 (= 16,384) | bytes | +| `MINOR_REWARD_QUOTIENT` | 2**8 (= 256) | | + +#### Time parameters + +| Name | Value | Unit | Duration | +| - | - | :-: | :-: | +| `CROSSLINK_LOOKBACK` | 2**5 (= 32) | slots | 3.2 minutes | +| `MAX_BRANCH_CHALLENGE_DELAY` | 2**11 (= 2,048) | epochs | 9 days | +| `CUSTODY_PERIOD_LENGTH` | 2**11 (= 2,048) | epochs | 9 days | | `PERSISTENT_COMMITTEE_PERIOD` | 2**11 (= 2,048) | epochs | 9 days | +| `CHALLENGE_RESPONSE_DEADLINE` | 2**14 (= 16,384) | epochs | 73 days | + +#### Max operations per block -### Flags, domains, etc. +| Name | Value | +|-------------------------------|---------------| +| `MAX_BRANCH_CHALLENGES` | 2**2 (= 4) | +| `MAX_BRANCH_RESPONSES` | 2**4 (= 16) | +| `MAX_EARLY_SUBKEY_REVEALS` | 2**4 (= 16) | -| Constant | Value | +#### Signature domains + +| Name | Value | |------------------------|-----------------| -| `SHARD_PROPOSER_DOMAIN`| 129 | -| `SHARD_ATTESTER_DOMAIN`| 130 | +| `DOMAIN_SHARD_PROPOSER`| 129 | +| `DOMAIN_SHARD_ATTESTER`| 130 | +| `DOMAIN_CUSTODY_SUBKEY`| 131 | ## Helper functions @@ -159,9 +227,9 @@ To validate a block header on shard `shard_block.shard_id`, compute as follows: * Let `proposer_index = get_shard_proposer_index(state, shard_block.shard_id, shard_block.slot)`. * Verify that `proposer_index` is not `None`. * Let `msg` be the `shard_block` but with `shard_block.signature` set to `[0, 0]`. -* Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_PROPOSER_DOMAIN))` passes. +* Verify that `bls_verify(pubkey=validators[proposer_index].pubkey, message_hash=hash(msg), signature=shard_block.signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), DOMAIN_SHARD_PROPOSER))` passes. * Let `group_public_key = bls_aggregate_pubkeys([state.validators[index].pubkey for i, index in enumerate(persistent_committee) if get_bitfield_bit(shard_block.participation_bitfield, i) is True])`. -* Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), SHARD_ATTESTER_DOMAIN))` passes. +* Verify that `bls_verify(pubkey=group_public_key, message_hash=shard_block.parent_root, sig=shard_block.aggregate_signature, domain=get_domain(state, slot_to_epoch(shard_block.slot), DOMAIN_SHARD_ATTESTER))` passes. ### Verifying shard block data @@ -222,3 +290,298 @@ The `shard_chain_commitment` is only valid if it equals `compute_commitment(head ### Shard block fork choice rule The fork choice rule for any shard is LMD GHOST using the shard chain attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). + +# Updates to the beacon chain + +## Data structures + +### `Validator` + +Add member values to the end of the `Validator` object: + +```python + 'open_branch_challenges': [BranchChallengeRecord], + 'next_subkey_to_reveal': 'uint64', + 'reveal_max_periods_late': 'uint64', +``` + +And the initializers: + +```python + 'open_branch_challenges': [], + 'next_subkey_to_reveal': get_current_custody_period(state), + 'reveal_max_periods_late': 0, +``` + +### `BeaconBlockBody` + +Add member values to the `BeaconBlockBody` structure: + +```python + 'branch_challenges': [BranchChallenge], + 'branch_responses': [BranchResponse], + 'subkey_reveals': [SubkeyReveal], +``` + +And initialize to the following: + +```python + 'branch_challenges': [], + 'branch_responses': [], + 'subkey_reveals': [], +``` + +### `BranchChallenge` + +Define a `BranchChallenge` as follows: + +```python +{ + 'responder_index': 'uint64', + 'data_index': 'uint64', + 'attestation': SlashableAttestation, +} +``` + +### `BranchResponse` + +Define a `BranchResponse` as follows: + +```python +{ + 'responder_index': 'uint64', + 'data': 'bytes32', + 'branch': ['bytes32'], + 'data_index': 'uint64', + 'root': 'bytes32', +} +``` + +### `BranchChallengeRecord` + +Define a `BranchChallengeRecord` as follows: + +```python +{ + 'challenger_index': 'uint64', + 'root': 'bytes32', + 'depth': 'uint64', + 'inclusion_epoch': 'uint64', + 'data_index': 'uint64', +} +``` + +### `SubkeyReveal` + +Define a `SubkeyReveal` as follows: + +```python +{ + 'validator_index': 'uint64', + 'period': 'uint64', + 'subkey': 'bytes96', + 'mask': 'bytes32', + 'revealer_index': 'uint64' +} +``` + +## Helpers + +### `get_attestation_merkle_depth` + +```python +def get_attestation_merkle_depth(attestation: Attestation) -> int: + start_epoch = attestation.data.latest_crosslink.epoch + end_epoch = slot_to_epoch(attestation.data.slot) + chunks_per_slot = SHARD_BLOCK_SIZE // 32 + chunks = (end_epoch - start_epoch) * EPOCH_LENGTH * chunks_per_slot + return log2(next_power_of_two(chunks)) +``` + +### `epoch_to_custody_period` + +```python +def epoch_to_custody_period(epoch: Epoch) -> int: + return epoch // CUSTODY_PERIOD_LENGTH +``` + +### `slot_to_custody_period` + +```python +def slot_to_custody_period(slot: Slot) -> int: + return epoch_to_custody_period(slot_to_epoch(slot)) +``` + +### `get_current_custody_period` + +```python +def get_current_custody_period(state: BeaconState) -> int: + return epoch_to_custody_period(get_current_epoch(state)) +``` + +### `verify_custody_subkey_reveal` + +```python +def verify_custody_subkey_reveal(pubkey: bytes48, + subkey: bytes96, + mask: bytes32, + mask_pubkey: bytes48, + period: int) -> bool: + # Legitimate reveal: checking that the provided value actually is the subkey + if mask == ZERO_HASH: + pubkeys=[pubkey] + message_hashes=[hash(int_to_bytes8(period))] + + # Punitive early reveal: checking that the provided value is a valid masked subkey + # (masking done to prevent "stealing the reward" from a whistleblower by block proposers) + # Secure under the aggregate extraction infeasibility assumption described on page 11-12 + # of https://crypto.stanford.edu/~dabo/pubs/papers/aggreg.pdf + else: + pubkeys=[pubkey, mask_pubkey] + message_hashes=[hash(int_to_bytes8(period)), mask] + + return bls_multi_verify( + pubkeys=pubkeys, + message_hashes=message_hashes, + signature=subkey, + domain=get_domain( + fork=state.fork, + epoch=period * CUSTODY_PERIOD_LENGTH, + domain_type=DOMAIN_CUSTODY_SUBKEY, + ) + ) +``` + +### `penalize_validator` + +Change the definition of `penalize_validator` as follows: + +```python +def penalize_validator(state: BeaconState, index: ValidatorIndex, whistleblower_index=None:ValidatorIndex) -> None: + """ + Penalize the validator of the given ``index``. + Note that this function mutates ``state``. + """ + exit_validator(state, index) + validator = state.validator_registry[index] + state.latest_penalized_balances[get_current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) + + block_proposer_index = get_beacon_proposer_index(state, state.slot) + whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT + if whistleblower_index is None: + state.validator_balances[block_proposer_index] += whistleblower_reward + else: + state.validator_balances[whistleblower_index] += ( + whistleblower_reward * INCLUDER_REWARD_QUOTIENT / (INCLUDER_REWARD_QUOTIENT + 1) + ) + state.validator_balances[block_proposer_index] += whistleblower_reward / (INCLUDER_REWARD_QUOTIENT + 1) + state.validator_balances[index] -= whistleblower_reward + validator.penalized_epoch = get_current_epoch(state) + validator.withdrawable_epoch = get_current_epoch(state) + LATEST_PENALIZED_EXIT_LENGTH +``` + +The only change is that this introduces the possibility of a penalization where the "whistleblower" that takes credit is NOT the block proposer. + +## Per-slot processing + +### Operations + +Add the following operations to the per-slot processing, in order the given below and _after_ all other operations (specifically, right after exits). + +#### Branch challenges + +Verify that `len(block.body.branch_challenges) <= MAX_BRANCH_CHALLENGES`. + +For each `challenge` in `block.body.branch_challenges`: + +* Verify that `slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY`. +* Verify that `state.validator_registry[responder_index].exit_epoch >= get_current_epoch(state) - MAX_BRANCH_CHALLENGE_DELAY`. +* Verify that `verify_slashable_attestation(state, challenge.attestation)` returns `True`. +* Verify that `challenge.responder_index` is in `challenge.attestation.validator_indices`. +* Let `depth = get_attestation_merkle_depth(challenge.attestation)`. Verify that `challenge.data_index < 2**depth`. +* Verify that there does not exist a `BranchChallengeRecord` in `state.validator_registry[challenge.responder_index].open_branch_challenges` with `root == challenge.attestation.data.shard_chain_commitment` and `data_index == data_index`. +* Append to `state.validator_registry[challenge.responder_index].open_branch_challenges` the object `BranchChallengeRecord(challenger_index=get_beacon_proposer_index(state, state.slot), root=challenge.attestation.data.shard_chain_commitment, depth=depth, inclusion_epoch=get_current_epoch(state), data_index=data_index)`. + +**Invariant**: the `open_branch_challenges` array will always stay sorted in order of `inclusion_epoch`. + +#### Branch responses + +Verify that `len(block.body.branch_responses) <= MAX_BRANCH_RESPONSES`. + +For each `response` in `block.body.branch_responses`: + +* Find the `BranchChallengeRecord` in `state.validator_registry[response.responder_index].open_branch_challenges` whose (`root`, `data_index`) match the (`root`, `data_index`) of the `response`. Verify that one such record exists (it is not possible for there to be more than one), call it `record`. +* Verify that `verify_merkle_branch(leaf=response.data, branch=response.branch, depth=record.depth, index=record.data_index, root=record.root)` is True. +* Verify that `get_current_epoch(state) >= record.inclusion_epoch + ENTRY_EXIT_DELAY`. +* Remove the `record` from `state.validator_registry[response.responder_index].open_branch_challenges` +* Determine the proposer `proposer_index = get_beacon_proposer_index(state, state.slot)` and set `state.validator_balances[proposer_index] += base_reward(state, index) // MINOR_REWARD_QUOTIENT`. + +#### Subkey reveals + +Verify that `len(block.body.early_subkey_reveals) <= MAX_EARLY_SUBKEY_REVEALS`. + +For each `reveal` in `block.body.early_subkey_reveals`: + +* Verify that `verify_custody_subkey_reveal(state.validator_registry[reveal.validator_index].pubkey, reveal.subkey, reveal.period, reveal.mask, state.validator_registry[reveal.revealer_index].pubkey)` returns `True`. +* Let `is_early_reveal = reveal.period > get_current_custody_period(state) or (reveal.period == get_current_custody_period(state) and state.validator_registry[reveal.validator_index].exit_epoch > get_current_epoch(state))` (ie. either the reveal is of a future period, or it's of the current period and the validator is still active) +* Verify that one of the following is true: + * (i) `is_early_reveal` is `True` + * (ii) `is_early_reveal` is `False` and `reveal.period == state.validator_registry[reveal.validator_index].next_subkey_to_reveal` (revealing a past subkey, or a current subkey for a validator that has exited) and `reveal.mask == ZERO_HASH` + +In case (i): + +* Verify that `state.validator_registry[reveal.validator_index].penalized_epoch > get_current_epoch(state). +* Run `penalize_validator(state, reveal.validator_index, reveal.revealer_index)`. +* Set `state.validator_balances[reveal.revealer_index] += base_reward(state, index) // MINOR_REWARD_QUOTIENT` + +In case (ii): + +* Determine the proposer `proposer_index = get_beacon_proposer_index(state, state.slot)` and set `state.validator_balances[proposer_index] += base_reward(state, index) // MINOR_REWARD_QUOTIENT`. +* Set `state.validator_registry[reveal.validator_index].next_subkey_to_reveal += 1` +* Set `state.validator_registry[reveal.validator_index].reveal_max_periods_late = max(state.validator_registry[reveal.validator_index].reveal_max_periods_late, get_current_period(state) - reveal.period)`. + +## Per-epoch processing + +Add the following loop immediately below the `process_ejections` loop: + +```python +def process_challenge_absences(state: BeaconState) -> None: + """ + Iterate through the validator registry + and penalize validators with balance that did not answer challenges. + """ + for index, validator in enumerate(state.validator_registry): + if len(validator.open_branch_challenges) > 0 and get_current_epoch(state) > validator.open_branch_challenges[0].inclusion_epoch + CHALLENGE_RESPONSE_DEADLINE: + penalize_validator(state, index, validator.open_branch_challenges[0].challenger_index) +``` + +In `process_penalties_and_exits`, change the definition of `eligible` to the following (note that it is not a pure function because `state` is declared in the surrounding scope): + +```python +def eligible(index): + validator = state.validator_registry[index] + # Cannot exit if there are still open branch challenges + if len(validator.open_branch_challenges) > 0: + return False + # Cannot exit if you have not revealed all of your subkeys + elif validator.next_subkey_to_reveal <= epoch_to_custody_period(validator.exit_epoch): + return False + # Cannot exit if you already have + elif validator.withdrawable_epoch < FAR_FUTURE_EPOCH: + return False + # Return minimum time + else: + return current_epoch >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWAL_EPOCHS +``` + +## One-time phase 1 initiation transition + +Run the following on the fork block after per-slot processing and before per-block and per-epoch processing. + +For all `validator` in `ValidatorRegistry`, update it to the new format and fill the new member values with: + +```python + 'open_branch_challenges': [], + 'next_subkey_to_reveal': get_current_custody_period(state), + 'reveal_max_periods_late': 0, +``` From 00aa553fee95963b74fbec84dbd274d7247b8a0e Mon Sep 17 00:00:00 2001 From: sigmoid Date: Wed, 20 Feb 2019 01:47:15 +0900 Subject: [PATCH 17/47] updated SSZ implementation list (#653) * updated SSZ implementation list Signed-off-by: sigmoid * Update specs/simple-serialize.md Co-Authored-By: NAKsir-melody --- specs/simple-serialize.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 6021619a60..109ee289e9 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -418,6 +418,7 @@ Note that this convention means that fields after the signature are _not_ signed | Go | [ https://github.com/prysmaticlabs/prysm/tree/master/shared/ssz ](https://github.com/prysmaticlabs/prysm/tree/master/shared/ssz) | Go implementation of SSZ mantained by Prysmatic Labs | | Swift | [ https://github.com/yeeth/SimpleSerialize.swift ](https://github.com/yeeth/SimpleSerialize.swift) | Swift implementation maintained SSZ | | C# | [ https://github.com/codingupastorm/csharp-ssz ](https://github.com/codingupastorm/csharp-ssz) | C# implementation maintained SSZ | +| C++ | [ https://github.com/NAKsir-melody/cpp_ssz](https://github.com/NAKsir-melody/cpp_ssz) | C++ implementation maintained SSZ | ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From a3d85c8405192600dca717939b822b4bb811a112 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 19 Feb 2019 09:49:37 -0800 Subject: [PATCH 18/47] Update 0_beacon-chain.md (#654) --- 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 ba96b04026..d2b20a4547 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1460,7 +1460,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], validator_registry_update_epoch=GENESIS_EPOCH, # Randomness and committees - latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)], + latest_randao_mixes=[EMPTY_SIGNATURE for _ in range(LATEST_RANDAO_MIXES_LENGTH)], previous_shuffling_start_shard=GENESIS_START_SHARD, current_shuffling_start_shard=GENESIS_START_SHARD, previous_shuffling_epoch=GENESIS_EPOCH, From 9b7b35bc9d18d0fac92ee142f1ea66ab289d3175 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 19 Feb 2019 21:08:10 +0000 Subject: [PATCH 19/47] Reduce GENESIS_SLOT to 2**32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Address the slot/epoch underflow problem, even for Java implementers! 🎉 * Squash a bug with `get_previous_epoch` * Fix #642 * Address #626 (Vitalik, Danny, myself agree that avoiding signed integers is probably best) --- 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 d2b20a4547..1278db4f75 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -212,7 +212,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | Name | Value | | - | - | | `GENESIS_FORK_VERSION` | `0` | -| `GENESIS_SLOT` | `2**63` | +| `GENESIS_SLOT` | `2**32` | | `GENESIS_EPOCH` | `slot_to_epoch(GENESIS_SLOT)` | | `GENESIS_START_SHARD` | `0` | | `FAR_FUTURE_EPOCH` | `2**64 - 1` | @@ -688,12 +688,8 @@ def slot_to_epoch(slot: Slot) -> Epoch: def get_previous_epoch(state: BeaconState) -> Epoch: """` Return the previous epoch of the given ``state``. - If the current epoch is ``GENESIS_EPOCH``, return ``GENESIS_EPOCH``. """ - current_epoch = get_current_epoch(state) - if current_epoch == GENESIS_EPOCH: - return GENESIS_EPOCH - return current_epoch - 1 + return get_current_epoch(state) - 1 ``` ### `get_current_epoch` From 8262ce1cc01d70447056f7af831a9b5b75950bd3 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Tue, 19 Feb 2019 15:07:30 -0700 Subject: [PATCH 20/47] Fix typo from earlier renaming in #534. --- 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 d2b20a4547..7db2147955 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1352,7 +1352,7 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward validator.slashed_epoch = get_current_epoch(state) - validator.withdrawable_epoch = get_current_epoch(state) + LATEST_PENALIZED_EXIT_LENGTH + validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH ``` #### `prepare_validator_for_withdrawal` From 09e8448763fee506c389555a77e72565a6029f43 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 19 Feb 2019 20:00:46 -0600 Subject: [PATCH 21/47] Turned slashed and initiated_exit into booleans Cuts validator record size from 120 to 106 bytes, and arguably is a simplification --- specs/core/0_beacon-chain.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7db2147955..c70866996d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -256,11 +256,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. * The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. * The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. -### Status flags - -| Name | Value | -| - | - | -| `INITIATED_EXIT` | `2**0` (= 1) | ### Max transactions per block @@ -571,10 +566,10 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'exit_epoch': 'uint64', # Epoch when validator is eligible to withdraw 'withdrawable_epoch': 'uint64', - # Epoch when validator was slashed - 'slashed_epoch': 'uint64', - # Status flags - 'status_flags': 'uint64', + # Did the validator initiate an exit + 'initiated_exit': 'bool', + # Was the validator penalized + 'slashed': 'bool', } ``` @@ -1273,8 +1268,8 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, withdrawable_epoch=FAR_FUTURE_EPOCH, - slashed_epoch=FAR_FUTURE_EPOCH, - status_flags=0, + initiated_exit=False, + slashed=False, ) # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. @@ -1314,7 +1309,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: Note that this function mutates ``state``. """ validator = state.validator_registry[index] - validator.status_flags |= INITIATED_EXIT + validator.initiated_exit = True ``` #### `exit_validator` @@ -1351,7 +1346,7 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - validator.slashed_epoch = get_current_epoch(state) + validator.slashed = True validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH ``` @@ -1644,7 +1639,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot`. * Verify that `proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard`. * Verify that `proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root`. -* Verify that `proposer.slashed_epoch > get_current_epoch(state)`. +* Verify that `proposer.slashed == False`. * Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_1, "signature"), signature=proposer_slashing.proposal_1.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_1.slot), DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_2, "signature"), signature=proposer_slashing.proposal_2.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_2.slot), DOMAIN_PROPOSAL))`. * Run `slash_validator(state, proposer_slashing.proposer_index)`. @@ -1661,7 +1656,7 @@ For each `attester_slashing` in `block.body.attester_slashings`: * Verify that `is_double_vote(slashable_attestation_1.data, slashable_attestation_2.data)` or `is_surround_vote(slashable_attestation_1.data, slashable_attestation_2.data)`. * Verify that `verify_slashable_attestation(state, slashable_attestation_1)`. * Verify that `verify_slashable_attestation(state, slashable_attestation_2)`. -* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed_epoch > get_current_epoch(state)]`. +* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed == False]`. * Verify that `len(slashable_indices) >= 1`. * Run `slash_validator(state, index)` for each `index` in `slashable_indices`. @@ -1895,7 +1890,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active validator](#dfn-active-validator) `index` with `validator.slashed_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active validator](#dfn-active-validator) `index` with `validator.slashed == True`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` loses `base_reward(state, index) - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` ##### Attestation inclusion @@ -1974,7 +1969,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and validator.status_flags & INITIATED_EXIT: + if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and validator.initiated_exit: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -2015,7 +2010,7 @@ def process_slashings(state: BeaconState) -> None: total_balance = sum(get_effective_balance(state, i) for i in active_validator_indices) for index, validator in enumerate(state.validator_registry): - if current_epoch == validator.slashed_epoch + LATEST_SLASHED_EXIT_LENGTH // 2: + if validator.slashed and current_epoch == validator.withdrawable_epoch - LATEST_SLASHED_EXIT_LENGTH // 2: epoch_index = current_epoch % LATEST_SLASHED_EXIT_LENGTH total_at_start = state.latest_slashed_balances[(epoch_index + 1) % LATEST_SLASHED_EXIT_LENGTH] total_at_end = state.latest_slashed_balances[epoch_index] From 461cd8b5995f1ea810c74e1643542686aaea5168 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 20 Feb 2019 00:46:28 -0600 Subject: [PATCH 22/47] Rename shard_block_root -> shard_data_commitment --- specs/core/0_beacon-chain.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7db2147955..1d3ae80ef5 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 'beacon_block_root': 'bytes32', # Root of the ancestor at the epoch boundary 'epoch_boundary_root': 'bytes32', - # Shard block's hash of root - 'shard_block_root': 'bytes32', + # Data from the shard since the last attestation + 'shard_data_commitment': 'bytes32', # Last crosslink 'latest_crosslink': Crosslink, # Last justified epoch in the beacon state @@ -585,7 +585,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Epoch number 'epoch': 'uint64', # Shard block root - 'shard_block_root': 'bytes32', + 'shard_data_commitment': 'bytes32', } ``` @@ -1475,7 +1475,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], finalized_epoch=GENESIS_EPOCH, # Recent state - latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, shard_data_commitment=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_active_index_roots=[ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)], latest_slashed_balances=[0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)], @@ -1674,7 +1674,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + SLOTS_PER_EPOCH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if slot_to_epoch(attestation.data.slot + 1) >= 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_data_commitment=attestation.data.shard_data_commitment, epoch=slot_to_epoch(attestation.data.slot))`. * Verify bitfields and aggregate signature: ```python @@ -1707,7 +1707,7 @@ For each `attestation` in `block.body.attestations`: ) ``` -* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`. +* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_data_commitment == ZERO_HASH`. * Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, inclusion_slot=state.slot)` to `state.latest_attestations`. ##### Deposits @@ -1815,10 +1815,10 @@ The steps below happen when `(state.slot + 1) % SLOTS_PER_EPOCH == 0`. 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: -* 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 `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 `shard_data_commitment` be `state.latest_crosslinks[shard].shard_data_commitment` +* Let `attesting_validator_indices(crosslink_committee, shard_data_commitment)` 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_data_commitment == shard_data_commitment]`. +* Let `winning_data_commitment(crosslink_committee)` be equal to the value of `shard_data_commitment` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, shard_data_commitment))` is maximized (ties broken by favoring lower `shard_data_commitment` values). +* Let `attesting_validators(crosslink_committee)` be equal to `attesting_validator_indices(crosslink_committee, winning_data_commitment(crosslink_committee))` for convenience. * 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`: @@ -1858,7 +1858,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=slot_to_epoch(slot), 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_data_commitment=winning_data_commitment(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. #### Rewards and penalties From 9ac61e57c006f2361416226b5bbb62a331f72fc0 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 Feb 2019 07:18:12 +0000 Subject: [PATCH 23/47] Update 0_beacon-chain.md --- 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 c70866996d..e2136d9632 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -18,7 +18,6 @@ - [Time parameters](#time-parameters) - [State list lengths](#state-list-lengths) - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status flags](#status-flags) - [Max transactions per block](#max-transactions-per-block) - [Signature domains](#signature-domains) - [Data structures](#data-structures) @@ -568,7 +567,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'withdrawable_epoch': 'uint64', # Did the validator initiate an exit 'initiated_exit': 'bool', - # Was the validator penalized + # Was the validator slashed 'slashed': 'bool', } ``` From e25e87f00340b9e9414b6e408233471b5d6fd0fb Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 Feb 2019 07:45:19 +0000 Subject: [PATCH 24/47] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e2136d9632..9463d3b19e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -89,7 +89,7 @@ - [`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) + - [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch) - [`bls_verify`](#bls_verify) - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) @@ -1208,13 +1208,12 @@ def integer_squareroot(n: int) -> int: return x ``` -### `get_entry_exit_effect_epoch` +### `get_delayed_activation_exit_epoch` ```python -def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: +def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: """ - An entry or exit triggered in the ``epoch`` given by the input takes effect at - the epoch given by the output. + Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. """ return epoch + 1 + ACTIVATION_EXIT_DELAY ``` @@ -1296,7 +1295,7 @@ def activate_validator(state: BeaconState, index: ValidatorIndex, is_genesis: bo """ validator = state.validator_registry[index] - validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) + validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### `initiate_validator_exit` @@ -1322,10 +1321,10 @@ def exit_validator(state: BeaconState, index: ValidatorIndex) -> None: validator = state.validator_registry[index] # The following updates only occur if not previous exited - if validator.exit_epoch <= get_entry_exit_effect_epoch(get_current_epoch(state)): + if validator.exit_epoch <= get_delayed_activation_exit_epoch(get_current_epoch(state)): return - validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state)) + validator.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### `slash_validator` @@ -1746,7 +1745,7 @@ Verify that `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`. For each `exit` in `block.body.voluntary_exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))`. +* Verify that `validator.exit_epoch > get_delayed_activation_exit_epoch(get_current_epoch(state))`. * Verify that `get_current_epoch(state) >= exit.epoch`. * Verify that `bls_verify(pubkey=validator.pubkey, message_hash=signed_root(exit, "signature"), signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. @@ -1956,7 +1955,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch > get_entry_exit_effect_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch > get_delayed_activation_exit_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1968,7 +1967,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and validator.initiated_exit: + if validator.exit_epoch > get_delayed_activation_exit_epoch(current_epoch) and validator.initiated_exit: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: From a79fa398f1e83322909c0f96577140bfa4e75d09 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 20 Feb 2019 19:42:53 +0800 Subject: [PATCH 25/47] Minor fix: `messages` -> `message_hashes` (#664) --- 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 7db2147955..f559317619 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1698,7 +1698,7 @@ For each `attestation` in `block.body.attestations`: bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_participants]), bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_participants]), ], - messages=[ + message_hashes=[ hash_tree_root(AttestationDataAndCustodyBit(data=attestation.data, custody_bit=0b0)), hash_tree_root(AttestationDataAndCustodyBit(data=attestation.data, custody_bit=0b1)), ], From 26908d55790fcb24bdf89c13957a54bba3d75e22 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 20 Feb 2019 23:58:25 +0800 Subject: [PATCH 26/47] Fix constant name `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` -> `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` (#661) --- 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 f559317619..93d5c2cd2e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -232,7 +232,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ACTIVATION_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | | `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | -| `MIN_VALIDATOR_WITHDRAWAL_DELAY` | `2**8` (= 256) | epochs | ~27 hours | +| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | ### State list lengths From f8d073c099f51c47fbe8b87d8698515e9dbb1f2f Mon Sep 17 00:00:00 2001 From: Patrick Gallagher <35622595+pi0neerpat@users.noreply.github.com> Date: Thu, 21 Feb 2019 08:46:12 -0500 Subject: [PATCH 27/47] Fixed improperly formatted table in Constants (#666) From 5f59aad00d5ae5e719d084972c73ff2a8238fa59 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 21 Feb 2019 22:34:11 +0100 Subject: [PATCH 28/47] Clarify use of `FAR_FUTURE_EPOCH` as a flag --- 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 9463d3b19e..51cc9d9f54 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1955,7 +1955,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch > get_delayed_activation_exit_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch == FAR_FUTURE_EPOCH and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1967,7 +1967,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > get_delayed_activation_exit_epoch(current_epoch) and validator.initiated_exit: + if validator.activation_epoch == FAR_FUTURE_EPOCH and validator.initiated_exit: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -2029,7 +2029,7 @@ def process_exit_queue(state: BeaconState) -> None: def eligible(index): validator = state.validator_registry[index] # Filter out dequeued validators - if validator.withdrawable_epoch < FAR_FUTURE_EPOCH: + if validator.withdrawable_epoch != FAR_FUTURE_EPOCH: return False # Dequeue if the minimum amount of time has passed else: From 0f222171ca3d43541c7104990fdec26686be6468 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 21 Feb 2019 15:14:59 -0700 Subject: [PATCH 29/47] fix epoch boundary root per #652 --- specs/validator/0_beacon-chain-validator.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 6fdeafb49c..1379e49ae2 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -262,7 +262,9 @@ Set `attestation_data.beacon_block_root = hash_tree_root(head)` where `head` is 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))`. -_Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(slot_to_epoch(head.slot)))`. +_Note:_ This can be looked up in the state using: +* Let `epoch_start_slot = get_epoch_start_slot(slot_to_epoch(head.slot))`. `get_block_root(state, 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)`. ##### Shard block root From d31aeacd3c9937d0ebc32f5370667733a9529f68 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 21 Feb 2019 15:18:41 -0700 Subject: [PATCH 30/47] fix minor errors per #628 --- 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 1379e49ae2..f20fe587b3 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -263,7 +263,7 @@ Set `attestation_data.beacon_block_root = hash_tree_root(head)` where `head` is 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))`. _Note:_ This can be looked up in the state using: -* Let `epoch_start_slot = get_epoch_start_slot(slot_to_epoch(head.slot))`. `get_block_root(state, get_epoch_start_slot(slot_to_epoch(head.slot)))`. +* 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)`. ##### Shard block root @@ -282,7 +282,7 @@ Set `attestation_data.justified_epoch = state.justified_epoch` where `state` is ##### Justified block root -Set `attestation_data.justified_block_root = hash_tree_root(justified_block)` where `justified_block` is the block at `state.justified_epoch` in the chain defined by `head`. +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, justified_epoch)`. From 2bd6057c127273baa77e126ee647117d430bdfed Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 21 Feb 2019 16:53:52 -0700 Subject: [PATCH 31/47] make getting assignments more generic --- specs/validator/0_beacon-chain-validator.md | 46 ++++++++++++--------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 6fdeafb49c..21577c6101 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -50,7 +50,8 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Aggregation bitfield](#aggregation-bitfield) - [Custody bitfield](#custody-bitfield) - [Aggregate signature](#aggregate-signature) - - [Responsibility lookahead](#responsibility-lookahead) + - [Validator assigments](#validator-assignments) + - [Lookahead](#lookahead) - [How to avoid slashing](#how-to-avoid-slashing) - [Proposer slashing](#proposer-slashing) - [Attester slashing](#attester-slashing) @@ -330,26 +331,18 @@ signed_attestation_data = bls_sign( ) ``` -## Responsibility lookahead +## Validator assignments -The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming responsibilities of proposing and attesting dictated by the shuffling and slot. - -There are three possibilities for the shuffling at the next epoch: -1. The shuffling changes due to a "validator registry change". -2. The shuffling changes due to `epochs_since_last_registry_update` being an exact power of 2 greater than 1. -3. The shuffling remains the same (i.e. the validator is in the same shard committee). - -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 committee assignments in the next epoch for a given `validator_index` and `registry_change`. +A validator can get the current and previous epoch committee assignments using the following helper via `get_committee_assignment(state, epoch, validator_index)` where `previous_epoch <= epoch <= current_epoch`. ```python -def get_next_epoch_committee_assignment( +def get_committee_assignment( state: BeaconState, + epoch: Epoch, validator_index: ValidatorIndex, - registry_change: bool) -> Tuple[List[ValidatorIndex], Shard, Slot, bool]: + registry_change: bool=False) -> Tuple[List[ValidatorIndex], Shard, Slot, bool]: """ - Return the committee assignment in the next epoch for ``validator_index`` and ``registry_change``. + Return the committee assignment in the ``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 @@ -357,10 +350,10 @@ def get_next_epoch_committee_assignment( * ``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) - for slot in range(next_epoch_start_slot, next_epoch_start_slot + SLOTS_PER_EPOCH): + assert previous_epoch <= epoch <= next_epoch + + epoch_start_slot = get_epoch_start_slot(epoch) + for slot in range(epoch_start_slot, epoch_start_slot + SLOTS_PER_EPOCH): crosslink_committees = get_crosslink_committees_at_slot( state, slot, @@ -381,7 +374,20 @@ def get_next_epoch_committee_assignment( return assignment ``` -`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+). +### Lookahead + +The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming assignemnts of proposing and attesting dictated by the shuffling and slot. + +There are three possibilities for the shuffling at the next epoch: +1. The shuffling changes due to a "validator registry change". +2. The shuffling changes due to `epochs_since_last_registry_update` being an exact power of 2 greater than 1. +3. The shuffling remains the same (i.e. the validator is in the same shard committee). + +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 `get_committee_assignment` helper can be used to get the potential crosslink committee assignments in the next epoch for a given `validator_index` and `registry_change`. +`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`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+). +Specifically, a validator should call both `get_committee_assignment(state, next_epoch, validator_index, registry_chang=True)` and `get_committee_assignment(state, next_epoch, validator_index, registry_change=False)` when checking for next epoch assignments. ## How to avoid slashing From f28af3f72b52baf43a9fec72038c0280c611993d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 21 Feb 2019 16:56:43 -0700 Subject: [PATCH 32/47] Add spacing --- specs/validator/0_beacon-chain-validator.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 21577c6101..74ab2e098b 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -386,7 +386,9 @@ 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 `get_committee_assignment` helper can be used to get the potential crosslink committee assignments in the next epoch for a given `validator_index` and `registry_change`. + `get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`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+). + Specifically, a validator should call both `get_committee_assignment(state, next_epoch, validator_index, registry_chang=True)` and `get_committee_assignment(state, next_epoch, validator_index, registry_change=False)` when checking for next epoch assignments. ## How to avoid slashing From 8169229f5aa033a5f9d394aea463b8d5a5b49b6b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 21 Feb 2019 16:58:58 -0700 Subject: [PATCH 33/47] clean up --- specs/validator/0_beacon-chain-validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 74ab2e098b..5d75e71425 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -385,7 +385,7 @@ 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 `get_committee_assignment` helper can be used to get the potential crosslink committee assignments in the next epoch for a given `validator_index` and `registry_change`. +When querying for assignments in the next epoch there are two options -- with and without a `registry_change` -- which is the optional fourth parameter of the `get_committee_assignment`. `get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`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+). From f77b9e241435e7b67a81cf5feabe595265d5f4c8 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 22 Feb 2019 00:21:56 -0600 Subject: [PATCH 34/47] Changed name to crosslink data commitment --- specs/core/0_beacon-chain.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 1d3ae80ef5..fcca0d47e8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -363,7 +363,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git # Root of the ancestor at the epoch boundary 'epoch_boundary_root': 'bytes32', # Data from the shard since the last attestation - 'shard_data_commitment': 'bytes32', + 'crosslink_data_root': 'bytes32', # Last crosslink 'latest_crosslink': Crosslink, # Last justified epoch in the beacon state @@ -584,8 +584,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git { # Epoch number 'epoch': 'uint64', - # Shard block root - 'shard_data_commitment': 'bytes32', + # Shard data since the previous crosslink + 'crosslink_data_root': 'bytes32', } ``` @@ -1475,7 +1475,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], finalized_epoch=GENESIS_EPOCH, # Recent state - latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, shard_data_commitment=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_active_index_roots=[ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)], latest_slashed_balances=[0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)], @@ -1674,7 +1674,7 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + SLOTS_PER_EPOCH`. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if slot_to_epoch(attestation.data.slot + 1) >= 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_data_commitment=attestation.data.shard_data_commitment, 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(crosslink_data_root=attestation.data.crosslink_data_root, epoch=slot_to_epoch(attestation.data.slot))`. * Verify bitfields and aggregate signature: ```python @@ -1707,7 +1707,7 @@ For each `attestation` in `block.body.attestations`: ) ``` -* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_data_commitment == ZERO_HASH`. +* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`. * Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, inclusion_slot=state.slot)` to `state.latest_attestations`. ##### Deposits @@ -1815,10 +1815,10 @@ The steps below happen when `(state.slot + 1) % SLOTS_PER_EPOCH == 0`. 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: -* Let `shard_data_commitment` be `state.latest_crosslinks[shard].shard_data_commitment` -* Let `attesting_validator_indices(crosslink_committee, shard_data_commitment)` 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_data_commitment == shard_data_commitment]`. -* Let `winning_data_commitment(crosslink_committee)` be equal to the value of `shard_data_commitment` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, shard_data_commitment))` is maximized (ties broken by favoring lower `shard_data_commitment` values). -* Let `attesting_validators(crosslink_committee)` be equal to `attesting_validator_indices(crosslink_committee, winning_data_commitment(crosslink_committee))` for convenience. +* Let `crosslink_data_root` be `state.latest_crosslinks[shard].crosslink_data_root` +* Let `attesting_validator_indices(crosslink_committee, crosslink_data_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.crosslink_data_root == crosslink_data_root]`. +* Let `winning_root(crosslink_committee)` be equal to the value of `crosslink_data_root` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))` is maximized (ties broken by favoring lower `crosslink_data_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) = 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`: @@ -1858,7 +1858,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=slot_to_epoch(slot), shard_data_commitment=winning_data_commitment(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), crosslink_data_root=winning_root(crosslink_committee))` if `3 * total_attesting_balance(crosslink_committee) >= 2 * get_total_balance(crosslink_committee)`. #### Rewards and penalties From e8d41fd8895b60742e2a24d0001763aae711e1bf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 22 Feb 2019 14:30:07 +0800 Subject: [PATCH 35/47] Fix missing `get_epoch_start_slot()` --- specs/validator/0_beacon-chain-validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index f20fe587b3..c469e5970b 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -284,7 +284,7 @@ Set `attestation_data.justified_epoch = state.justified_epoch` where `state` is 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, justified_epoch)`. +_Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(state.justified_epoch))`. #### Construct attestation From a8c3c45fafb24153daf566bb0dc55858c6838150 Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Fri, 22 Feb 2019 12:58:59 +0100 Subject: [PATCH 36/47] Update 0_beacon-chain.md --- 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 0556f3178a..f7aa1c6d7d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1028,10 +1028,7 @@ def is_power_of_two(value: int) -> bool: """ Check if ``value`` is a power of two integer. """ - if value == 0: - return False - else: - return 2**int(math.log2(value)) == value + return (value > 0) && (value & (value - 1) == 0) ``` ### `int_to_bytes1`, `int_to_bytes2`, ... From e1fc8a5765eb225f1c3485935b63a6c6bef3f5d4 Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Fri, 22 Feb 2019 13:00:29 +0100 Subject: [PATCH 37/47] 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 f7aa1c6d7d..5c67181376 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1028,7 +1028,7 @@ def is_power_of_two(value: int) -> bool: """ Check if ``value`` is a power of two integer. """ - return (value > 0) && (value & (value - 1) == 0) + return (value > 0) and (value & (value - 1) == 0) ``` ### `int_to_bytes1`, `int_to_bytes2`, ... From 68b5fe5b9fd5576661f4874c7ba31efed44765f7 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 22 Feb 2019 08:15:40 -0700 Subject: [PATCH 38/47] small typo Co-Authored-By: djrtwo --- specs/validator/0_beacon-chain-validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 5d75e71425..7b4404484b 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -389,7 +389,7 @@ When querying for assignments in the next epoch there are two options -- with an `get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`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+). -Specifically, a validator should call both `get_committee_assignment(state, next_epoch, validator_index, registry_chang=True)` and `get_committee_assignment(state, next_epoch, validator_index, registry_change=False)` when checking for next epoch assignments. +Specifically, a validator should call both `get_committee_assignment(state, next_epoch, validator_index, registry_change=True)` and `get_committee_assignment(state, next_epoch, validator_index, registry_change=False)` when checking for next epoch assignments. ## How to avoid slashing From a68341653733e5b3705bf22d9d637a40b491f875 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 22 Feb 2019 08:36:36 -0700 Subject: [PATCH 39/47] Fix slot range for attestation inclusion (#669) * Fix slot range for attestation inclusion * Update specs/core/0_beacon-chain.md Co-Authored-By: djrtwo --- specs/core/0_beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 9ed06af3b6..ab9d918ffc 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1657,7 +1657,8 @@ Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`. For each `attestation` in `block.body.attestations`: -* Verify that `attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + SLOTS_PER_EPOCH`. +* Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`. +* Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if slot_to_epoch(attestation.data.slot + 1) >= 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))`. From b89cd234f0d6f3d62edacadec8884e40f52a400c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 22 Feb 2019 08:50:03 -0700 Subject: [PATCH 40/47] change shard_block_root to crosslink_data_root throughout --- specs/core/1_shard-data-chains.md | 19 ++++++++++--------- specs/validator/0_beacon-chain-validator.md | 6 +++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 363437ae52..48eea7d2be 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -16,10 +16,10 @@ - [Max operations per block](#max-operations-per-block) - [Signature domains](#signature-domains) - [Helper functions](#helper-functions) - - [get_split_offset](#get_split_offset) - - [get_shuffled_committee](#get_shuffled_committee) - - [get_persistent_committee](#get_persistent_committee) - - [get_shard_proposer_index](#get_shard_proposer_index) + - [`get_split_offset`](#get_split_offset) + - [`get_shuffled_committee`](#get_shuffled_committee) + - [`get_persistent_committee`](#get_persistent_committee) + - [`get_shard_proposer_index`](#get_shard_proposer_index) - [Data Structures](#data-structures) - [Shard chain blocks](#shard-chain-blocks) - [Shard block processing](#shard-block-processing) @@ -100,7 +100,7 @@ Phase 1 depends upon all of the constants defined in [Phase 0](0_beacon-chain.md ## Helper functions -#### get_split_offset +#### `get_split_offset` ````python def get_split_offset(list_size: int, chunks: int, index: int) -> int: @@ -111,7 +111,7 @@ def get_split_offset(list_size: int, chunks: int, index: int) -> int: return (len(list_size) * index) // chunks ```` -#### get_shuffled_committee +#### `get_shuffled_committee` ```python def get_shuffled_committee(state: BeaconState, @@ -130,7 +130,7 @@ def get_shuffled_committee(state: BeaconState, ] ``` -#### get_persistent_committee +#### `get_persistent_committee` ```python def get_persistent_committee(state: BeaconState, @@ -158,7 +158,8 @@ def get_persistent_committee(state: BeaconState, [i for i in later_committee if epoch % PERSISTENT_COMMITTEE_PERIOD >= get_switchover_epoch(i)] ))) ``` -#### get_shard_proposer_index + +#### `get_shard_proposer_index` ```python def get_shard_proposer_index(state: BeaconState, @@ -289,7 +290,7 @@ The `shard_chain_commitment` is only valid if it equals `compute_commitment(head ### Shard block fork choice rule -The fork choice rule for any shard is LMD GHOST using the shard chain attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). +The fork choice rule for any shard is LMD GHOST using the shard chain attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the latest block referenced in the most recent accepted crosslink (ie. `state.crosslinks[shard].crosslink_data_root`). Only blocks whose `beacon_chain_ref` is the block in the main beacon chain at the specified `slot` should be considered (if the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot). # Updates to the beacon chain diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 6fdeafb49c..15188a9298 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -41,7 +41,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers - [Shard](#shard) - [Beacon block root](#beacon-block-root) - [Epoch boundary root](#epoch-boundary-root) - - [Shard block root](#shard-block-root) + - [Crosslink data root](#crosslink-data-root) - [Latest crosslink](#latest-crosslink) - [Justified epoch](#justified-epoch) - [Justified block root](#justified-block-root) @@ -264,9 +264,9 @@ Set `attestation_data.epoch_boundary_root = hash_tree_root(epoch_boundary)` wher _Note:_ This can be looked up in the state using `get_block_root(state, get_epoch_start_slot(slot_to_epoch(head.slot)))`. -##### Shard block root +##### Crosslink data root -Set `attestation_data.shard_block_root = ZERO_HASH`. +Set `attestation_data.crosslink_data_root = ZERO_HASH`. _Note:_ This is a stub for phase 0. From c96f7095053bd451527df1f8405b28da5f00768c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 23 Feb 2019 00:31:27 +0800 Subject: [PATCH 41/47] Update 0_beacon-chain-validator.md --- specs/validator/0_beacon-chain-validator.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 7b4404484b..82b1827a5e 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -350,6 +350,8 @@ def get_committee_assignment( * ``assignment[3]`` is a bool signalling if the validator is expected to propose a beacon block at the assigned slot. """ + previous_epoch = get_previous_epoch(state) + next_epoch = get_current_epoch(state) assert previous_epoch <= epoch <= next_epoch epoch_start_slot = get_epoch_start_slot(epoch) From 060e3c36c53f8000a5c8d382e3097ef0f6ecb234 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 22 Feb 2019 12:51:09 -0700 Subject: [PATCH 42/47] fix up previous epoch logic around genesis (#672) --- specs/core/0_beacon-chain.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e0b23d6ee0..1db5eb3c03 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -683,7 +683,7 @@ def get_previous_epoch(state: BeaconState) -> Epoch: """` Return the previous epoch of the given ``state``. """ - return get_current_epoch(state) - 1 + return max(get_current_epoch(state) - 1, GENESIS_EPOCH) ``` ### `get_current_epoch` @@ -865,16 +865,16 @@ def get_crosslink_committees_at_slot(state: BeaconState, assert previous_epoch <= epoch <= next_epoch - if epoch == previous_epoch: - committees_per_epoch = get_previous_epoch_committee_count(state) - seed = state.previous_shuffling_seed - shuffling_epoch = state.previous_shuffling_epoch - shuffling_start_shard = state.previous_shuffling_start_shard - elif epoch == current_epoch: + if epoch == current_epoch: committees_per_epoch = get_current_epoch_committee_count(state) seed = state.current_shuffling_seed shuffling_epoch = state.current_shuffling_epoch shuffling_start_shard = state.current_shuffling_start_shard + elif epoch == previous_epoch: + committees_per_epoch = get_previous_epoch_committee_count(state) + seed = state.previous_shuffling_seed + shuffling_epoch = state.previous_shuffling_epoch + shuffling_start_shard = state.previous_shuffling_start_shard elif epoch == next_epoch: current_committees_per_epoch = get_current_epoch_committee_count(state) committees_per_epoch = get_next_epoch_committee_count(state) @@ -1657,6 +1657,7 @@ Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`. For each `attestation` in `block.body.attestations`: +* Verify that `attestation.data.slot >= GENESIS_SLOT`. * Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`. * Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH. * Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch if slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state) else state.previous_justified_epoch`. From 1d757aec7476adef9b26b2e36303d6a96f722383 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 22 Feb 2019 21:02:03 +0100 Subject: [PATCH 43/47] Specify lexicographic ordering of crosslink_data_root Fix #542. --- 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 1db5eb3c03..bab14bbd25 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1805,7 +1805,7 @@ For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_s * Let `crosslink_data_root` be `state.latest_crosslinks[shard].crosslink_data_root` * Let `attesting_validator_indices(crosslink_committee, crosslink_data_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.crosslink_data_root == crosslink_data_root]`. -* Let `winning_root(crosslink_committee)` be equal to the value of `crosslink_data_root` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))` is maximized (ties broken by favoring lower `crosslink_data_root` values). +* Let `winning_root(crosslink_committee)` be equal to the value of `crosslink_data_root` such that `get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))` is maximized (ties broken by favoring lexicographically smallest `crosslink_data_root`). * 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) = get_total_balance(state, attesting_validators(crosslink_committee))`. From da6b5466d5d5417b76da6e884025abd96341b8c0 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 23 Feb 2019 20:25:55 +0100 Subject: [PATCH 44/47] Allow transfers from non-activated validators See item 6 in https://github.com/ethereum/eth2.0-specs/issues/675. This makes transfers more generic. --- 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 1db5eb3c03..577ae9ee62 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1757,7 +1757,7 @@ For each `transfer` in `block.body.transfers`: * Verify that `state.validator_balances[transfer.from] >= transfer.fee`. * Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. * Verify that `state.slot == transfer.slot`. -* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch`. +* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch` or `get_current_epoch(state) < state.validator_registry[transfer.from].activation_epoch`. * Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. * Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=signed_root(transfer, "signature"), signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. * Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. From 6b5769a861d141866cbfae75c10a09ccd01531aa Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 25 Feb 2019 10:30:03 +0100 Subject: [PATCH 45/47] 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 577ae9ee62..d3421b0083 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1757,7 +1757,7 @@ For each `transfer` in `block.body.transfers`: * Verify that `state.validator_balances[transfer.from] >= transfer.fee`. * Verify that `state.validator_balances[transfer.from] == transfer.amount + transfer.fee` or `state.validator_balances[transfer.from] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT`. * Verify that `state.slot == transfer.slot`. -* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch` or `get_current_epoch(state) < state.validator_registry[transfer.from].activation_epoch`. +* Verify that `get_current_epoch(state) >= state.validator_registry[transfer.from].withdrawable_epoch` or `state.validator_registry[transfer.from].activation_epoch == FAR_FUTURE_EPOCH`. * Verify that `state.validator_registry[transfer.from].withdrawal_credentials == BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]`. * Verify that `bls_verify(pubkey=transfer.pubkey, message_hash=signed_root(transfer, "signature"), signature=transfer.signature, domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER))`. * Set `state.validator_balances[transfer.from] -= transfer.amount + transfer.fee`. From fbb70e91eb4daef0629a35e1b472bca7e1ea9322 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 25 Feb 2019 07:58:23 -0700 Subject: [PATCH 46/47] remove todo --- 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 9df89766a3..06b4cfb4fd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1702,7 +1702,6 @@ For each `attestation` in `block.body.attestations`: Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. -[TODO: add logic to ensure that deposits from 1.0 chain are processed in order] [TODO: update the call to `verify_merkle_branch` below if it needs to change after we process deposits in order] For each `deposit` in `block.body.deposits`: From b2a3ee4bbc030266fee15bae1299319fc122bac6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 25 Feb 2019 09:36:52 -0700 Subject: [PATCH 47/47] skip bad proof of possession deposits --- specs/core/0_beacon-chain.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 06b4cfb4fd..6bff0f705f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1235,7 +1235,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: """ deposit_input = deposit.deposit_data.deposit_input - assert bls_verify( + proof_is_valid = bls_verify( pubkey=deposit_input.pubkey, message_hash=signed_root(deposit_input, "proof_of_possession"), signature=deposit_input.proof_of_possession, @@ -1245,6 +1245,9 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: DOMAIN_DEPOSIT, ) ) + + if not proof_is_valid: + return validator_pubkeys = [v.pubkey for v in state.validator_registry] pubkey = deposit_input.pubkey