Merge branch 'master' into JustinDrake-patch-4
This commit is contained in:
commit
753219ba6a
|
@ -1,7 +1,6 @@
|
||||||
# Ethereum 2.0 spec—Casper and sharding
|
# Ethereum 2.0 spec—Casper and sharding
|
||||||
|
|
||||||
###### tags: `spec`, `eth2.0`, `casper`, `sharding`
|
###### tags: `spec`, `eth2.0`, `casper`, `sharding`
|
||||||
###### spec version: 2.2 (October 2018)
|
|
||||||
|
|
||||||
**NOTICE**: This document is a work-in-progress for researchers and implementers. It reflects recent spec changes and takes precedence over the [Python proof-of-concept implementation](https://github.com/ethereum/beacon_chain).
|
**NOTICE**: This document is a work-in-progress for researchers and implementers. It reflects recent spec changes and takes precedence over the [Python proof-of-concept implementation](https://github.com/ethereum/beacon_chain).
|
||||||
|
|
||||||
|
@ -35,6 +34,7 @@ The primary source of load on the beacon chain are "attestations". Attestations
|
||||||
| --- | --- | :---: | - |
|
| --- | --- | :---: | - |
|
||||||
| `SHARD_COUNT` | 2**10 (= 1,024)| shards |
|
| `SHARD_COUNT` | 2**10 (= 1,024)| shards |
|
||||||
| `DEPOSIT_SIZE` | 2**5 (= 32) | ETH |
|
| `DEPOSIT_SIZE` | 2**5 (= 32) | ETH |
|
||||||
|
| `GWEI_PER_ETH` | 10**9 | Gwei/ETH |
|
||||||
| `MIN_COMMITTEE_SIZE` | 2**7 (= 128) | validators |
|
| `MIN_COMMITTEE_SIZE` | 2**7 (= 128) | validators |
|
||||||
| `GENESIS_TIME` | **TBD** | seconds |
|
| `GENESIS_TIME` | **TBD** | seconds |
|
||||||
| `SLOT_DURATION` | 2**4 (= 16) | seconds |
|
| `SLOT_DURATION` | 2**4 (= 16) | seconds |
|
||||||
|
@ -44,6 +44,7 @@ The primary source of load on the beacon chain are "attestations". Attestations
|
||||||
| `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days |
|
| `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days |
|
||||||
| `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — |
|
| `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — |
|
||||||
| `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — |
|
| `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — |
|
||||||
|
| `LOGOUT_MESSAGE` | `"LOGOUT"` | — |
|
||||||
|
|
||||||
**Notes**
|
**Notes**
|
||||||
|
|
||||||
|
@ -51,18 +52,30 @@ The primary source of load on the beacon chain are "attestations". Attestations
|
||||||
* The `BASE_REWARD_QUOTIENT` constant is the per-slot interest rate assuming all validators are participating, assuming total deposits of 1 ETH. It corresponds to ~3.88% annual interest assuming 10 million participating ETH.
|
* The `BASE_REWARD_QUOTIENT` constant is the per-slot interest rate assuming all validators are participating, assuming total deposits of 1 ETH. It corresponds to ~3.88% annual interest assuming 10 million participating ETH.
|
||||||
* At most `1/MAX_VALIDATOR_CHURN_QUOTIENT` of the validators can change during each dynasty.
|
* At most `1/MAX_VALIDATOR_CHURN_QUOTIENT` of the validators can change during each dynasty.
|
||||||
|
|
||||||
**Status codes**
|
**Validator status codes**
|
||||||
|
|
||||||
| Status code | Value |
|
| Name | Value |
|
||||||
| - | :-: |
|
| - | :-: |
|
||||||
| `PENDING_LOG_IN` | `0` |
|
| `PENDING_ACTIVATION` | `0` |
|
||||||
| `LOGGED_IN` | `1` |
|
| `ACTIVE` | `1` |
|
||||||
| `PENDING_EXIT` | `2` |
|
| `PENDING_EXIT` | `2` |
|
||||||
| `PENDING_WITHDRAW` | `3` |
|
| `PENDING_WITHDRAW` | `3` |
|
||||||
| `WITHDRAWN` | `4` |
|
| `WITHDRAWN` | `4` |
|
||||||
| `PENALIZED` | `128` |
|
| `PENALIZED` | `127` |
|
||||||
| `ENTRY` | `1` |
|
|
||||||
| `EXIT` | `2` |
|
**Special record types**
|
||||||
|
|
||||||
|
| Name | Value |
|
||||||
|
| - | :-: |
|
||||||
|
| `LOGOUT` | `0` |
|
||||||
|
| `CASPER_SLASHING` | `1` |
|
||||||
|
|
||||||
|
**Validator set delta flags**
|
||||||
|
|
||||||
|
| Name | Value |
|
||||||
|
| - | :-: |
|
||||||
|
| `ENTRY` | `0` |
|
||||||
|
| `EXIT` | `1` |
|
||||||
|
|
||||||
### PoW chain registration contract
|
### PoW chain registration contract
|
||||||
|
|
||||||
|
@ -133,7 +146,7 @@ An `AttestationSignedData` has the following fields:
|
||||||
'parent_hashes': ['hash32'],
|
'parent_hashes': ['hash32'],
|
||||||
# Shard block hash
|
# Shard block hash
|
||||||
'shard_block_hash': 'hash32',
|
'shard_block_hash': 'hash32',
|
||||||
# Slot of last justified block
|
# Slot of last justified block referenced in the attestation
|
||||||
'justified_slot': 'int64'
|
'justified_slot': 'int64'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -157,8 +170,6 @@ The `ActiveState` has the following fields:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Most recent 2 * CYCLE_LENGTH block hashes, oldest to newest
|
|
||||||
'recent_block_hashes': ['hash32'],
|
|
||||||
# Attestations not yet processed
|
# Attestations not yet processed
|
||||||
'pending_attestations': [AttestationRecord],
|
'pending_attestations': [AttestationRecord],
|
||||||
# Specials not yet been processed
|
# Specials not yet been processed
|
||||||
|
@ -219,7 +230,7 @@ A `ValidatorRecord` has the following fields:
|
||||||
# RANDAO commitment
|
# RANDAO commitment
|
||||||
'randao_commitment': 'hash32',
|
'randao_commitment': 'hash32',
|
||||||
# Balance
|
# Balance
|
||||||
'balance': 'int128',
|
'balance': 'int64',
|
||||||
# Status code
|
# Status code
|
||||||
'status': 'int8',
|
'status': 'int8',
|
||||||
# Slot when validator exited (or 0)
|
# Slot when validator exited (or 0)
|
||||||
|
@ -300,7 +311,7 @@ We start off by defining some helper algorithms. First, the function that select
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_active_validator_indices(validators):
|
def get_active_validator_indices(validators):
|
||||||
return [i for i, v in enumerate(validators) if v.status == LOGGED_IN]
|
return [i for i, v in enumerate(validators) if v.status == ACTIVE]
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, a function that shuffles this list:
|
Now, a function that shuffles this list:
|
||||||
|
@ -448,8 +459,8 @@ def add_validator(validators, pubkey, proof_of_possession, withdrawal_shard,
|
||||||
withdrawal_shard=withdrawal_shard,
|
withdrawal_shard=withdrawal_shard,
|
||||||
withdrawal_address=withdrawal_address,
|
withdrawal_address=withdrawal_address,
|
||||||
randao_commitment=randao_commitment,
|
randao_commitment=randao_commitment,
|
||||||
balance=DEPOSIT_SIZE, # in WEI
|
balance=DEPOSIT_SIZE * GWEI_PER_ETH, # in Gwei
|
||||||
status=PENDING_LOG_IN,
|
status=PENDING_ACTIVATION,
|
||||||
exit_slot=0
|
exit_slot=0
|
||||||
)
|
)
|
||||||
index = min_empty_validator(validators)
|
index = min_empty_validator(validators)
|
||||||
|
@ -496,7 +507,7 @@ For each one of these attestations:
|
||||||
* Verify that `len(attester_bitfield) == ceil_div8(len(attestation_indices))`, where `ceil_div8 = (x + 7) // 8`. Verify that bits `len(attestation_indices)....` and higher, if present (i.e. `len(attestation_indices)` is not a multiple of 8), are all zero
|
* Verify that `len(attester_bitfield) == ceil_div8(len(attestation_indices))`, where `ceil_div8 = (x + 7) // 8`. Verify that bits `len(attestation_indices)....` and higher, if present (i.e. `len(attestation_indices)` is not a multiple of 8), are all zero
|
||||||
* Derive a group public key by adding the public keys of all of the attesters in `attestation_indices` for whom the corresponding bit in `attester_bitfield` (the ith bit is `(attester_bitfield[i // 8] >> (7 - (i %8))) % 2`) equals 1
|
* Derive a group public key by adding the public keys of all of the attesters in `attestation_indices` for whom the corresponding bit in `attester_bitfield` (the ith bit is `(attester_bitfield[i // 8] >> (7 - (i %8))) % 2`) equals 1
|
||||||
* Let `version = pre_fork_version if slot < fork_slot_number else post_fork_version`.
|
* Let `version = pre_fork_version if slot < fork_slot_number else post_fork_version`.
|
||||||
* Verify that `aggregate_sig` verifies using the group pubkey generated and the serialized form of `AttestationSignedData(version, slot, parent_hashes, shard, shard_block_hash, justified_slot)` as the message.
|
* Verify that `aggregate_sig` verifies using the group pubkey generated and the serialized form of `AttestationSignedData(version, slot, shard, parent_hashes, shard_block_hash, justified_slot)` as the message.
|
||||||
|
|
||||||
Extend the list of `AttestationRecord` objects in the `active_state` with those included in the block, ordering the new additions in the same order as they came in the block. Similarly extend the list of `SpecialRecord` objects in the `active_state` with those included in the block.
|
Extend the list of `AttestationRecord` objects in the `active_state` with those included in the block, ordering the new additions in the same order as they came in the block. Similarly extend the list of `SpecialRecord` objects in the `active_state` with those included in the block.
|
||||||
|
|
||||||
|
@ -526,7 +537,7 @@ For every `(shard, shard_block_hash)` tuple:
|
||||||
#### Balance recalculations related to FFG rewards
|
#### Balance recalculations related to FFG rewards
|
||||||
|
|
||||||
* Let `total_balance` be the total balance of active validators.
|
* Let `total_balance` be the total balance of active validators.
|
||||||
* Let `total_balance_in_eth = total_balance // 10**9`.
|
* Let `total_balance_in_eth = total_balance // GWEI_PER_ETH.
|
||||||
* Let `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_balance_in_eth)`. (The per-slot maximum interest rate is `1/reward_quotient`.)
|
* Let `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_balance_in_eth)`. (The per-slot maximum interest rate is `1/reward_quotient`.)
|
||||||
* Let `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2`. (The portion lost by offline validators after `D` slots is about `D*D/2/quadratic_penalty_quotient`.)
|
* Let `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2`. (The portion lost by offline validators after `D` slots is about `D*D/2/quadratic_penalty_quotient`.)
|
||||||
* Let `time_since_finality = block.slot - last_finalized_slot`.
|
* Let `time_since_finality = block.slot - last_finalized_slot`.
|
||||||
|
@ -561,8 +572,8 @@ In addition, validators with `status == PENALIZED` lose `B // reward_quotient +
|
||||||
|
|
||||||
For each `SpecialRecord` `obj` in `active_state.pending_specials`:
|
For each `SpecialRecord` `obj` in `active_state.pending_specials`:
|
||||||
|
|
||||||
* **[covers logouts]**: If `obj.kind == 0`, interpret `data[0]` as a validator index as an `int32` and `data[1]` as a signature. If `BLSVerify(pubkey=validators[data[0]].pubkey, msg=hash("bye bye"), sig=data[1])`, and `validators[i].status == LOGGED_IN`, set `validators[i].status = PENDING_EXIT` and `validators[i].exit_slot = current_slot`
|
* **[covers logouts]**: If `obj.type == LOGOUT`, interpret `data[0]` as a validator index as an `int32` and `data[1]` as a signature. If `BLSVerify(pubkey=validators[data[0]].pubkey, msg=hash(LOGOUT_MESSAGE), sig=data[1])`, and `validators[i].status == ACTIVE`, set `validators[i].status = PENDING_EXIT` and `validators[i].exit_slot = current_slot`
|
||||||
* **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == 1`, interpret `data[0]` as a list of concatenated `int32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `inds` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `inds`, set their end dynasty to equal the current dynasty plus 1, and if its `status` does not equal `PENALIZED`, then:
|
* **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.type == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `int32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, set their end dynasty to equal the current dynasty plus 1, and if its `status` does not equal `PENALIZED`, then:
|
||||||
|
|
||||||
1. Set its `exit_slot` to equal the current `slot`
|
1. Set its `exit_slot` to equal the current `slot`
|
||||||
2. Set its `status` to `PENALIZED`
|
2. Set its `status` to `PENALIZED`
|
||||||
|
@ -594,14 +605,14 @@ def change_validators(validators):
|
||||||
total_balance = sum([v.balance for i, v in enumerate(validators) if i in active_validators])
|
total_balance = sum([v.balance for i, v in enumerate(validators) if i in active_validators])
|
||||||
# The maximum total wei that can deposit+withdraw
|
# The maximum total wei that can deposit+withdraw
|
||||||
max_allowable_change = max(
|
max_allowable_change = max(
|
||||||
DEPOSIT_SIZE * 2,
|
2 * DEPOSIT_SIZE GWEI_PER_ETH,
|
||||||
total_balance // MAX_VALIDATOR_CHURN_QUOTIENT
|
total_balance // MAX_VALIDATOR_CHURN_QUOTIENT
|
||||||
)
|
)
|
||||||
# Go through the list start to end depositing+withdrawing as many as possible
|
# Go through the list start to end depositing+withdrawing as many as possible
|
||||||
total_changed = 0
|
total_changed = 0
|
||||||
for i in range(len(validators)):
|
for i in range(len(validators)):
|
||||||
if validators[i].status == PENDING_LOG_IN:
|
if validators[i].status == PENDING_ACTIVATION:
|
||||||
validators[i].status = LOGGED_IN
|
validators[i].status = ACTIVE
|
||||||
total_changed += DEPOSIT_SIZE
|
total_changed += DEPOSIT_SIZE
|
||||||
add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, ENTRY)
|
add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, ENTRY)
|
||||||
if validators[i].status == PENDING_EXIT:
|
if validators[i].status == PENDING_EXIT:
|
||||||
|
|
Loading…
Reference in New Issue