Merge branch 'master' into JustinDrake-patch-8

This commit is contained in:
Justin 2018-12-10 10:11:41 +00:00 committed by GitHub
commit 0afafe1c76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 293 additions and 271 deletions

View File

@ -2,19 +2,19 @@
**Warning: This document is pending academic review and should not yet be considered secure.**
See https://z.cash/blog/new-snark-curve/ for BLS-12-381 parameters.
See https://z.cash/blog/new-snark-curve/ for BLS-12-381 parameters. `q` is the field modulus.
We represent coordinates as defined in https://github.com/zkcrypto/pairing/tree/master/src/bls12_381/.
Specifically, a point in G1 as a 384-bit integer `z`, which we decompose into:
* `x = z % 2**381`
* `x = z % 2**381` (must be `< q`)
* `highflag = z // 2**382`
* `lowflag = (z % 2**382) // 2**381`
If `highflag == 3`, the point is the point at infinity and we require `lowflag = x = 0`. Otherwise, we require `highflag == 2`, in which case the point is `(x, y)` where `y` is the valid coordinate such that `(y * 2) // q == lowflag`.
We represent a point in G2 as a pair of 384-bit integers `(z1, z2)` that are each decomposed into `x1`, `highflag1`, `lowflag1`, `x2`, `highflag2`, `lowflag2` as above. We require `lowflag2 == highflag2 == 0`. If `highflag1 == 3`, the point is the point at infinity and we require `lowflag1 == x1 == x2 == 0`. Otherwise, we require `highflag == 2`, in which case the point is `(x1 * i + x2, y)` where `y` is the valid coordinate such that the imaginary part of `y` satisfies `(y_im * 2) // q == lowflag1`.
We represent a point in G2 as a pair of 384-bit integers `(z1, z2)` that are each decomposed into `x1`, `highflag1`, `lowflag1`, `x2`, `highflag2`, `lowflag2` as above, where `x1` and `x2` must both be `< q`. We require `lowflag2 == highflag2 == 0`. If `highflag1 == 3`, the point is the point at infinity and we require `lowflag1 == x1 == x2 == 0`. Otherwise, we require `highflag == 2`, in which case the point is `(x1 * i + x2, y)` where `y` is the valid coordinate such that the imaginary part of `y` satisfies `(y_im * 2) // q == lowflag1`.
`BLSVerify(pubkey: uint384, msg: bytes32, sig: [uint384], domain: uint64)` is done as follows:

View File

