From e0e2fed1b56b628efac016fec00872b8948983a7 Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 11 Dec 2018 09:53:56 -0800 Subject: [PATCH 01/13] rename hash tree roots as root --- specs/core/0_beacon-chain.md | 42 +++++++++++++++---------------- specs/core/1_shard-data-chains.md | 8 +++--- specs/simple-serialize.md | 10 ++++---- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 06e9c2a56..de105ee5d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -64,7 +64,7 @@ - [`clamp`](#clamp) - [`get_new_shuffling`](#get_new_shuffling) - [`get_shard_committees_at_slot`](#get_shard_committees_at_slot) - - [`get_block_hash`](#get_block_hash) + - [`get_block_root`](#get_block_root) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`get_updated_ancestor_hashes`](#get_updated_ancestor_hashes) - [`get_attestation_participants`](#get_attestation_participants) @@ -73,7 +73,7 @@ - [`get_new_validator_registry_delta_chain_tip`](#get_new_validator_registry_delta_chain_tip) - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - - [`SSZTreeHash`](#ssztreehash) + - [`hash_tree_root`](#hash_tree_root) - [`verify_casper_votes`](#verify_casper_votes) - [`integer_squareroot`](#integer_squareroot) - [`BLSVerify`](#blsverify) @@ -322,7 +322,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # Slot of the last justified beacon block 'justified_slot': 'uint64', # Hash of the last justified beacon block - 'justified_block_hash': 'hash32', + 'justified_block_root': 'hash32', } ``` @@ -655,7 +655,7 @@ Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Cli For a beacon chain block, `block`, to be processed by a node, the following conditions must be met: -* The parent block with `SSZTreeHash` `block.ancestor_hashes[0]` has been processed and accepted. +* The parent block with `hash_tree_root` `block.ancestor_hashes[0]` has been processed and accepted. * The node has processed its `state` up to slot, `block.slot - 1`. * The Ethereum 1.0 block pointed to by the `state.processed_pow_receipt_root` has been processed and accepted. * The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. @@ -869,10 +869,10 @@ def get_shard_committees_at_slot(state: BeaconState, return state.shard_committees_at_slots[slot - earliest_slot_in_array] ``` -#### `get_block_hash` +#### `get_block_root` ```python -def get_block_hash(state: BeaconState, +def get_block_root(state: BeaconState, slot: int) -> Hash32: """ Returns the block hash at a recent ``slot``. @@ -882,7 +882,7 @@ def get_block_hash(state: BeaconState, return state.latest_block_hashes[slot - earliest_slot_in_array] ``` -`get_block_hash(_, s)` should always return `SSZTreeHash` of the block in the beacon chain at slot `s`, and `get_shard_committees_at_slot(_, s)` should not change unless the [validator](#dfn-validator) registry changes. +`get_block_root(_, s)` should always return `hash_tree_root` of the block in the beacon chain at slot `s`, and `get_shard_committees_at_slot(_, s)` should not change unless the [validator](#dfn-validator) registry changes. #### `get_beacon_proposer_index` @@ -987,9 +987,9 @@ def get_domain(fork_data: ForkData, ) * 2**32 + domain_type ``` -#### `SSZTreeHash` +#### `hash_tree_root` -`SSZTreeHash` is a function for hashing objects into a single root utilizing a hash tree structure. `SSZTreeHash` is defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#tree-hash). +`hash_tree_root` 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). #### `verify_casper_votes` @@ -1000,7 +1000,7 @@ def verify_casper_votes(state: BeaconState, votes: SlashableVoteData) -> bool: pubs = [aggregate_pubkey([state.validators[i].pubkey for i in votes.aggregate_signature_poc_0_indices]), aggregate_pubkey([state.validators[i].pubkey for i in votes.aggregate_signature_poc_1_indices])] - return BLSMultiVerify(pubkeys=pubs, msgs=[SSZTreeHash(votes)+bytes1(0), SSZTreeHash(votes)+bytes1(1), sig=aggregate_signature) + return BLSMultiVerify(pubkeys=pubs, msgs=[hash_tree_root(votes)+bytes1(0), hash_tree_root(votes)+bytes1(1), sig=aggregate_signature) ``` #### `integer_squareroot` @@ -1292,9 +1292,9 @@ def exit_validator(state: BeaconState, Below are the processing steps that happen at every slot. * Let `latest_block` be the latest `BeaconBlock` that was processed in the chain. -* Let `latest_hash` be the `SSZTreeHash` of `latest_block`. +* Let `latest_hash` be the `hash_tree_root` of `latest_block`. * Set `state.slot += 1` -* Set `state.latest_block_hashes = state.latest_block_hashes + [latest_hash]`. (The output of `get_block_hash` should not change, except that it will no longer throw for `state.slot - 1`). +* Set `state.latest_block_hashes = state.latest_block_hashes + [latest_hash]`. (The output of `get_block_root` should not change, except that it will no longer throw for `state.slot - 1`). If there is a block from the proposer for `state.slot`, we process that incoming block: @@ -1309,8 +1309,8 @@ If there is no block from the proposer at state.slot: ### Proposer signature -* Let `block_hash_without_sig` be the `SSZTreeHash` of `block` where `block.signature` is set to `[0, 0]`. -* Let `proposal_hash = SSZTreeHash(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_hash_without_sig))`. +* Let `block_hash_without_sig` be the `hash_tree_root` of `block` where `block.signature` is set to `[0, 0]`. +* Let `proposal_hash = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_hash_without_sig))`. * Verify that `BLSVerify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, data=proposal_hash, sig=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. ### RANDAO @@ -1336,8 +1336,8 @@ 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 `BLSVerify(pubkey=proposer.pubkey, msg=SSZTreeHash(proposer_slashing.proposal_data_1), sig=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. -* Verify that `BLSVerify(pubkey=proposer.pubkey, msg=SSZTreeHash(proposer_slashing.proposal_data_2), sig=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. +* Verify that `BLSVerify(pubkey=proposer.pubkey, msg=hash_tree_root(proposer_slashing.proposal_data_1), sig=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. +* Verify that `BLSVerify(pubkey=proposer.pubkey, msg=hash_tree_root(proposer_slashing.proposal_data_2), sig=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * 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_hash != proposer_slashing.proposal_data_2.block_hash`. @@ -1368,12 +1368,12 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`. * Verify that `attestation.data.slot + EPOCH_LENGTH >= state.slot`. * Verify that `attestation.data.justified_slot` is equal to `state.justified_slot if attestation.data.slot >= state.slot - (state.slot % EPOCH_LENGTH) else state.previous_justified_slot`. -* Verify that `attestation.data.justified_block_hash` is equal to `get_block_hash(state, attestation.data.justified_slot)`. +* Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, attestation.data.justified_slot)`. * Verify that either `attestation.data.latest_crosslink_hash` or `attestation.data.shard_block_hash` equals `state.latest_crosslinks[shard].shard_block_hash`. * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.participation_bitfield)`. * Let `group_public_key = BLSAddPubkeys([state.validator_registry[v].pubkey for v in participants])`. - * Verify that `BLSVerify(pubkey=group_public_key, msg=SSZTreeHash(attestation.data) + bytes1(0), sig=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. + * Verify that `BLSVerify(pubkey=group_public_key, msg=hash_tree_root(attestation.data) + bytes1(0), sig=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. * [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_hash == ZERO_HASH`. * Append `PendingAttestationRecord(data=attestation.data, participation_bitfield=attestation.participation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`. @@ -1457,7 +1457,7 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the current epoch: * Let `this_epoch_attestations = [a for a in state.latest_attestations if state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]`. (Note: this is the set of attestations of slots in the epoch `state.slot-EPOCH_LENGTH...state.slot-1`, _not_ attestations that got included in the chain during the epoch `state.slot-EPOCH_LENGTH...state.slot-1`.) -* Let `this_epoch_boundary_attestations = [a for a in this_epoch_attestations if a.data.epoch_boundary_hash == get_block_hash(state, state.slot-EPOCH_LENGTH) and a.justified_slot == state.justified_slot]`. +* Let `this_epoch_boundary_attestations = [a for a in this_epoch_attestations if a.data.epoch_boundary_hash == get_block_root(state, state.slot-EPOCH_LENGTH) and a.justified_slot == state.justified_slot]`. * Let `this_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_boundary_attestations]`. * Let `this_epoch_boundary_attesters = [state.validator_registry[i] for indices in this_epoch_boundary_attester_indices for i in indices]`. * Let `this_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in this_epoch_boundary_attesters])`. @@ -1465,7 +1465,7 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]`. -* Let `previous_epoch_boundary_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.epoch_boundary_hash == get_block_hash(state, state.slot - 2 * EPOCH_LENGTH) and a.justified_slot == state.previous_justified_slot]`. +* Let `previous_epoch_boundary_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.epoch_boundary_hash == justified_block_root(state, state.slot - 2 * EPOCH_LENGTH) and a.justified_slot == state.previous_justified_slot]`. * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_boundary_attestations]`. * Let `previous_epoch_boundary_attesters = [state.validator_registry[i] for indices in previous_epoch_boundary_attester_indices for i in indices]`. * Let `previous_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_boundary_attesters])`. @@ -1660,7 +1660,7 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com ## State root processing -Verify `block.state_root == SSZTreeHash(state)` if there exists a `block` for the slot being processed. +Verify `block.state_root == hash_tree_root(state)` if there exists a `block` for the slot being processed. # Appendix ## Appendix A - Hash function diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 27b9d9ef1..5cdf64245 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -41,7 +41,7 @@ A `ShardBlock` object has the following fields: # What shard is it on 'shard_id': 'uint64', # Parent block hash - 'parent_hash': 'hash32', + 'parent_root': 'hash32', # Beacon chain block 'beacon_chain_ref': 'hash32', # Depth of the Merkle tree @@ -62,16 +62,16 @@ A `ShardBlock` object has the following fields: For a block on a shard to be processed by a node, the following conditions must be met: -* The `ShardBlock` pointed to by `parent_hash` has already been processed and accepted +* The `ShardBlock` pointed to by `parent_root` has already been processed and accepted * The signature for the block from the _proposer_ (see below for definition) of that block is included along with the block in the network message object To validate a block header on shard `shard_id`, compute as follows: -* Verify that `beacon_chain_ref` is the hash of a block in the beacon chain with slot less than or equal to `slot`. Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_hash`. +* Verify that `beacon_chain_ref` is the hash of a block in the beacon chain with slot less than or equal to `slot`. Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_root`. * Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. Let `validators` be `[validators[i] for i in state.current_persistent_committees[shard_id]]`. * Assert `len(attester_bitfield) == ceil_div8(len(validators))` * Let `proposer_index = hash(state.randao_mix + bytes8(shard_id) + bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. -* Generate the `group_public_key` by adding the public keys of all the validators for whom the corresponding position in the bitfield is set to 1. Verify that `BLSVerify(pub=group_public_key, msg=parent_hash, sig=block.aggregate_sig, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. +* Generate the `group_public_key` by adding the public keys of all the validators for whom the corresponding position in the bitfield is set to 1. Verify that `BLSVerify(pub=group_public_key, msg=parent_root, sig=block.aggregate_sig, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. ### Block Merklization helper diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 46f73c045..66c691722 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -390,7 +390,7 @@ return typ(**values), item_index ### Tree Hash -The below `SSZTreeHash` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For the final output only (ie. not intermediate outputs), if the output is less than 32 bytes, right-zero-pad it to 32 bytes. The goal is collision resistance *within* each type, not between types. +The below `hash_tree_root` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For the final output only (ie. not intermediate outputs), if the output is less than 32 bytes, right-zero-pad it to 32 bytes. The goal is collision resistance *within* each type, not between types. Refer to [Appendix A](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#appendix-a---hash-function) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. @@ -435,13 +435,13 @@ def merkle_hash(lst): return hash(chunkz[0] + datalen) ``` -To `SSZTreeHash` a list, we simply do: +To `hash_tree_root` a list, we simply do: ```python -return merkle_hash([SSZTreeHash(item) for item in value]) +return merkle_hash([hash_tree_root(item) for item in value]) ``` -Where the inner `SSZTreeHash` is a recursive application of the tree-hashing function (returning less than 32 bytes for short single values). +Where the inner `hash_tree_root` is a recursive application of the tree-hashing function (returning less than 32 bytes for short single values). #### Container @@ -449,7 +449,7 @@ Where the inner `SSZTreeHash` is a recursive application of the tree-hashing fun Recursively tree hash the values in the container in order sorted by key, and return the hash of the concatenation of the results. ```python -return hash(b''.join([SSZTreeHash(getattr(x, field)) for field in sorted(value.fields))) +return hash(b''.join([hash_tree_root(getattr(x, field)) for field in sorted(value.fields))) ``` From 42364fe7b1a3d06c05e1778f31a60d9605c2cb35 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 Dec 2018 13:22:17 -0600 Subject: [PATCH 02/13] add granular reward for the four different parts of attestation --- specs/core/0_beacon-chain.md | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 06e9c2a56..f1447d70d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1453,6 +1453,7 @@ All [validators](#dfn-validator): * Let `total_balance = sum([get_effective_balance(v) for v in active_validators])`. * Let `base_reward_quotient = BASE_REWARD_QUOTIENT * integer_squareroot(total_balance // GWEI_PER_ETH)`. * Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient` for any validator `v`. +* Let `base_inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the current epoch: @@ -1465,11 +1466,25 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]`. -* Let `previous_epoch_boundary_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.epoch_boundary_hash == get_block_hash(state, state.slot - 2 * EPOCH_LENGTH) and a.justified_slot == state.previous_justified_slot]`. +* Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_attestations]`. +* Let `previous_epoch_attesters = [state.validator_registry[i] for indices in previous_epoch_attester_indices for i in indices]`. + +* Let `previous_epoch_justified_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.justified_slot == state.previous_justified_slot]`. +* Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_justified_attestations]`. +* Let `previous_epoch_justified_attesters = [state.validator_registry[i] for indices in previous_epoch_justified_attester_indices for i in indices]`. +* Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_justified_attesters])`. + +* Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_attestations if a.epoch_boundary_hash == get_block_hash(state, state.slot - 2 * EPOCH_LENGTH)]`. * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_boundary_attestations]`. * Let `previous_epoch_boundary_attesters = [state.validator_registry[i] for indices in previous_epoch_boundary_attester_indices for i in indices]`. * Let `previous_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_boundary_attesters])`. +* Let `previous_epoch_head_attestations = [a for a in previous_epoch_attestations if a.beacon_block_hash == get_block_hash(state, a.slot)]`. +* Let `previous_epoch_head_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_head_attestations]`. +* Let `previous_epoch_head_attesters = [state.validator_registry[i] for indices in previous_epoch_head_attester_indices for i in indices]`. +* Let `previous_epoch_head_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_head_attesters])`. + + For every `shard_committee` in `state.shard_committees_at_slots`: * Let `attesting_validators(shard_committee, shard_block_hash)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_attestations + previous_epoch_attestations if a.shard == shard_committee.shard and a.shard_block_hash == shard_block_hash]`. @@ -1526,15 +1541,24 @@ Note: When applying penalties in the following balance recalculations implemente Case 1: `slots_since_finality <= 4 * EPOCH_LENGTH`: -* Any [validator](#dfn-validator) `v` in `previous_epoch_boundary_attesters` gains `adjust_for_inclusion_distance(base_reward(v) * previous_epoch_boundary_attesting_balance // total_balance, inclusion_distance(v))`. -* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_boundary_attesters` loses `base_reward(v)`. +* Expected FFG source: + * Any [validator](#dfn-validator) `v` in `previous_epoch_justified_attesters` gains `adjust_for_inclusion_distance(base_reward(v) * previous_epoch_justified_attesting_balance // total_balance, inclusion_distance(v))`. + * Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_justified_attesters` loses `base_reward(v)`. +* Expected FFG target: + * Any [validator](#dfn-validator) `v` in `previous_epoch_boundary_attesters` gains `adjust_for_inclusion_distance(base_reward(v) * previous_epoch_boundary_attesting_balance // total_balance, inclusion_distance(v))`. + * Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_boundary_attesters` loses `base_reward(v)`. +* Expected beacon chain head: + * Any [validator](#dfn-validator) `v` in `previous_epoch_head_attesters` gains `adjust_for_inclusion_distance(base_reward(v) * previous_epoch_head_attesting_balance // total_balance, inclusion_distance(v))`. + * Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_head_attesters` loses `base_reward(v)`. Case 2: `slots_since_finality > 4 * EPOCH_LENGTH`: -* Any [validator](#dfn-validator) in `previous_epoch_boundary_attesters` sees their balance unchanged. -* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_boundary_attesters`, and any [validator](#dfn-validator) with `status == EXITED_WITH_PENALTY`, loses `base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT`. +* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_justified_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. +* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_boundary_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. +* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_head_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. +* Any [validator](#dfn-validator) with `status == EXITED_WITH_PENALTY`, loses `3 * base_inactivity_penalty(v, slots_since_finality)`. -For each `v` in `previous_epoch_boundary_attesters`, we determine the proposer `proposer_index = get_beacon_proposer_index(state, inclusion_slot(v))` and set `state.validator_registry[proposer_index].balance += base_reward(v) // INCLUDER_REWARD_QUOTIENT`. +For each `v` in `previous_epoch_attesters`, we determine the proposer `proposer_index = get_beacon_proposer_index(state, inclusion_slot(v))` and set `state.validator_registry[proposer_index].balance += base_reward(v) // INCLUDER_REWARD_QUOTIENT`. ### Crosslink rewards and penalties From 5e84a53ff1f21b1de51925b98b871f219b5aad99 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 Dec 2018 13:32:27 -0600 Subject: [PATCH 03/13] base_reward is 1/4 of total reward val can earn per cycle --- 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 f1447d70d..6311e28d0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -191,7 +191,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | Name | Value | | - | - | -| `BASE_REWARD_QUOTIENT` | `2**11` (= 2,048) | +| `BASE_REWARD_QUOTIENT` | `2**10` (= 1,024) | | `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) | | `INCLUDER_REWARD_QUOTIENT` | `2**3` (= 8) | | `INACTIVITY_PENALTY_QUOTIENT` | `2**34` (= 17,179,869,184) | @@ -1452,7 +1452,7 @@ All [validators](#dfn-validator): * Let `active_validators = [state.validator_registry[i] for i in get_active_validator_indices(state.validator_registry)]`. * Let `total_balance = sum([get_effective_balance(v) for v in active_validators])`. * Let `base_reward_quotient = BASE_REWARD_QUOTIENT * integer_squareroot(total_balance // GWEI_PER_ETH)`. -* Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient` for any validator `v`. +* Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient // 4` for any validator `v`. * Let `base_inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the current epoch: From 032007ad17f6cca2d42f282b2785c7e74b49713e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 Dec 2018 13:40:00 -0600 Subject: [PATCH 04/13] add organization to previous epoch attester helpers --- specs/core/0_beacon-chain.md | 50 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6311e28d0..4944af31b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1455,34 +1455,36 @@ All [validators](#dfn-validator): * Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient // 4` for any validator `v`. * Let `base_inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. -[Validators](#dfn-Validator) justifying the epoch boundary block at the start of the current epoch: +[Validators](#dfn-Validator) validators attesting during the current epoch: * Let `this_epoch_attestations = [a for a in state.latest_attestations if state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]`. (Note: this is the set of attestations of slots in the epoch `state.slot-EPOCH_LENGTH...state.slot-1`, _not_ attestations that got included in the chain during the epoch `state.slot-EPOCH_LENGTH...state.slot-1`.) -* Let `this_epoch_boundary_attestations = [a for a in this_epoch_attestations if a.data.epoch_boundary_hash == get_block_hash(state, state.slot-EPOCH_LENGTH) and a.justified_slot == state.justified_slot]`. -* Let `this_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_boundary_attestations]`. -* Let `this_epoch_boundary_attesters = [state.validator_registry[i] for indices in this_epoch_boundary_attester_indices for i in indices]`. -* Let `this_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in this_epoch_boundary_attesters])`. +* Validators justifying the epoch boundary block at the start of the current epoch: + * Let `this_epoch_boundary_attestations = [a for a in this_epoch_attestations if a.data.epoch_boundary_hash == get_block_hash(state, state.slot-EPOCH_LENGTH) and a.justified_slot == state.justified_slot]`. + * Let `this_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_boundary_attestations]`. + * Let `this_epoch_boundary_attesters = [state.validator_registry[i] for indices in this_epoch_boundary_attester_indices for i in indices]`. + * Let `this_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in this_epoch_boundary_attesters])`. -[Validators](#dfn-Validator) justifying the epoch boundary block at the start of the previous epoch: +[Validators](#dfn-Validator) attesting during the previous epoch: -* Let `previous_epoch_attestations = [a for a in state.latest_attestations if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]`. -* Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_attestations]`. -* Let `previous_epoch_attesters = [state.validator_registry[i] for indices in previous_epoch_attester_indices for i in indices]`. - -* Let `previous_epoch_justified_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.justified_slot == state.previous_justified_slot]`. -* Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_justified_attestations]`. -* Let `previous_epoch_justified_attesters = [state.validator_registry[i] for indices in previous_epoch_justified_attester_indices for i in indices]`. -* Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_justified_attesters])`. - -* Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_attestations if a.epoch_boundary_hash == get_block_hash(state, state.slot - 2 * EPOCH_LENGTH)]`. -* Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_boundary_attestations]`. -* Let `previous_epoch_boundary_attesters = [state.validator_registry[i] for indices in previous_epoch_boundary_attester_indices for i in indices]`. -* Let `previous_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_boundary_attesters])`. - -* Let `previous_epoch_head_attestations = [a for a in previous_epoch_attestations if a.beacon_block_hash == get_block_hash(state, a.slot)]`. -* Let `previous_epoch_head_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_head_attestations]`. -* Let `previous_epoch_head_attesters = [state.validator_registry[i] for indices in previous_epoch_head_attester_indices for i in indices]`. -* Let `previous_epoch_head_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_head_attesters])`. +* Validators that made an attestation during the previous epoch: + * Let `previous_epoch_attestations = [a for a in state.latest_attestations if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]`. + * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_attestations]`. + * Let `previous_epoch_attesters = [state.validator_registry[i] for indices in previous_epoch_attester_indices for i in indices]`. +* Validators targeting the previous justified hash: + * Let `previous_epoch_justified_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.justified_slot == state.previous_justified_slot]`. + * Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_justified_attestations]`. + * Let `previous_epoch_justified_attesters = [state.validator_registry[i] for indices in previous_epoch_justified_attester_indices for i in indices]`. + * Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_justified_attesters])`. +* Validators justifying the epoch boundary block at the start of the previous epoch: + * Let `previous_epoch_boundary_attestations = [a for a in previous_epoch_justified_attestations if a.epoch_boundary_hash == get_block_hash(state, state.slot - 2 * EPOCH_LENGTH)]`. + * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_boundary_attestations]`. + * Let `previous_epoch_boundary_attesters = [state.validator_registry[i] for indices in previous_epoch_boundary_attester_indices for i in indices]`. + * Let `previous_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_boundary_attesters])`. +* Validators attesting to the expected beacon chain head during the previous epoch: + * Let `previous_epoch_head_attestations = [a for a in previous_epoch_attestations if a.beacon_block_hash == get_block_hash(state, a.slot)]`. + * Let `previous_epoch_head_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_head_attestations]`. + * Let `previous_epoch_head_attesters = [state.validator_registry[i] for indices in previous_epoch_head_attester_indices for i in indices]`. + * Let `previous_epoch_head_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_head_attesters])`. For every `shard_committee` in `state.shard_committees_at_slots`: From 9f8ae361f9e33ca1f40bba08cba61a36221ae767 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 11 Dec 2018 20:30:28 +0000 Subject: [PATCH 05/13] Rename `BLSVerify` to `bls_verify` and put `hash` in helpers --- specs/core/0_beacon-chain.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 06e9c2a56..96e066d7d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -57,6 +57,7 @@ - [Beacon chain fork choice rule](#beacon-chain-fork-choice-rule) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Helper functions](#helper-functions) + - [`hash`](#hash) - [`is_active_validator`](#is_active_validator) - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) @@ -76,7 +77,7 @@ - [`SSZTreeHash`](#ssztreehash) - [`verify_casper_votes`](#verify_casper_votes) - [`integer_squareroot`](#integer_squareroot) - - [`BLSVerify`](#blsverify) + - [`bls_verify`](#blsverify) - [On startup](#on-startup) - [Routine for processing deposits](#routine-for-processing-deposits) - [Routine for updating validator status](#routine-for-updating-validator-status) @@ -103,8 +104,6 @@ - [Proposer reshuffling](#proposer-reshuffling) - [Final updates](#final-updates) - [State root processing](#state-root-processing) -- [Appendix](#appendix) - - [Appendix A - Hash function](#appendix-a---hash-function) - [References](#references) - [Normative](#normative) - [Informative](#informative) @@ -708,6 +707,12 @@ The per-slot transitions generally focus on verifying aggregate signatures and s Note: The definitions below are for specification purposes and are not necessarily optimal implementations. +#### `hash` + +The hash function is denoted by `hash`. In Phase 0 the beacon chain is deployed with the same hash function as Ethereum 1.0, i.e. Keccak-256 (also incorrectly known as SHA3). + +Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethereum 2.0 deployment phase. + #### `is_active_validator` ```python def is_active_validator(validator: ValidatorRecord) -> bool: @@ -1000,7 +1005,7 @@ def verify_casper_votes(state: BeaconState, votes: SlashableVoteData) -> bool: pubs = [aggregate_pubkey([state.validators[i].pubkey for i in votes.aggregate_signature_poc_0_indices]), aggregate_pubkey([state.validators[i].pubkey for i in votes.aggregate_signature_poc_1_indices])] - return BLSMultiVerify(pubkeys=pubs, msgs=[SSZTreeHash(votes)+bytes1(0), SSZTreeHash(votes)+bytes1(1), sig=aggregate_signature) + return bls_verify_multiple(pubkeys=pubs, msgs=[SSZTreeHash(votes)+bytes1(0), SSZTreeHash(votes)+bytes1(1), sig=aggregate_signature) ``` #### `integer_squareroot` @@ -1018,9 +1023,9 @@ def integer_squareroot(n: int) -> int: return x ``` -#### `BLSVerify` +#### `bls_verify` -`BLSVerify` is a function for verifying a BLS12-381 signature, defined in the [BLS Verification spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_verify.md). +`bls_verify` is a function for verifying a BLS12-381 signature, defined in the [BLS Verification spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_verify.md). ### On startup @@ -1142,7 +1147,7 @@ def process_deposit(state: BeaconState, Process a deposit from Ethereum 1.0. Note that this function mutates ``state``. """ - assert BLSVerify( + assert bls_verify( pub=pubkey, msg=hash(bytes32(pubkey) + withdrawal_credentials + randao_commitment), sig=proof_of_possession, @@ -1311,7 +1316,7 @@ If there is no block from the proposer at state.slot: * Let `block_hash_without_sig` be the `SSZTreeHash` of `block` where `block.signature` is set to `[0, 0]`. * Let `proposal_hash = SSZTreeHash(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_hash_without_sig))`. -* Verify that `BLSVerify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, data=proposal_hash, sig=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, data=proposal_hash, sig=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. ### RANDAO @@ -1336,8 +1341,8 @@ 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 `BLSVerify(pubkey=proposer.pubkey, msg=SSZTreeHash(proposer_slashing.proposal_data_1), sig=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. -* Verify that `BLSVerify(pubkey=proposer.pubkey, msg=SSZTreeHash(proposer_slashing.proposal_data_2), sig=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, msg=SSZTreeHash(proposer_slashing.proposal_data_1), sig=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, msg=SSZTreeHash(proposer_slashing.proposal_data_2), sig=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * 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_hash != proposer_slashing.proposal_data_2.block_hash`. @@ -1373,7 +1378,7 @@ For each `attestation` in `block.body.attestations`: * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.participation_bitfield)`. * Let `group_public_key = BLSAddPubkeys([state.validator_registry[v].pubkey for v in participants])`. - * Verify that `BLSVerify(pubkey=group_public_key, msg=SSZTreeHash(attestation.data) + bytes1(0), sig=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. + * Verify that `bls_verify(pubkey=group_public_key, msg=SSZTreeHash(attestation.data) + bytes1(0), sig=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. * [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_hash == ZERO_HASH`. * Append `PendingAttestationRecord(data=attestation.data, participation_bitfield=attestation.participation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`. @@ -1420,7 +1425,7 @@ Verify that `len(block.body.exits) <= MAX_EXITS`. For each `exit` in `block.body.exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `BLSVerify(pubkey=validator.pubkey, msg=ZERO_HASH, sig=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. +* Verify that `bls_verify(pubkey=validator.pubkey, msg=ZERO_HASH, sig=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. * Verify that `validator.status == ACTIVE`. * Verify that `state.slot >= exit.slot`. * Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`. @@ -1662,11 +1667,6 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com Verify `block.state_root == SSZTreeHash(state)` if there exists a `block` for the slot being processed. -# Appendix -## Appendix A - Hash function - -In Phase 0 the beacon chain is deployed with the same hash function as Ethereum 1.0, i.e. Keccak-256 (also incorrectly known as SHA3). We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethereum 2.0 deployment phase. - # References This section is divided into Normative and Informative references. Normative references are those that must be read in order to implement this specification, while Informative references are merely that, information. An example of the former might be the details of a required consensus algorithm, and an example of the latter might be a pointer to research that demonstrates why a particular consensus algorithm might be better suited for inclusion in the standard than another. From a13997d738642f6e0090defaaa8dfb16563925c8 Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 11 Dec 2018 12:38:32 -0800 Subject: [PATCH 06/13] updated rest of the fields except ancestor_hashes --- specs/core/0_beacon-chain.md | 68 +++++++++++++++---------------- specs/core/1_shard-data-chains.md | 8 ++-- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index de105ee5d..97cf0ac97 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -311,14 +311,14 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'slot': 'uint64', # Shard number 'shard': 'uint64', - # Hash of the signed beacon block - 'beacon_block_hash': 'hash32', - # Hash of the ancestor at the epoch boundary - 'epoch_boundary_hash': 'hash32', - # Shard block hash being attested to - 'shard_block_hash': 'hash32', - # Last crosslink hash - 'latest_crosslink_hash': 'hash32', + # Hash of root of the signed beacon block + 'beacon_block_root': 'hash32', + # Hash of root of the ancestor at the epoch boundary + 'epoch_boundary_root': 'hash32', + # Shard block's hash of root + 'shard_block_root': 'hash32', + # Last crosslink's hash of root + 'latest_crosslink_root': 'hash32', # Slot of the last justified beacon block 'justified_slot': 'uint64', # Hash of the last justified beacon block @@ -419,8 +419,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'slot': 'uint64', # Shard number (`BEACON_CHAIN_SHARD_NUMBER` for beacon chain) 'shard': 'uint64', - # Block hash - 'block_hash': 'hash32', + # Block's hash of root + 'block_root': 'hash32', } ``` @@ -456,7 +456,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # Recent state 'latest_crosslinks': [CrosslinkRecord], - 'latest_block_hashes': ['hash32'], # Needed to process attestations, older to newer + 'latest_block_roots': ['hash32'], # Needed to process attestations, older to newer 'latest_penalized_exit_balances': ['uint64'], # Balances penalized at every withdrawal period 'latest_attestations': [PendingAttestationRecord], @@ -496,7 +496,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # Slot number 'slot': 'uint64', # Shard block hash - 'shard_block_hash': 'hash32', + 'shard_block_root': 'hash32', } ``` @@ -877,9 +877,9 @@ def get_block_root(state: BeaconState, """ Returns the block hash at a recent ``slot``. """ - earliest_slot_in_array = state.slot - len(state.latest_block_hashes) + earliest_slot_in_array = state.slot - len(state.latest_block_roots) assert earliest_slot_in_array <= slot < state.slot - return state.latest_block_hashes[slot - earliest_slot_in_array] + return state.latest_block_roots[slot - earliest_slot_in_array] ``` `get_block_root(_, s)` should always return `hash_tree_root` of the block in the beacon chain at slot `s`, and `get_shard_committees_at_slot(_, s)` should not change unless the [validator](#dfn-validator) registry changes. @@ -898,13 +898,13 @@ def get_beacon_proposer_index(state: BeaconState, #### `get_updated_ancestor_hashes` -```python +```pythonproposal_root def get_updated_ancestor_hashes(latest_block: BeaconBlock, - latest_hash: Hash32) -> List[Hash32]: + latest_root: Hash32) -> List[Hash32]: new_ancestor_hashes = copy.deepcopy(latest_block.ancestor_hashes) for i in range(32): if latest_block.slot % 2**i == 0: - new_ancestor_hashes[i] = latest_hash + new_ancestor_hashes[i] = latest_root return new_ancestor_hashes ``` @@ -1082,8 +1082,8 @@ def on_startup(initial_validator_deposits: List[Deposit], finalized_slot=INITIAL_SLOT_NUMBER, # Recent state - latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_hash=ZERO_HASH) for _ in range(SHARD_COUNT)], - latest_block_hashes=[ZERO_HASH for _ in range(EPOCH_LENGTH * 2)], + latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_block_roots=[ZERO_HASH for _ in range(EPOCH_LENGTH * 2)], latest_penalized_exit_balances=[], latest_attestations=[], @@ -1292,15 +1292,15 @@ def exit_validator(state: BeaconState, Below are the processing steps that happen at every slot. * Let `latest_block` be the latest `BeaconBlock` that was processed in the chain. -* Let `latest_hash` be the `hash_tree_root` of `latest_block`. +* Let `latest_root` be the `hash_tree_root` of `latest_block`. * Set `state.slot += 1` -* Set `state.latest_block_hashes = state.latest_block_hashes + [latest_hash]`. (The output of `get_block_root` should not change, except that it will no longer throw for `state.slot - 1`). +* Set `state.latest_block_roots = state.latest_block_roots + [latest_root]`. (The output of `get_block_root` should not change, except that it will no longer throw for `state.slot - 1`). If there is a block from the proposer for `state.slot`, we process that incoming block: * Let `block` be that associated incoming block. * Verify that `block.slot == state.slot` -* Verify that `block.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_hash)`. +* Verify that `block.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_root)`. If there is no block from the proposer at state.slot: @@ -1309,9 +1309,9 @@ If there is no block from the proposer at state.slot: ### Proposer signature -* Let `block_hash_without_sig` be the `hash_tree_root` of `block` where `block.signature` is set to `[0, 0]`. -* Let `proposal_hash = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_hash_without_sig))`. -* Verify that `BLSVerify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, data=proposal_hash, sig=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. +* Let `block_root_without_sig` be the `hash_tree_root` of `block` where `block.signature` is set to `[0, 0]`. +* Let `proposal_root = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_root_without_sig))`. +* Verify that `BLSVerify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, data=proposal_root, sig=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. ### RANDAO @@ -1340,7 +1340,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `BLSVerify(pubkey=proposer.pubkey, msg=hash_tree_root(proposer_slashing.proposal_data_2), sig=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * 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_hash != proposer_slashing.proposal_data_2.block_hash`. +* Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. * Verify that `proposer.status != EXITED_WITH_PENALTY`. * Run `update_validator_status(state, proposer_slashing.proposer_index, new_status=EXITED_WITH_PENALTY)`. @@ -1369,12 +1369,12 @@ For each `attestation` in `block.body.attestations`: * Verify that `attestation.data.slot + EPOCH_LENGTH >= state.slot`. * Verify that `attestation.data.justified_slot` is equal to `state.justified_slot if attestation.data.slot >= state.slot - (state.slot % EPOCH_LENGTH) else state.previous_justified_slot`. * Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state, attestation.data.justified_slot)`. -* Verify that either `attestation.data.latest_crosslink_hash` or `attestation.data.shard_block_hash` equals `state.latest_crosslinks[shard].shard_block_hash`. +* Verify that either `attestation.data.latest_crosslink_root` or `attestation.data.shard_block_root` equals `state.latest_crosslinks[shard].shard_block_root`. * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.participation_bitfield)`. * Let `group_public_key = BLSAddPubkeys([state.validator_registry[v].pubkey for v in participants])`. * Verify that `BLSVerify(pubkey=group_public_key, msg=hash_tree_root(attestation.data) + bytes1(0), sig=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. -* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_hash == ZERO_HASH`. +* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`. * Append `PendingAttestationRecord(data=attestation.data, participation_bitfield=attestation.participation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`. #### Deposits @@ -1457,7 +1457,7 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the current epoch: * Let `this_epoch_attestations = [a for a in state.latest_attestations if state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]`. (Note: this is the set of attestations of slots in the epoch `state.slot-EPOCH_LENGTH...state.slot-1`, _not_ attestations that got included in the chain during the epoch `state.slot-EPOCH_LENGTH...state.slot-1`.) -* Let `this_epoch_boundary_attestations = [a for a in this_epoch_attestations if a.data.epoch_boundary_hash == get_block_root(state, state.slot-EPOCH_LENGTH) and a.justified_slot == state.justified_slot]`. +* Let `this_epoch_boundary_attestations = [a for a in this_epoch_attestations if a.data.epoch_boundary_root == get_block_root(state, state.slot-EPOCH_LENGTH) and a.justified_slot == state.justified_slot]`. * Let `this_epoch_boundary_attester_indices` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_boundary_attestations]`. * Let `this_epoch_boundary_attesters = [state.validator_registry[i] for indices in this_epoch_boundary_attester_indices for i in indices]`. * Let `this_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in this_epoch_boundary_attesters])`. @@ -1465,15 +1465,15 @@ All [validators](#dfn-validator): [Validators](#dfn-Validator) justifying the epoch boundary block at the start of the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]`. -* Let `previous_epoch_boundary_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.epoch_boundary_hash == justified_block_root(state, state.slot - 2 * EPOCH_LENGTH) and a.justified_slot == state.previous_justified_slot]`. +* Let `previous_epoch_boundary_attestations = [a for a in this_epoch_attestations + previous_epoch_attestations if a.epoch_boundary_root == justified_block_root(state, state.slot - 2 * EPOCH_LENGTH) and a.justified_slot == state.previous_justified_slot]`. * Let `previous_epoch_boundary_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_boundary_attestations]`. * Let `previous_epoch_boundary_attesters = [state.validator_registry[i] for indices in previous_epoch_boundary_attester_indices for i in indices]`. * Let `previous_epoch_boundary_attesting_balance = sum([get_effective_balance(v) for v in previous_epoch_boundary_attesters])`. For every `shard_committee` in `state.shard_committees_at_slots`: -* Let `attesting_validators(shard_committee, shard_block_hash)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_attestations + previous_epoch_attestations if a.shard == shard_committee.shard and a.shard_block_hash == shard_block_hash]`. -* Let `winning_hash(shard_committee)` be equal to the value of `shard_block_hash` such that `sum([get_effective_balance(v) for v in attesting_validators(shard_committee, shard_block_hash)])` is maximized (ties broken by favoring lower `shard_block_hash` values). +* Let `attesting_validators(shard_committee, shard_block_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_attestations + previous_epoch_attestations if a.shard == shard_committee.shard and a.shard_block_root == shard_block_root]`. +* Let `winning_hash(shard_committee)` be equal to the value of `shard_block_root` such that `sum([get_effective_balance(v) for v in attesting_validators(shard_committee, shard_block_root)])` is maximized (ties broken by favoring lower `shard_block_root` values). * Let `attesting_validators(shard_committee)` be equal to `attesting_validators(shard_committee, winning_hash(shard_committee))` for convenience. * Let `total_attesting_balance(shard_committee)` be the sum of the balances-at-stake of `attesting_validators(shard_committee)`. * Let `total_balance(shard_committee) = sum([get_effective_balance(v) for v in shard_committee.committee])`. @@ -1516,7 +1516,7 @@ Set `state.finalized_slot = state.previous_justified_slot` if any of the followi For every `shard_committee` in `state.shard_committees_at_slots`: -* Set `state.latest_crosslinks[shard] = CrosslinkRecord(slot=state.slot, shard_block_hash=winning_hash(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. +* Set `state.latest_crosslinks[shard] = CrosslinkRecord(slot=state.slot, block_root=winning_hash(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. ### Justification and finalization rewards and penalties @@ -1656,7 +1656,7 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com ### Final updates * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. -* Set `state.latest_block_hashes = state.latest_block_hashes[EPOCH_LENGTH:]`. +* Set `state.latest_block_roots = state.latest_block_roots[EPOCH_LENGTH:]`. ## State root processing diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 5cdf64245..9cb2facc5 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -40,7 +40,7 @@ A `ShardBlock` object has the following fields: 'slot': 'uint64', # What shard is it on 'shard_id': 'uint64', - # Parent block hash + # Parent block's hash of root 'parent_root': 'hash32', # Beacon chain block 'beacon_chain_ref': 'hash32', @@ -96,9 +96,9 @@ At network layer, we expect a shard block header to be broadcast along with its A node should sign a crosslink only if the following conditions hold. **If a node has the capability to perform the required level of verification, it should NOT follow chains on which a crosslink for which these conditions do NOT hold has been included, or a sufficient number of signatures have been included that during the next state recalculation, a crosslink will be registered.** -First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_hash` for the same shard (unless `last_crosslink_hash` equals zero, in which case we are at the genesis). +First, the conditions must recursively apply to the crosslink referenced in `last_crosslink_root` for the same shard (unless `last_crosslink_root` equals zero, in which case we are at the genesis). -Second, we verify the `shard_block_combined_data_root`. Let `h` be the slot _immediately after_ the slot of the shard block included by the last crosslink, and `h+n-1` be the slot number of the block directly referenced by the current `shard_block_hash`. Let `B[i]` be the block at slot `h+i` in the shard chain. Let `bodies[0] .... bodies[n-1]` be the bodies of these blocks and `roots[0] ... roots[n-1]` the data roots. If there is a missing slot in the shard chain at position `h+i`, then `bodies[i] == b'\x00' * shard_block_maxbytes(state[i])` and `roots[i]` be the Merkle root of the empty data. Define `compute_merkle_root` be a simple Merkle root calculating function that takes as input a list of objects, where the list's length must be an exact power of two. We define the function for computing the combined data root as follows: +Second, we verify the `shard_block_combined_data_root`. Let `h` be the slot _immediately after_ the slot of the shard block included by the last crosslink, and `h+n-1` be the slot number of the block directly referenced by the current `shard_block_root`. Let `B[i]` be the block at slot `h+i` in the shard chain. Let `bodies[0] .... bodies[n-1]` be the bodies of these blocks and `roots[0] ... roots[n-1]` the data roots. If there is a missing slot in the shard chain at position `h+i`, then `bodies[i] == b'\x00' * shard_block_maxbytes(state[i])` and `roots[i]` be the Merkle root of the empty data. Define `compute_merkle_root` be a simple Merkle root calculating function that takes as input a list of objects, where the list's length must be an exact power of two. We define the function for computing the combined data root as follows: ```python ZERO_ROOT = merkle_root(bytes([0] * SHARD_BLOCK_SIZE)) @@ -121,4 +121,4 @@ Verify that the `shard_block_combined_data_root` is the output of these function ### Shard block fork choice rule -The fork choice rule for any shard is LMD GHOST using the validators 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_hash`). 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 validators 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). From 3f31eecb18fa2bc80c3632b482d73e6f04e992fd Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 11 Dec 2018 12:50:20 -0800 Subject: [PATCH 07/13] fixed typo & s/winning_hash/winning_root/ --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 97cf0ac97..6e70dfa9e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -898,7 +898,7 @@ def get_beacon_proposer_index(state: BeaconState, #### `get_updated_ancestor_hashes` -```pythonproposal_root +```python def get_updated_ancestor_hashes(latest_block: BeaconBlock, latest_root: Hash32) -> List[Hash32]: new_ancestor_hashes = copy.deepcopy(latest_block.ancestor_hashes) @@ -1473,8 +1473,8 @@ All [validators](#dfn-validator): For every `shard_committee` in `state.shard_committees_at_slots`: * Let `attesting_validators(shard_committee, shard_block_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in this_epoch_attestations + previous_epoch_attestations if a.shard == shard_committee.shard and a.shard_block_root == shard_block_root]`. -* Let `winning_hash(shard_committee)` be equal to the value of `shard_block_root` such that `sum([get_effective_balance(v) for v in attesting_validators(shard_committee, shard_block_root)])` is maximized (ties broken by favoring lower `shard_block_root` values). -* Let `attesting_validators(shard_committee)` be equal to `attesting_validators(shard_committee, winning_hash(shard_committee))` for convenience. +* Let `winning_root(shard_committee)` be equal to the value of `shard_block_root` such that `sum([get_effective_balance(v) for v in attesting_validators(shard_committee, shard_block_root)])` is maximized (ties broken by favoring lower `shard_block_root` values). +* Let `attesting_validators(shard_committee)` be equal to `attesting_validators(shard_committee, winning_root(shard_committee))` for convenience. * Let `total_attesting_balance(shard_committee)` be the sum of the balances-at-stake of `attesting_validators(shard_committee)`. * Let `total_balance(shard_committee) = sum([get_effective_balance(v) for v in shard_committee.committee])`. * Let `inclusion_slot(v) = a.slot_included` for the attestation `a` where `v` is in `get_attestation_participants(state, a.data, a.participation_bitfield)`. @@ -1516,7 +1516,7 @@ Set `state.finalized_slot = state.previous_justified_slot` if any of the followi For every `shard_committee` in `state.shard_committees_at_slots`: -* Set `state.latest_crosslinks[shard] = CrosslinkRecord(slot=state.slot, block_root=winning_hash(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. +* Set `state.latest_crosslinks[shard] = CrosslinkRecord(slot=state.slot, block_root=winning_root(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. ### Justification and finalization rewards and penalties From e2714b3344af95fe3c6fe42da6aac61c02172ee4 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 11 Dec 2018 15:18:30 -0600 Subject: [PATCH 08/13] add bls_verify_multiple def to phase 0 spec (#292) --- specs/core/0_beacon-chain.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 21e283c6b..9bdf6a924 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -78,6 +78,7 @@ - [`verify_casper_votes`](#verify_casper_votes) - [`integer_squareroot`](#integer_squareroot) - [`bls_verify`](#blsverify) + - [`bls_verify_multiple`](#blsverifymultiple) - [On startup](#on-startup) - [Routine for processing deposits](#routine-for-processing-deposits) - [Routine for updating validator status](#routine-for-updating-validator-status) @@ -1025,7 +1026,11 @@ def integer_squareroot(n: int) -> int: #### `bls_verify` -`bls_verify` is a function for verifying a BLS12-381 signature, defined in the [BLS Verification spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_verify.md). +`bls_verify` is a function for verifying a BLS12-381 signature, defined in the [BLS Verification spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_verify.md#bls_verify). + +#### `bls_verify_multiple` + +`bls_verify_multiple` is a function for verifying a BLS12-381 signature constructed from multiple messages, defined in the [BLS Verification spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_verify.md#bls_verify_multiple). ### On startup From 21da07b4f73a6de011e5c6f628294da09296a0de Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Wed, 12 Dec 2018 18:30:58 +0800 Subject: [PATCH 09/13] typo (#294) --- 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 9bdf6a924..3c7772ccb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -370,7 +370,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted ```python { # Minimum slot for processing exit - 'slot': 'unit64', + 'slot': 'uint64', # Index of the exiting validator 'validator_index': 'uint64', # Validator signature From 6b5479cac9a17253005afb5bedab4f3e90f365dd Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 12 Dec 2018 08:02:50 -0500 Subject: [PATCH 10/13] Moved ancestor hashes to state, changed to batched accumulator (#269) * Replaced ancestors hashes in blocks with the previous block root * Add a block root batched accumulator * Cleanup processing presentation --- specs/core/0_beacon-chain.md | 96 +++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3c7772ccb..ba4a13429 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -67,7 +67,7 @@ - [`get_shard_committees_at_slot`](#get_shard_committees_at_slot) - [`get_block_root`](#get_block_root) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - - [`get_updated_ancestor_hashes`](#get_updated_ancestor_hashes) + - [`merkle_root`](#merkle_root) - [`get_attestation_participants`](#get_attestation_participants) - [`bytes1`, `bytes2`, ...](#bytes1-bytes2-) - [`get_effective_balance`](#get_effective_balance) @@ -77,16 +77,20 @@ - [`hash_tree_root`](#hash_tree_root) - [`verify_casper_votes`](#verify_casper_votes) - [`integer_squareroot`](#integer_squareroot) - - [`bls_verify`](#blsverify) - - [`bls_verify_multiple`](#blsverifymultiple) + - [`bls_verify`](#bls_verify) + - [`bls_verify_multiple`](#bls_verify_multiple) - [On startup](#on-startup) - [Routine for processing deposits](#routine-for-processing-deposits) - [Routine for updating validator status](#routine-for-updating-validator-status) - [Per-slot processing](#per-slot-processing) + - [Misc counters](#misc-counters) + - [Block roots](#block-roots) + - [Per-block processing](#per-block-processing) + - [Slot](#slot) - [Proposer signature](#proposer-signature) - [RANDAO](#randao) - [PoW receipt root](#pow-receipt-root) - - [Block operations](#block-operations) + - [Operations](#operations) - [Proposer slashings](#proposer-slashings-1) - [Casper slashings](#casper-slashings-1) - [Attestations](#attestations-1) @@ -154,6 +158,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | | `BLS_WITHDRAWAL_PREFIX_BYTE` | `0x00` | - | | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | +| `LATEST_BLOCK_ROOTS_COUNT` | `2**13` (= 8,192) | block roots | * For the safety of crosslinks a minimum committee size of 111 is [recommended](https://vitalik.ca/files/Ithaca201807_Sharding.pdf). (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) The shuffling algorithm generally ensures (assuming sufficient validators) committee sizes at least `TARGET_COMMITTEE_SIZE // 2`. @@ -388,7 +393,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'slot': 'uint64', # Skip list of ancestor beacon block hashes # i'th item is the most recent ancestor whose slot is a multiple of 2**i for i = 0, ..., 31 - 'ancestor_hashes': ['hash32'], + 'parent_hash': 'hash32', 'state_root': 'hash32', 'randao_reveal': 'hash32', 'candidate_pow_receipt_root': 'hash32', @@ -459,6 +464,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'latest_block_roots': ['hash32'], # Needed to process attestations, older to newer 'latest_penalized_exit_balances': ['uint64'], # Balances penalized at every withdrawal period 'latest_attestations': [PendingAttestationRecord], + 'batched_block_roots': ['hash32'], # PoW receipt root 'processed_pow_receipt_root': 'hash32', @@ -477,7 +483,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # RANDAO commitment 'randao_commitment': 'hash32', # Slots the proposer has skipped (i.e. layers of RANDAO expected) - 'randao_skips': 'uint64', + 'randao_layers': 'uint64', # Balance in Gwei 'balance': 'uint64', # Status code @@ -655,7 +661,7 @@ Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Cli For a beacon chain block, `block`, to be processed by a node, the following conditions must be met: -* The parent block with `hash_tree_root` `block.ancestor_hashes[0]` has been processed and accepted. +* The parent block with root `block.parent_root` has been processed and accepted. * The node has processed its `state` up to slot, `block.slot - 1`. * The Ethereum 1.0 block pointed to by the `state.processed_pow_receipt_root` has been processed and accepted. * The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. @@ -902,16 +908,14 @@ def get_beacon_proposer_index(state: BeaconState, return first_committee[slot % len(first_committee)] ``` -#### `get_updated_ancestor_hashes` +#### `merkle_root` ```python -def get_updated_ancestor_hashes(latest_block: BeaconBlock, - latest_root: Hash32) -> List[Hash32]: - new_ancestor_hashes = copy.deepcopy(latest_block.ancestor_hashes) - for i in range(32): - if latest_block.slot % 2**i == 0: - new_ancestor_hashes[i] = latest_root - return new_ancestor_hashes +def merkle_root(values): + o = [0] * len(values) + values + for i in range(len(values)-1, 0, -1): + o[i] = hash(o[i*2] + o[i*2+1]) + return o[1] ``` #### `get_attestation_participants` @@ -1006,7 +1010,7 @@ def verify_casper_votes(state: BeaconState, votes: SlashableVoteData) -> bool: pubs = [aggregate_pubkey([state.validators[i].pubkey for i in votes.aggregate_signature_poc_0_indices]), aggregate_pubkey([state.validators[i].pubkey for i in votes.aggregate_signature_poc_1_indices])] - return bls_verify_multiple(pubkeys=pubs, msgs=[hash_tree_root(votes)+bytes1(0), hash_tree_root(votes)+bytes1(1), sig=aggregate_signature) + return bls_verify_multiple(pubkeys=pubs, messages=[hash_tree_root(votes)+bytes1(0), hash_tree_root(votes)+bytes1(1), signature=aggregate_signature) ``` #### `integer_squareroot` @@ -1040,7 +1044,7 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow { 'header': BeaconBlockHeader( slot=INITIAL_SLOT_NUMBER, - ancestor_hashes=[ZERO_HASH for i in range(32)], + parent_hash=ZERO_HASH, state_root=STARTUP_STATE_ROOT, randao_reveal=ZERO_HASH, candidate_pow_receipt_root=ZERO_HASH, @@ -1093,10 +1097,10 @@ def on_startup(initial_validator_deposits: List[Deposit], # Recent state latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], - latest_block_roots=[ZERO_HASH for _ in range(EPOCH_LENGTH * 2)], + latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_COUNT)], latest_penalized_exit_balances=[], latest_attestations=[], - + batched_block_roots=[] # PoW receipt root processed_pow_receipt_root=processed_pow_receipt_root, candidate_pow_receipt_roots=[], @@ -1153,9 +1157,9 @@ def process_deposit(state: BeaconState, Note that this function mutates ``state``. """ assert bls_verify( - pub=pubkey, - msg=hash(bytes32(pubkey) + withdrawal_credentials + randao_commitment), - sig=proof_of_possession, + pubkey=pubkey, + message=hash(bytes32(pubkey) + withdrawal_credentials + randao_commitment), + signature=proof_of_possession, domain=get_domain( state.fork_data, state.slot, @@ -1170,7 +1174,7 @@ def process_deposit(state: BeaconState, pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - randao_skips=0, + randao_layers=0, balance=deposit, status=PENDING_ACTIVATION, latest_status_change_slot=state.slot, @@ -1301,43 +1305,46 @@ def exit_validator(state: BeaconState, Below are the processing steps that happen at every slot. -* Let `latest_block` be the latest `BeaconBlock` that was processed in the chain. -* Let `latest_root` be the `hash_tree_root` of `latest_block`. -* Set `state.slot += 1` -* Set `state.latest_block_roots = state.latest_block_roots + [latest_root]`. (The output of `get_block_root` should not change, except that it will no longer throw for `state.slot - 1`). +### Misc counters -If there is a block from the proposer for `state.slot`, we process that incoming block: +* Set `state.slot += 1`. +* Set `state.validator_registry[get_beacon_proposer_index(state, state.slot)].randao_layers += 1`. -* Let `block` be that associated incoming block. -* Verify that `block.slot == state.slot` -* Verify that `block.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_root)`. +### Block roots -If there is no block from the proposer at state.slot: +* Let `previous_block_root` be the `tree_hash_root` of the previous beacon block processed in the chain. +* Set `state.latest_block_roots = state.latest_block_roots[1:] + [previous_block_root]`. +* If `state.slot % LATEST_BLOCK_ROOTS_COUNT == 0` append `merkle_root(state.latest_block_roots)` to `state.batched_block_roots`. -* Set `state.validator_registry[get_beacon_proposer_index(state, state.slot)].randao_skips += 1`. -* Skip all other per-slot processing. Move directly to [per-epoch processing](#per-epoch-processing). +## Per-block processing + +Below are the processing steps that happen at every `block`. + +### Slot + +* Verify that `block.slot == state.slot`. ### Proposer signature -* Let `block_root_without_sig` be the `hash_tree_root` of `block` where `block.signature` is set to `[0, 0]`. -* Let `proposal_root = hash_tree_root(ProposalSignedData(state.slot, BEACON_CHAIN_SHARD_NUMBER, block_root_without_sig))`. -* Verify that `bls_verify(pubkey=state.validator_registry[get_beacon_proposer_index(state, state.slot)].pubkey, data=proposal_root, sig=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. +* Let `block_without_signature_root` be the `hash_tree_root` of `block` where `block.signature` is set to `[0, 0]`. +* 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, data=proposal_root, signature=block.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`. ### RANDAO * Let `repeat_hash(x, n) = x if n == 0 else repeat_hash(hash(x), n-1)`. * Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`. -* Verify that `repeat_hash(block.randao_reveal, proposer.randao_skips + 1) == proposer.randao_commitment`. +* Verify that `repeat_hash(block.randao_reveal, proposer.randao_layers) == proposer.randao_commitment`. * Set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)`. * Set `proposer.randao_commitment = block.randao_reveal`. -* Set `proposer.randao_skips = 0`. +* Set `proposer.randao_layers = 0`. ### PoW receipt root * If `block.candidate_pow_receipt_root` is `x.candidate_pow_receipt_root` for some `x` in `state.candidate_pow_receipt_roots`, set `x.votes += 1`. * Otherwise, append to `state.candidate_pow_receipt_roots` a new `CandidatePoWReceiptRootRecord(candidate_pow_receipt_root=block.candidate_pow_receipt_root, votes=1)`. -### Block operations +### Operations #### Proposer slashings @@ -1346,8 +1353,8 @@ 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 `bls_verify(pubkey=proposer.pubkey, msg=hash_tree_root(proposer_slashing.proposal_data_1), sig=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. -* Verify that `bls_verify(pubkey=proposer.pubkey, msg=hash_tree_root(proposer_slashing.proposal_data_2), sig=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. +* Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * 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`. @@ -1383,7 +1390,7 @@ For each `attestation` in `block.body.attestations`: * `aggregate_signature` verification: * Let `participants = get_attestation_participants(state, attestation.data, attestation.participation_bitfield)`. * Let `group_public_key = BLSAddPubkeys([state.validator_registry[v].pubkey for v in participants])`. - * Verify that `bls_verify(pubkey=group_public_key, msg=hash_tree_root(attestation.data) + bytes1(0), sig=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. + * Verify that `bls_verify(pubkey=group_public_key, message=hash_tree_root(attestation.data) + bytes1(0), signature=attestation.aggregate_signature, domain=get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION))`. * [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_hash == ZERO_HASH`. * Append `PendingAttestationRecord(data=attestation.data, participation_bitfield=attestation.participation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`. @@ -1430,7 +1437,7 @@ Verify that `len(block.body.exits) <= MAX_EXITS`. For each `exit` in `block.body.exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `bls_verify(pubkey=validator.pubkey, msg=ZERO_HASH, sig=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. +* Verify that `bls_verify(pubkey=validator.pubkey, message=ZERO_HASH, signature=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. * Verify that `validator.status == ACTIVE`. * Verify that `state.slot >= exit.slot`. * Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`. @@ -1666,7 +1673,6 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com ### Final updates * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. -* Set `state.latest_block_roots = state.latest_block_roots[EPOCH_LENGTH:]`. ## State root processing From bc60576d3c7c322c510248fb8154a2838b845caf Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 Dec 2018 09:07:44 -0600 Subject: [PATCH 11/13] rework rewards sections as per PR feedback --- specs/core/0_beacon-chain.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2f443e6d6..9bf68e341 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -103,8 +103,10 @@ - [Justification](#justification) - [Finalization](#finalization) - [Crosslinks](#crosslinks) - - [Justification and finalization rewards and penalties](#justification-and-finalization-rewards-and-penalties) - - [Crosslink rewards and penalties](#crosslink-rewards-and-penalties) + - [Rewards and penalties](#rewards-and-penalties) + - [Justification and finalization](#justification-and-finalization) + - [Attestation inclusion](#attestation-inclusion) + - [Crosslinks](#crosslinks-1) - [Validator registry](#validator-registry) - [Proposer reshuffling](#proposer-reshuffling) - [Final updates](#final-updates) @@ -1553,7 +1555,9 @@ For every `shard_committee` in `state.shard_committees_at_slots`: * Set `state.latest_crosslinks[shard] = CrosslinkRecord(slot=state.slot, block_root=winning_root(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. -### Justification and finalization rewards and penalties +### Rewards and penalties + +#### Justification and finalization Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. @@ -1578,9 +1582,11 @@ Case 2: `slots_since_finality > 4 * EPOCH_LENGTH`: * Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_head_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. * Any [validator](#dfn-validator) with `status == EXITED_WITH_PENALTY`, loses `3 * base_inactivity_penalty(v, slots_since_finality)`. +#### Attestation inclusion + For each `v` in `previous_epoch_attesters`, we determine the proposer `proposer_index = get_beacon_proposer_index(state, inclusion_slot(v))` and set `state.validator_registry[proposer_index].balance += base_reward(v) // INCLUDER_REWARD_QUOTIENT`. -### Crosslink rewards and penalties +#### Crosslinks For every `shard_committee` in `state.shard_committees_at_slots[:EPOCH_LENGTH]` (i.e. the objects corresponding to the epoch before the current one), for each `v` in `[state.validator_registry[index] for index in shard_committee.committee]`, adjust balances as follows: From 541f33094fd02294e27930730eb6f71db2061c55 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 Dec 2018 09:14:44 -0600 Subject: [PATCH 12/13] move rewards helpers to rewrds and penalties section --- specs/core/0_beacon-chain.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 9bf68e341..f9edb7d02 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1470,9 +1470,6 @@ All [validators](#dfn-validator): * Let `active_validators = [state.validator_registry[i] for i in get_active_validator_indices(state.validator_registry)]`. * Let `total_balance = sum([get_effective_balance(v) for v in active_validators])`. -* Let `base_reward_quotient = BASE_REWARD_QUOTIENT * integer_squareroot(total_balance // GWEI_PER_ETH)`. -* Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient // 4` for any validator `v`. -* Let `base_inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. [Validators](#dfn-Validator) validators attesting during the current epoch: @@ -1557,6 +1554,12 @@ For every `shard_committee` in `state.shard_committees_at_slots`: ### Rewards and penalties +First, we define some additional helpers: + +* Let `base_reward_quotient = BASE_REWARD_QUOTIENT * integer_squareroot(total_balance // GWEI_PER_ETH)`. +* Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient // 4` for any validator `v`. +* Let `base_inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. + #### Justification and finalization Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow. From 10fa84b057746d5e598d7bdf329fe8b56f95625e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 12 Dec 2018 09:28:47 -0600 Subject: [PATCH 13/13] rename base_inactivity_penalty to inactivity_penalty --- specs/core/0_beacon-chain.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f9edb7d02..00dd9ad64 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1558,7 +1558,7 @@ First, we define some additional helpers: * Let `base_reward_quotient = BASE_REWARD_QUOTIENT * integer_squareroot(total_balance // GWEI_PER_ETH)`. * Let `base_reward(v) = get_effective_balance(v) // base_reward_quotient // 4` for any validator `v`. -* Let `base_inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. +* Let `inactivity_penalty(v, slots_since_finality) = base_reward(v) + get_effective_balance(v) * slots_since_finality // INACTIVITY_PENALTY_QUOTIENT` for any validator `v`. #### Justification and finalization @@ -1580,10 +1580,10 @@ Case 1: `slots_since_finality <= 4 * EPOCH_LENGTH`: Case 2: `slots_since_finality > 4 * EPOCH_LENGTH`: -* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_justified_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. -* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_boundary_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. -* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_head_attesters`, loses `base_inactivity_penalty(v, slots_since_finality)`. -* Any [validator](#dfn-validator) with `status == EXITED_WITH_PENALTY`, loses `3 * base_inactivity_penalty(v, slots_since_finality)`. +* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_justified_attesters`, loses `inactivity_penalty(v, slots_since_finality)`. +* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_boundary_attesters`, loses `inactivity_penalty(v, slots_since_finality)`. +* Any [active validator](#dfn-active-validator) `v` not in `previous_epoch_head_attesters`, loses `inactivity_penalty(v, slots_since_finality)`. +* Any [validator](#dfn-validator) with `status == EXITED_WITH_PENALTY`, loses `3 * inactivity_penalty(v, slots_since_finality)`. #### Attestation inclusion