@ -17,20 +17,28 @@
- [Time parameters](#time-parameters)
- [Reward and penalty quotients](#reward-and-penalty-quotients)
- [Status codes](#status-codes)
- [Special record types](#special-record-types)
- [Max operations per block](#max-operations-per-block)
- [Validator registry delta flags](#validator-registry-delta-flags)
- [Signature domains](#signature-domains)
- [Data structures](#data-structures)
- [Deposits](#deposits)
- [`DepositParametersRecord`](#depositparametersrecord)
- [Beacon chain blocks](#beacon-chain-blocks)
- [`BeaconBlockHeader`](#beaconblockheader)
- [`BeaconBlockBody`](#beaconblockbody)
- [`BeaconBlock`](#beaconblock)
- [`AttestationRecord`](#attestationrecord)
- [Beacon chain operations](#beacon-chain-operations)
- [Proposer slashings](#proposer-slashings)
- [`ProposerSlashing`](#proposerslashing)
- [Casper slashings](#casper-slashings)
- [`CasperSlashing`](#casperslashing)
- [`SlashableVoteData`](#slashablevotedata)
- [Attestations](#attestations)
- [`Attestation`](#attestation)
- [`AttestationData`](#attestationdata)
- [Deposits](#deposits)
- [`Deposit`](#deposit)
- [`DepositParameters`](#depositparameters)
- [Exits](#exits)
- [`Exit`](#exit)
- [Beacon chain blocks](#beacon-chain-blocks)
- [`BeaconBlock`](#beaconblock)
- [`BeaconBlockBody`](#beaconblockbody)
- [`ProposalSignedData`](#proposalsigneddata)
- [`SpecialRecord`](#specialrecord)
- [Beacon chain state](#beacon-chain-state)
- [`BeaconState`](#beaconstate)
- [`ValidatorRecord`](#validatorrecord)
@ -40,12 +48,6 @@
- [`CandidatePoWReceiptRootRecord`](#candidatepowreceiptrootrecord)
- [`PendingAttestationRecord`](#pendingattestationrecord)
- [`ForkData`](#forkdata)
- [Specials](#specials)
- [`VoluntaryExitSpecial`](#voluntaryexitspecial)
- [`CasperSlashingSpecial`](#casperslashingspecial)
- [`SpecialAttestationData`](#specialattestationdata)
- [`ProposerSlashingSpecial`](#proposerslashingspecial)
- [`DepositProofSpecial`](#depositproofspecial)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
- [Deposit arguments](#deposit-arguments)
- [`Deposit` logs](#deposit-logs)
@ -64,25 +66,27 @@
- [`get_shard_committees_at_slot`](#get_shard_committees_at_slot)
- [`get_block_hash`](#get_block_hash)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`get_updated_ancestor_hashes`](#get_updated_ancestor_hashes)
- [`get_attestation_participants`](#get_attestation_participants)
- [`bytes1`, `bytes2`, ...](#bytes1-bytes2-)
- [`get_effective_balance`](#get_effective_balance)
- [`get_new_validator_registry_delta_chain_tip`](#get_new_validator_registry_delta_chain_tip)
- [`get_domain`](#get_domain)
- [`verify_casper_votes`](#verify_casper_votes)
- [`integer_squareroot`](#integer_squareroot)
- [On startup](#on-startup)
- [Routine for activating a validator](#routine-for-activating-a-validator)
- [Routine for exiting a validator](#routine-for-exiting-a-validator)
- [Per-slot processing](#per-slot-processing)
- [Proposer signature](#proposer-signature)
- [Attestations](#attestations)
- [RANDAO](#randao)
- [PoW receipt root](#pow-receipt-root)
- [Special objects](#special-objects)
- [`VOLUNTARY_EXIT`](#voluntary_exit)
- [`CASPER_SLASHING`](#casper_slashing)
- [`PROPOSER_SLASHING`](#proposer_slashing)
- [`DEPOSIT_PROOF`](#deposit_proof)
- [Block operations](#block-operations)
- [Proposer slashings](#proposer-slashings-1)
- [Casper slashings](#casper-slashings-1)
- [Attestations](#attestations-1)
- [Deposits](#deposits-1)
- [Exits](#exits-1)
- [Ejections](#ejections)
- [Per-epoch processing](#per-epoch-processing)
- [Helpers](#helpers)
@ -141,12 +145,12 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| - | - | :-: |
| `SHARD_COUNT` | `2**10` (= 1,024) | shards |
| `TARGET_COMMITTEE_SIZE` | `2**8` (= 256) | [validators](#dfn-validator) |
| `MAX_ATTESTATIONS_PER_BLOCK` | `2**7` (= 128) | attestations |
| `EJECTION_BALANCE` | `2**4` (= 16) | ETH |
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - |
| `GWEI_PER_ETH` | `10**9` | Gwei/ETH |
| `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - |
| `BLS_WITHDRAWAL_CREDENTIALS` | `0x00` | - |
| `BLS_WITHDRAWAL_PREFIX_BYTE` | `0x00` | - |
| `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes |
* 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`.
@ -187,10 +191,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| `BASE_REWARD_QUOTIENT` | `2**11` (= 2,048) |
| `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) |
| `INCLUDER_REWARD_QUOTIENT` | `2**3` (= 8) |
| `INACTIVITY_PENALTY_QUOTIENT` | `2**34` (= 131,072) |
| `INACTIVITY_PENALTY_QUOTIENT` | `2**34` (= 17,179,869,184) |
* The `BASE_REWARD_QUOTIENT` constant 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 `SQRT_E_DROP_TIME**2` where `SQRT_E_DROP_TIME := 2**17 slots` (~9 days) is the amount of time it takes for the inactivity penalty to cut deposits of non-participating [validators](#dfn-validator) by ~39.4%. The portion lost by offline [validators](#dfn-validator) after `D` epochs is about `D*D/2/INACTIVITY_PENALTY_QUOTIENT`.
* 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**17 slots` (~9 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` slots is about `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` slots it is roughly `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`.
### Status codes
@ -202,14 +206,15 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| `EXITED_WITHOUT_PENALTY` | `3` |
| `EXITED_WITH_PENALTY` | `4` |
### Special record types
### Max operations per block
| Name | Value | Maximum count |
| - | - | :-: |
| `VOLUNTARY_EXIT` | `0` | `16` |
| `CASPER_SLASHING` | `1` | `16` |
| `PROPOSER_SLASHING` | `2` | `16` |
| `DEPOSIT_PROOF` | `3` | `16` |
| Name | Value |
| - | - |
| `MAX_PROPOSER_SLASHINGS` | `2**4` (= 16) |
| `MAX_CASPER_SLASHINGS` | `2**4` (= 16) |
| `MAX_ATTESTATIONS` | `2**7` (= 128) |
| `MAX_DEPOSITS` | `2**4` (= 16) |
| `MAX_EXITS` | `2**4` (= 16) |
### Validator registry delta flags
@ -229,59 +234,58 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
## Data structures
### Deposits
### Beacon chain operations
#### `DepositParametersRecord`
#### Proposer slashings
##### `ProposerSlashing`
```python
{
# BLS pubkey
'pubkey': 'uint384',
# BLS proof of possession (a BLS signature)
'proof_of_possession': ['uint384'],
# Withdrawal credentials
'withdrawal_credentials': 'hash32',
# Initial RANDAO commitment
'randao_commitment': 'hash32',
# Proposer index
'proposer_index': 'uint24',
# First proposal data
'proposal_data_1': ProposalSignedData,
# First proposal signature
'proposal_signature_1': '[uint384]',
# Second proposal data
'proposal_data_2': ProposalSignedData,
# Second proposal signature
'proposal_signature_2': '[uint384]',
}
```
### Beacon chain blocks
#### Casper slashings
#### `BeaconBlockHeader`
##### `CasperSlashing`
```python
{
'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'],
'state_root': 'hash32',
'randao_reveal': 'hash32',
'candidate_pow_receipt_root': 'hash32',
'signature': ['uint384'],
# First batch of votes
'votes_1': SlashableVoteData,
# Second batch of votes
'votes_2': SlashableVoteData,
}
```
#### `BeaconBlockBody`
##### `SlashableVoteData`
```python
{
'attestations': [AttestationRecord],
'specials': [SpecialRecord],
# Proof-of-custody indices (0 bits)
'aggregate_signature_poc_0_indices': '[uint24]',
# Proof-of-custody indices (1 bits)
'aggregate_signature_poc_1_indices': '[uint24]',
# Attestation data
'data': AttestationData,
# Aggregate signature
'aggregate_signature': '[uint384]',
}
```
#### `BeaconBlock`
#### Attestations
```python
{
'header': BeaconBlockHeader,
'body': BeaconBlockBody,
}
```
#### `AttestationRecord`
##### `Attestation`
```python
{
@ -296,7 +300,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
}
```
#### `AttestationData`
##### `AttestationData`
```python
{
@ -319,6 +323,91 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
}
```
#### Deposits
##### `Deposit`
```python
{
# Receipt Merkle branch
'merkle_branch': '[hash32]',
# Merkle tree index
'merkle_tree_index': 'uint64',
# Deposit data
'deposit_data': {
# Deposit parameters
'deposit_parameters': DepositParameters,
# Value in Gwei
'value': 'uint64',
# Timestamp from deposit contract
'timestamp': 'uint64',
},
}
```
##### `DepositParameters`
```python
{
# BLS pubkey
'pubkey': 'uint384',
# BLS proof of possession (a BLS signature)
'proof_of_possession': ['uint384'],
# Withdrawal credentials
'withdrawal_credentials': 'hash32',
# Initial RANDAO commitment
'randao_commitment': 'hash32',
}
```
#### Exits
##### `Exit`
```python
{
# Minimum slot for processing exit
'slot': 'unit64',
# Index of the exiting validator
'validator_index': 'uint64',
# Validator signature
'signature': '[uint384]',
}
```
### Beacon chain blocks
#### `BeaconBlock`
```python
{
## Header ##
'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'],
'state_root': 'hash32',
'randao_reveal': 'hash32',
'candidate_pow_receipt_root': 'hash32',
'signature': ['uint384'],
## Body ##
'body': BeaconBlockBody,
}
```
#### `BeaconBlockBody`
```python
{
'attestations': [Attestation],
'proposer_slashings': [ProposerSlashing],
'casper_slashings': [CasperSlashing],
'deposits': [Deposit],
'exits': [Exit],
}
```
#### `ProposalSignedData`
```python
@ -332,17 +421,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
}
```
#### `SpecialRecord`
```python
{
# Kind
'kind': 'uint64',
# Data
'data': 'bytes',
}
```
### Beacon chain state
#### `BeaconState`
@ -484,93 +562,15 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
}
```
### Specials
#### `VoluntaryExitSpecial`
```python
{
# Minimum slot for processing exit
'slot': 'unit64',
# Index of the exiting validator
'validator_index': 'uint64',
# Validator signature
'signature': '[uint384]',
}
```
#### `CasperSlashingSpecial`
```python
{
# First vote
vote_1: SpecialAttestationData,
# Second vote
vote_2: SpecialAttestationData,
}
```
#### `SpecialAttestationData`
```python
{
# Proof-of-custody indices (0 bits)
'aggregate_signature_poc_0_indices': '[uint24]',
# Proof-of-custody indices (1 bits)
'aggregate_signature_poc_1_indices': '[uint24]',
# Attestation data
'data': AttestationData,
# Aggregate signature
'aggregate_signature': '[uint384]',
}
```
#### `ProposerSlashingSpecial`
```python
{
# Proposer index
'proposer_index': 'uint24',
# First proposal data
'proposal_data_1': ProposalSignedData,
# First proposal signature
'proposal_signature_1': '[uint384]',
# Second proposal data
'proposal_data_2': ProposalSignedData,
# Second proposal signature
'proposal_signature_2': '[uint384]',
}
```
#### `DepositProofSpecial`
```python
{
# Receipt Merkle branch
'merkle_branch': '[hash32]',
# Merkle tree index
'merkle_tree_index': 'uint64',
# Deposit data
'deposit_data': {
# Deposit parameters
'deposit_parameters': DepositParametersRecord,
# Value in Gwei
'value': 'uint64',
# Timestamp from deposit contract
'timestamp': 'uint64',
},
}
```
## Ethereum 1.0 deposit contract
The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards when the EVM2.0 is deployed and the shards have state.
### Deposit arguments
The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositParametersRecord` object. One of the `DepositParametersRecord` fields is `withdrawal_credentials` which must satisfy:
The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositParameters`. One of the `DepositParameters` fields is `withdrawal_credentials` which must satisfy:
* `withdrawal_credentials[:1] == BLS_WITHDRAWAL_CREDENTIALS`
* `withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE`
* `withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:]` where `withdrawal_pubkey` is a BLS pubkey
We recommend the private key corresponding to `withdrawal_pubkey` be stored in cold storage until a withdrawal is required.
@ -650,10 +650,10 @@ 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 `block.header.ancestor_hashes[0]` has been processed and accepted.
* The node has processed its `state` up to slot, `block.header.slot - 1`.
* The parent block with hash `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.header.slot * SLOT_DURATION`.
* The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`.
If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied.
@ -666,7 +666,7 @@ The beacon chain fork choice rule is a hybrid that combines justification and fi
* Let `store` be the set of attestations and blocks that the [validator](#dfn-validator) `v` has observed and verified (in particular, block ancestors must be recursively verified). Attestations not part of any chain are still included in `store`.
* Let `finalized_head` be the finalized block with the highest slot number. (A block `B` is finalized if there is a descendant of `B` in `store` the processing of which sets `B` as finalized.)
* Let `justified_head` be the descendant of `finalized_head` with the highest slot number that has been justified for at least `EPOCH_LENGTH` slots. (A block `B` is justified if there is a descendant of `B` in `store` the processing of which sets `B` as justified.) If no such descendant exists set `justified_head` to `finalized_head`.
* Let `get_ancestor(store, block, slot)` be the ancestor of `block` with slot number `slot`. The `get_ancestor` function can be defined recursively as `def get_ancestor(store, block, slot): return block if block.header.slot == slot else get_ancestor(store, store.get_parent(block), slot)`.
* Let `get_ancestor(store, block, slot)` be the ancestor of `block` with slot number `slot`. The `get_ancestor` function can be defined recursively as `def get_ancestor(store, block, slot): return block if block.slot == slot else get_ancestor(store, store.get_parent(block), slot)`.
* Let `get_latest_attestation(store, validator)` be the attestation with the highest slot number in `store` from `validator`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first.
* Let `get_latest_attestation_target(store, validator)` be the target block in the attestation `get_latest_attestation(store, validator)`.
* The head is `lmd_ghost(store, justified_head)` where the function `lmd_ghost` is defined below. Note that the implementation below is suboptimal; there are implementations that compute the head in time logarithmic in slot count.
@ -680,7 +680,7 @@ def lmd_ghost(store, start):
for validator in active_validators]
def get_vote_count(block):
return len([target for target in attestation_targets if
get_ancestor(store, target, block.header.slot) == block])
get_ancestor(store, target, block.slot) == block])
head = start
while 1:
@ -704,7 +704,6 @@ 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.
#### `is_active_validator`
```python
def is_active_validator(validator: ValidatorRecord) -> bool:
"""
@ -897,9 +896,9 @@ def get_beacon_proposer_index(state: BeaconState,
```python
def get_updated_ancestor_hashes(latest_block: BeaconBlock,
latest_hash: Hash32) -> List[Hash32]:
new_ancestor_hashes = copy.deepcopy(latest_block.header.ancestor_hashes)
new_ancestor_hashes = copy.deepcopy(latest_block.ancestor_hashes)
for i in range(32):
if latest_block.header.slot % 2**i == 0:
if latest_block.slot % 2**i == 0:
new_ancestor_hashes[i] = latest_hash
return new_ancestor_hashes
```
@ -956,7 +955,7 @@ def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_
current_validator_registry_delta_chain_tip +
bytes1(flag) +
bytes3(index) +
bytes32(pubkey)
bytes48(pubkey)
)
```
@ -972,6 +971,18 @@ def get_domain(fork_data: ForkData,
) * 2**32 + domain_type
```
#### `verify_casper_votes`
```python
def verify_casper_votes(state: BeaconState, votes: SlashableVoteData) -> bool:
if len(votes.aggregate_signature_poc_0_indices) + len(votes.aggregate_signature_poc_1_indices) > MAX_CASPER_VOTES:
return False
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)
```
#### `integer_squareroot`
```python
@ -993,14 +1004,21 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow
```python
{
'slot': INITIAL_SLOT_NUMBER,
'randao_reveal': ZERO_HASH,
'candidate_pow_receipt_root': ZERO_HASH,
'ancestor_hashes': [ZERO_HASH for i in range(32)],
'state_root': STARTUP_STATE_ROOT,
'attestations': [],
'specials': [],
'proposer_signature': [0, 0],
'header': BeaconBlockHeader(
slot=INITIAL_SLOT_NUMBER,
ancestor_hashes=[ZERO_HASH for i in range(32)],
state_root=STARTUP_STATE_ROOT,
randao_reveal=ZERO_HASH,
candidate_pow_receipt_root=ZERO_HASH,
proposer_signature=[0, 0]
),
'body': BeaconBlockBody(
proposer_slashings=[],
casper_slashings=[],
attestations=[],
deposits=[],
exits=[]
),
}
```
@ -1025,12 +1043,15 @@ def on_startup(initial_validator_entries: List[Any],
proof_of_possession=proof_of_possession,
withdrawal_credentials=withdrawal_credentials,
randao_commitment=randao_commitment,
current_slot=INITIAL_SLOT_NUMBER,
status=ACTIVE,
current_slot=INITIAL_SLOT_NUMBER,
)
# Setup state
initial_shuffling = get_new_shuffling(ZERO_HASH, initial_validator_registry, 0)
active_validator_indices = get_active_validator_indices(initial_validator_registry)
initial_persistent_committees = split(shuffle(active_validator_indices, ZERO_HASH), SHARD_COUNT)
state = BeaconState(
# Misc
slot=INITIAL_SLOT_NUMBER,
@ -1051,7 +1072,7 @@ def on_startup(initial_validator_entries: List[Any],
randao_mix=ZERO_HASH,
next_seed=ZERO_HASH,
shard_committees_at_slots=initial_shuffling + initial_shuffling,
persistent_committees=split(shuffle(initial_validator_registry, ZERO_HASH), SHARD_COUNT),
persistent_committees=initial_persistent_committees,
persistent_committee_reassignments=[],
# Finality
@ -1156,8 +1177,7 @@ def process_deposit(state: BeaconState,
proof_of_possession: bytes,
withdrawal_credentials: Hash32,
randao_commitment: Hash32,
status: int,
current_slot: int) -> int:
status: int) -> int:
"""
Process a deposit from Ethereum 1.0.
Note that this function mutates ``state``.
@ -1175,7 +1195,7 @@ def process_deposit(state: BeaconState,
withdrawal_credentials=withdrawal_credentials,
randao_commitment=randao_commitment,
status=status,
current_slot=current_slot,
current_slot=state.slot,
)
return index
@ -1186,7 +1206,7 @@ def process_deposit(state: BeaconState,
```python
def exit_validator(index: int,
state: BeaconState,
new_status: int) -> None:
new_status: bool) -> None:
"""
Exit the validator with the given ``index``.
Note that this function mutates ``state``.
@ -1205,7 +1225,7 @@ def exit_validator(index: int,
committee.pop(i)
break
if new_status == EXITED_WITH_PENALTY:
if penalize:
state.latest_penalized_exit_balances[state.slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += get_effective_balance(validator)
whistleblower = state.validator_registry[get_beacon_proposer_index(state, state.slot)]
@ -1231,23 +1251,71 @@ Below are the processing steps that happen at every slot.
* 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`).
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.header.slot == state.slot`
* Verify that `block.header.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_hash)`.
* Verify that `block.slot == state.slot`
* Verify that `block.ancestor_hashes` equals `get_updated_ancestor_hashes(latest_block, latest_hash)`.
If there is no block from the proposer at state.slot:
* 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).
### Proposer signature
* Let `block_hash_without_sig` be the hash of `block` where `block.header.signature` is set to `[0, 0]`.
* Let `block_hash_without_sig` be the hash of `block` where `block.signature` is set to `[0, 0]`.
* Let `proposal_hash = hash(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.header.signature, domain=get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))`.
* 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))`.
### Attestations
### RANDAO
* Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS_PER_BLOCK`.
* 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`.
* Set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)`.
* Set `proposer.randao_commitment = block.randao_reveal`.
* Set `proposer.randao_skips = 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
#### Proposer slashings
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=hash(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(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.status != EXITED_WITH_PENALTY`.
* Run `exit_validator(proposer_slashing.proposer_index, state, new_status=EXITED_WITH_PENALTY)`.
#### Casper slashings
Verify that `len(block.body.casper_slashings) <= MAX_CASPER_SLASHINGS`.
For each `casper_slashing` in `block.body.casper_slashings`:
* Verify that `verify_casper_votes(state, casper_slashing.votes_1)`.
* Verify that `verify_casper_votes(state, casper_slashing.votes_2)`.
* Verify that `casper_slashing.votes_1.data != casper_slashing.votes_2.data`.
* Let `indices(vote) = vote.aggregate_signature_poc_0_indices + vote.aggregate_signature_poc_1_indices`.
* Let `intersection = [x for x in indices(casper_slashing.votes_1) if x in indices(casper_slashing.votes_2)]`.
* Verify that `len(intersection) >= 1`.
* Verify that `casper_slashing.votes_1.data.justified_slot + 1 < casper_slashing.votes_2.data.justified_slot + 1 == casper_slashing.votes_2.data.slot < casper_slashing.votes_1.data.slot` or `casper_slashing.votes_1.data.slot == casper_slashing.votes_2.data.slot`.
* For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `exit_validator(i, state, penalize=True, new_status=EXITED_WITH_PENALTY)`
#### Attestations
Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`.
For each `attestation` in `block.body.attestations`:
@ -1263,75 +1331,14 @@ For each `attestation` in `block.body.attestations`:
* [TO BE REMOVED IN PHASE 1] Verify that `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`.
### RANDAO
#### Deposits
* 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.header.randao_reveal, proposer.randao_skips + 1) == proposer.randao_commitment`.
* Set `state.randao_mix = xor(state.randao_mix, block.header.randao_reveal)`.
* Set `proposer.randao_commitment = block.header.randao_reveal`.
* Set `proposer.randao_skips = 0`.
Verify that `len(block.body.deposits) <= MAX_DEPOSITS`.
### PoW receipt root
For each `deposit` in `block.body.deposits`:
* If `block.header.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.header.candidate_pow_receipt_root, votes=1)`.
### Special objects
* Verify that the quantity of each type of object in `block.body.specials` is less than or equal to its maximum (see table at the top).
* Verify that objects are sorted in order of `kind`. That is, `block.body.specials[i+1].kind >= block.body.specials[i].kind` for `0 <= i < len(block.body.specials-1)`.
For each `special` in `block.body.specials`:
* Verify that `special.kind` is a valid value.
* Verify that `special.data` deserializes according to the format for the given `kind` (see format definitions in "Data structures" above).
* Process `special.data` as specified below for each kind.
#### `VOLUNTARY_EXIT`
* Let `validator = state.validator_registry[validator_index]`.
* Verify that `BLSVerify(pubkey=validator.pubkey, msg=ZERO_HASH, sig=signature, domain=get_domain(state.fork_data, slot, DOMAIN_EXIT))`.
* Verify that `validator.status == ACTIVE`.
* Verify that `state.slot >= slot`.
* Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`.
* Run `exit_validator(validator_index, state, new_status=ACTIVE_PENDING_EXIT)`.
#### `CASPER_SLASHING`
Let `verify_special_attestation_data` be the following helper:
```python
def verify_special_attestation_data(state: BeaconState, obj: SpecialAttestationData) -> bool:
pubs = [aggregate_pubkey([state.validators[i].pubkey for i in obj.aggregate_signature_poc_0_indices]),
aggregate_pubkey([state.validators[i].pubkey for i in obj.aggregate_signature_poc_1_indices])]
return BLSMultiVerify(pubkeys=pubs, msgs=[SSZTreeHash(obj)+bytes1(0), SSZTreeHash(obj)+bytes1(1), sig=aggregate_signature)
```
* Verify that `verify_special_attestation_data(vote_1)`.
* Verify that `verify_special_attestation_data(vote_2)`.
* Verify that `vote_1.data != vote_2.data`.
* Let `indices(vote) = vote.aggregate_signature_poc_0_indices + vote.aggregate_signature_poc_1_indices`.
* Let `intersection = [x for x in indices(vote_1) if x in indices(vote_2)]`.
* Verify that `len(intersection) >= 1`.
* Verify that `vote_1.data.justified_slot + 1 < vote_2.data.justified_slot + 1 == vote_2.data.slot < vote_1.data.slot` or `vote_1.data.slot == vote_2.data.slot`.
For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `exit_validator(i, state, new_status=EXITED_WITH_PENALTY)`
#### `PROPOSER_SLASHING`
* Verify that `BLSVerify(pubkey=state.validator_registry[proposer_index].pubkey, msg=hash(proposal_data_1), sig=proposal_signature_1, domain=get_domain(state.fork_data, proposal_data_1.slot, DOMAIN_PROPOSAL))`.
* Verify that `BLSVerify(pubkey=state.validator_registry[proposer_index].pubkey, msg=hash(proposal_data_2), sig=proposal_signature_2, domain=get_domain(state.fork_data, proposal_data_2.slot, DOMAIN_PROPOSAL))`.
* Verify that `proposal_data_1 != proposal_data_2`.
* Verify that `proposal_data_1.slot == proposal_data_2.slot`.
* Verify that `state.validator_registry[proposer_index].status != EXITED_WITH_PENALTY`.
* Run `exit_validator(proposer_index, state, new_status=EXITED_WITH_PENALTY)`.
#### `DEPOSIT_PROOF`
Let `serialized_deposit_data` be the serialized form of `deposit_data. It should be the `DepositParametersRecord` followed by 8 bytes for `deposit_data.value` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-chain-deposit-contract) of which the hash was placed into the Merkle tree.
Use the following procedure to verify the `merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_pow_receipt_root`:
* Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositParameters` followed by 8 bytes for `deposit_data.value` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-chain-deposit-contract) of which the hash was placed into the Merkle tree.
* Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_pow_receipt_root`:
```python
def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, root: Hash32) -> bool:
@ -1344,22 +1351,34 @@ def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int,
return value == root
```
* Verify that `state.slot - (deposit_data.timestamp - state.genesis_time) // SLOT_DURATION < ZERO_BALANCE_VALIDATOR_TTL`.
* Verify that `state.slot - (deposit.deposit_data.timestamp - state.genesis_time) // SLOT_DURATION < ZERO_BALANCE_VALIDATOR_TTL`.
* Run the following:
```python
process_deposit(
state=state,
pubkey=deposit_data.deposit_parameters.pubkey,
deposit=deposit_data.value,
proof_of_possession=deposit_data.deposit_parameters.proof_of_possession,
withdrawal_credentials=deposit_data.deposit_parameters.withdrawal_credentials,
randao_commitment=deposit_data.deposit_parameters.randao_commitment,
status=PENDING_ACTIVATION,
current_slot=state.slot
pubkey=deposit.deposit_data.deposit_parameters.pubkey,
deposit=deposit.deposit_data.value,
proof_of_possession=deposit.deposit_data.deposit_parameters.proof_of_possession,
withdrawal_credentials=deposit.deposit_data.deposit_parameters.withdrawal_credentials,
randao_commitment=deposit.deposit_data.deposit_parameters.randao_commitment,
status=PENDING_ACTIVATION
)
```
#### Exits
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 `validator.status == ACTIVE`.
* Verify that `state.slot >= exit.slot`.
* Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`.
* Run `exit_validator(validator_index, state, new_status=ACTIVE_PENDING_EXIT)`.
### Ejections
* Run `process_ejections(state)`.
@ -1392,14 +1411,16 @@ All [validators](#dfn-validator):
* 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_attesters` 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_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:
* 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_attesters` 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_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`:
@ -1439,6 +1460,7 @@ If `state.slot % POW_RECEIPT_ROOT_VOTING_PERIOD == 0`:
### Finalization
Set `state.finalized_slot = state.previous_justified_slot` if any of the following are true:
* `state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and state.justification_bitfield % 4 == 3`
* `state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and state.justification_bitfield % 8 == 7`
* `state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and state.justification_bitfield % 16 in (15, 14)`
@ -1626,7 +1648,7 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com
## State root processing
Verify `block.header.state_root == hash(state)` if there exists a `block` for the slot being processed.
Verify `block.state_root == hash(state)` if there exists a `block` for the slot being processed.
# Appendix
## Appendix A - Hash function

View File

@ -56,7 +56,7 @@ overhead.
| Constant | Value | Definition |
|:------------------|:-----:|:--------------------------------------------------------------------------------------|
| `LENGTH_BYTES` | 4 | Number of bytes used for the length added before a variable-length serialized object. |
| `SSZ_CHUNK_SIZE` | 128 | Number of bytes for the chuck size of the Merkle tree leaf. |
| `SSZ_CHUNK_SIZE` | 128 | Number of bytes for the chunk size of the Merkle tree leaf. |
## Overview
@ -394,11 +394,11 @@ The below `SSZTreeHash` algorithm is defined recursively in the case of lists an
We define `hash(x)` as `BLAKE2b-512(x)[0:32]`.
#### `uintN`, `bool`, `address`, `hash32`
#### `uint8`..`uint256`, `bool`, `address`, `hash1`..`hash32`
Return the serialization of the value.
#### `bytes`, `hashN`
#### `uint264`..`uintN`, `bytes`, `hash33`..`hashN`
Return the hash of the serialization of the value.