Merge pull request #1868 from ethereum/dev

Release v0.12.1
This commit is contained in:
Danny Ryan 2020-06-02 17:40:51 -06:00 committed by GitHub
commit 52582662f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 197 additions and 139 deletions

View File

@ -76,8 +76,8 @@ BLS_WITHDRAWAL_PREFIX: 0x00
# Time parameters
# ---------------------------------------------------------------
# 86400 seconds (1 day)
MIN_GENESIS_DELAY: 86400
# 172800 seconds (2 days)
GENESIS_DELAY: 172800
# 12 seconds
SECONDS_PER_SLOT: 12
# 2**0 (= 1) slots 12 seconds

View File

@ -77,7 +77,7 @@ BLS_WITHDRAWAL_PREFIX: 0x00
# Time parameters
# ---------------------------------------------------------------
# [customized] Faster to spin up testnets, but does not give validator reasonable warning time for genesis
MIN_GENESIS_DELAY: 300
GENESIS_DELAY: 300
# [customized] Faster for testing purposes
SECONDS_PER_SLOT: 6
# 2**0 (= 1) slots 6 seconds

View File

@ -218,7 +218,7 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
| `MIN_GENESIS_DELAY` | `86400` | seconds | 1 day |
| `GENESIS_DELAY` | `172800` | seconds | 2 days |
| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds |
| `SECONDS_PER_ETH1_BLOCK` | `14` | seconds | 14 seconds |
| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds |
@ -1137,7 +1137,7 @@ Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 b
- `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash`
- `deposits` is the sequence of all deposits, ordered chronologically, up to (and including) the block with hash `eth1_block_hash`
Eth1 blocks must only be considered once they are at least `SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE` seconds old (i.e. `eth1_timestamp + SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= current_unix_time`). Due to this constraint, if `MIN_GENESIS_DELAY < SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE`, then the `genesis_time` can happen before the time/state is first known. Values should be configured to avoid this case.
Eth1 blocks must only be considered once they are at least `SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE` seconds old (i.e. `eth1_timestamp + SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= current_unix_time`). Due to this constraint, if `GENESIS_DELAY < SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE`, then the `genesis_time` can happen before the time/state is first known. Values should be configured to avoid this case.
```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
@ -1149,7 +1149,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
epoch=GENESIS_EPOCH,
)
state = BeaconState(
genesis_time=eth1_timestamp - eth1_timestamp % MIN_GENESIS_DELAY + 2 * MIN_GENESIS_DELAY,
genesis_time=eth1_timestamp + GENESIS_DELAY,
fork=fork,
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=len(deposits)),
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),

View File

@ -150,7 +150,7 @@ def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
elif block.slot == slot:
return root
else:
# root is older than queried slot, thus a skip slot. Return earliest root prior to slot
# root is older than queried slot, thus a skip slot. Return most recent root prior to slot
return root
```
@ -285,7 +285,7 @@ def validate_on_attestation(store: Store, attestation: Attestation) -> None:
# Attestations must not be for blocks in the future. If not, the attestation should not be considered
assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot
# FFG and LMD vote must be consistent with each other
# LMD vote must be consistent with FFG vote target
target_slot = compute_start_slot_at_epoch(target.epoch)
assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot)

View File

@ -150,6 +150,7 @@ This section outlines constants that are used in this spec.
| Name | Value | Description |
|---|---|---|
| `GOSSIP_MAX_SIZE` | `2**20` (= 1048576, 1 MiB) | The maximum allowed size of uncompressed gossip messages. |
| `MAX_REQUEST_BLOCKS` | `2**10` (= 1024) | Maximum number of blocks in a single request |
| `MAX_CHUNK_SIZE` | `2**20` (1048576, 1 MiB) | The maximum allowed size of uncompressed req/resp chunked responses. |
| `TTFB_TIMEOUT` | `5s` | The maximum time to wait for first byte of request response (time-to-first-byte). |
| `RESP_TIMEOUT` | `10s` | The maximum time for complete response transfer. |
@ -203,7 +204,7 @@ Topics are plain UTF-8 strings and are encoded on the wire as determined by prot
- `current_fork_version` is the fork version of the epoch of the message to be sent on the topic
- `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
- `Name` - see table below
- `Encoding` - the encoding strategy describes a specific representation of bytes that will be transmitted over the wire. See the [Encodings](#Encoding-strategies) section for further details.
- `Encoding` - the encoding strategy describes a specific representation of bytes that will be transmitted over the wire. See the [Encodings](#Encodings) section for further details.
*Note*: `ForkDigestValue` is composed of values that are not known until the genesis block/state are available. Due to this, clients SHOULD NOT subscribe to gossipsub topics until these genesis values are known.
@ -391,11 +392,11 @@ The `ErrorMessage` schema is:
```
(
error_message: String
error_message: List[byte, 256]
)
```
*Note*: The String type is encoded as UTF-8 bytes without NULL terminator when SSZ-encoded. As the `ErrorMessage` is not an SSZ-container, only the UTF-8 bytes will be sent when SSZ-encoded.
*Note*: By convention, the `error_message` is a sequence of bytes that MAY be interpreted as a UTF-8 string (for debugging purposes). Clients MUST treat as valid any byte sequences.
### Encoding strategies
@ -443,9 +444,9 @@ In case of an invalid input (header or payload), a reader MUST:
All messages that contain only a single field MUST be encoded directly as the type of that field and MUST NOT be encoded as an SSZ container.
Responses that are SSZ-lists (for example `[]SignedBeaconBlock`) send their
Responses that are SSZ-lists (for example `List[SignedBeaconBlock, ...]`) send their
constituents individually as `response_chunk`s. For example, the
`[]SignedBeaconBlock` response type sends zero or more `response_chunk`s. Each _successful_ `response_chunk` contains a single `SignedBeaconBlock` payload.
`List[SignedBeaconBlock, ...]` response type sends zero or more `response_chunk`s. Each _successful_ `response_chunk` contains a single `SignedBeaconBlock` payload.
### Messages
@ -468,9 +469,9 @@ The fields are, as seen by the client at the time of sending the message:
- `fork_digest`: The node's `ForkDigest` (`compute_fork_digest(current_fork_version, genesis_validators_root)`) where
- `current_fork_version` is the fork version at the node's current epoch defined by the wall-clock time (not necessarily the epoch to which the node is sync)
- `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
- `finalized_root`: `state.finalized_checkpoint.root` for the state corresponding to the head block.
- `finalized_root`: `state.finalized_checkpoint.root` for the state corresponding to the head block (Note this defaults to `Root(b'\x00' * 32)` for the genesis finalized checkpoint).
- `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the head block.
- `head_root`: The hash_tree_root root of the current head block.
- `head_root`: The `hash_tree_root` root of the current head block (`BeaconBlock`).
- `head_slot`: The slot of the block corresponding to the `head_root`.
The dialing client MUST send a `Status` request upon connection.
@ -528,7 +529,7 @@ Request Content:
Response Content:
```
(
[]SignedBeaconBlock
List[SignedBeaconBlock, MAX_REQUEST_BLOCKS]
)
```
@ -545,7 +546,7 @@ The response MUST consist of zero or more `response_chunk`. Each _successful_ `r
Clients MUST keep a record of signed blocks seen since the since the start of the weak subjectivity period and MUST support serving requests of blocks up to their own `head_block_root`.
Clients MUST respond with at least the first block that exists in the range, if they have it.
Clients MUST respond with at least the first block that exists in the range, if they have it, and no more than `MAX_REQUEST_BLOCKS` blocks.
The following blocks, where they exist, MUST be send in consecutive order.
@ -568,7 +569,7 @@ Request Content:
```
(
[]Root
List[Root, MAX_REQUEST_BLOCKS]
)
```
@ -576,12 +577,14 @@ Response Content:
```
(
[]SignedBeaconBlock
List[SignedBeaconBlock, MAX_REQUEST_BLOCKS]
)
```
Requests blocks by block root (= `hash_tree_root(SignedBeaconBlock.message)`). The response is a list of `SignedBeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks.
No more than `MAX_REQUEST_BLOCKS` may be requested at a time.
`BeaconBlocksByRoot` is primarily used to recover recent blocks (e.g. when receiving a block or attestation whose parent is unknown).
The request MUST be encoded as an SSZ-field.
@ -1052,7 +1055,7 @@ discv5 uses ENRs and we will presumably need to:
Although client software might very well be running locally prior to the solidification of the eth2 genesis state and block, clients cannot form valid ENRs prior to this point. ENRs contain `fork_digest` which utilizes the `genesis_validators_root` for a cleaner separation between chains so prior to knowing genesis, we cannot use `fork_digest` to cleanly find peers on our intended chain. Once genesis data is known, we can then form ENRs and safely find peers.
When using an eth1 deposit contract for deposits, `fork_digest` will be known at least `MIN_GENESIS_DELAY` (24 hours in mainnet configuration) before `genesis_time`, providing ample time to find peers and form initial connections and gossip subnets prior to genesis.
When using an eth1 deposit contract for deposits, `fork_digest` will be known `GENESIS_DELAY` (48hours in mainnet configuration) before `genesis_time`, providing ample time to find peers and form initial connections and gossip subnets prior to genesis.
## Compression/Encoding

View File

@ -40,6 +40,7 @@
- [`compute_shard_from_committee_index`](#compute_shard_from_committee_index)
- [`compute_offset_slots`](#compute_offset_slots)
- [`compute_updated_gasprice`](#compute_updated_gasprice)
- [`compute_committee_source_epoch`](#compute_committee_source_epoch)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_active_shard_count`](#get_active_shard_count)
- [`get_online_validator_indices`](#get_online_validator_indices)
@ -52,8 +53,9 @@
- [`get_latest_slot_for_shard`](#get_latest_slot_for_shard)
- [`get_offset_slots`](#get_offset_slots)
- [Predicates](#predicates)
- [`verify_attestation_custody`](#verify_attestation_custody)
- [Updated `is_valid_indexed_attestation`](#updated-is_valid_indexed_attestation)
- [`is_shard_attestation`](#is_shard_attestation)
- [`is_on_time_attestation`](#is_on_time_attestation)
- [`is_winning_attestation`](#is_winning_attestation)
- [`optional_aggregate_verify`](#optional_aggregate_verify)
- [`optional_fast_aggregate_verify`](#optional_fast_aggregate_verify)
@ -61,12 +63,14 @@
- [Operations](#operations)
- [New Attestation processing](#new-attestation-processing)
- [`validate_attestation`](#validate_attestation)
- [Updated `process_attestation`](#updated-process_attestation)
- [Shard transition processing](#shard-transition-processing)
- [`apply_shard_transition`](#apply_shard_transition)
- [`process_crosslink_for_shard`](#process_crosslink_for_shard)
- [`process_crosslinks`](#process_crosslinks)
- [`process_attestation`](#process_attestation)
- [`verify_empty_shard_transition`](#verify_empty_shard_transition)
- [`process_shard_transitions`](#process_shard_transitions)
- [New Attester slashing processing](#new-attester-slashing-processing)
- [Shard transition false positives](#shard-transition-false-positives)
- [Light client processing](#light-client-processing)
- [Epoch transition](#epoch-transition)
- [Custody game updates](#custody-game-updates)
@ -130,7 +134,7 @@ class AttestationData(Container):
source: Checkpoint
target: Checkpoint
# Current-slot shard block root
head_shard_root: Root
shard_head_root: Root
# Shard transition root
shard_transition_root: Root
```
@ -153,6 +157,7 @@ class PendingAttestation(Container):
data: AttestationData
inclusion_delay: Slot
proposer_index: ValidatorIndex
# Phase 1
crosslink_success: boolean
```
@ -302,6 +307,7 @@ class ShardBlock(Container):
shard_parent_root: Root
beacon_parent_root: Root
slot: Slot
shard: Shard
proposer_index: ValidatorIndex
body: ByteList[MAX_SHARD_BLOCK_SIZE]
```
@ -321,6 +327,7 @@ class ShardBlockHeader(Container):
shard_parent_root: Root
beacon_parent_root: Root
slot: Slot
shard: Shard
proposer_index: ValidatorIndex
body_root: Root
```
@ -413,7 +420,7 @@ def unpack_compact_validator(compact_validator: uint64) -> Tuple[ValidatorIndex,
```python
def committee_to_compact_committee(state: BeaconState, committee: Sequence[ValidatorIndex]) -> CompactCommittee:
"""
Given a state and a list of validator indices, outputs the CompactCommittee representing them.
Given a state and a list of validator indices, outputs the ``CompactCommittee`` representing them.
"""
validators = [state.validators[i] for i in committee]
compact_validators = [
@ -445,17 +452,30 @@ def compute_offset_slots(start_slot: Slot, end_slot: Slot) -> Sequence[Slot]:
#### `compute_updated_gasprice`
```python
def compute_updated_gasprice(prev_gasprice: Gwei, length: uint8) -> Gwei:
if length > TARGET_SHARD_BLOCK_SIZE:
delta = (prev_gasprice * (length - TARGET_SHARD_BLOCK_SIZE)
def compute_updated_gasprice(prev_gasprice: Gwei, shard_block_length: uint8) -> Gwei:
if shard_block_length > TARGET_SHARD_BLOCK_SIZE:
delta = (prev_gasprice * (shard_block_length - TARGET_SHARD_BLOCK_SIZE)
// TARGET_SHARD_BLOCK_SIZE // GASPRICE_ADJUSTMENT_COEFFICIENT)
return min(prev_gasprice + delta, MAX_GASPRICE)
else:
delta = (prev_gasprice * (TARGET_SHARD_BLOCK_SIZE - length)
delta = (prev_gasprice * (TARGET_SHARD_BLOCK_SIZE - shard_block_length)
// TARGET_SHARD_BLOCK_SIZE // GASPRICE_ADJUSTMENT_COEFFICIENT)
return max(prev_gasprice, MIN_GASPRICE + delta) - delta
```
#### `compute_committee_source_epoch`
```python
def compute_committee_source_epoch(epoch: Epoch, period: uint64) -> Epoch:
"""
Return the source epoch for computing the committee.
"""
source_epoch = epoch - epoch % period
if source_epoch >= period:
source_epoch -= period # `period` epochs lookahead
return source_epoch
```
### Beacon state accessors
#### `get_active_shard_count`
@ -477,9 +497,10 @@ def get_online_validator_indices(state: BeaconState) -> Set[ValidatorIndex]:
```python
def get_shard_committee(beacon_state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
source_epoch = epoch - epoch % SHARD_COMMITTEE_PERIOD
if source_epoch >= SHARD_COMMITTEE_PERIOD:
source_epoch -= SHARD_COMMITTEE_PERIOD
"""
Return the shard committee of the given ``epoch`` of the given ``shard``.
"""
source_epoch = compute_committee_source_epoch(epoch, SHARD_COMMITTEE_PERIOD)
active_validator_indices = get_active_validator_indices(beacon_state, source_epoch)
seed = get_seed(beacon_state, source_epoch, DOMAIN_SHARD_COMMITTEE)
active_shard_count = get_active_shard_count(beacon_state)
@ -495,9 +516,10 @@ def get_shard_committee(beacon_state: BeaconState, epoch: Epoch, shard: Shard) -
```python
def get_light_client_committee(beacon_state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
source_epoch = epoch - epoch % LIGHT_CLIENT_COMMITTEE_PERIOD
if source_epoch >= LIGHT_CLIENT_COMMITTEE_PERIOD:
source_epoch -= LIGHT_CLIENT_COMMITTEE_PERIOD
"""
Return the light client committee of no more than ``TARGET_COMMITTEE_SIZE`` validators.
"""
source_epoch = compute_committee_source_epoch(epoch, LIGHT_CLIENT_COMMITTEE_PERIOD)
active_validator_indices = get_active_validator_indices(beacon_state, source_epoch)
seed = get_seed(beacon_state, source_epoch, DOMAIN_LIGHT_CLIENT)
return compute_committee(
@ -554,11 +576,45 @@ def get_latest_slot_for_shard(state: BeaconState, shard: Shard) -> Slot:
```python
def get_offset_slots(state: BeaconState, shard: Shard) -> Sequence[Slot]:
return compute_offset_slots(state.shard_states[shard].slot, state.slot)
"""
Return the offset slots of the given ``shard`` between that latest included slot and current slot.
"""
return compute_offset_slots(get_latest_slot_for_shard(state, shard), state.slot)
```
### Predicates
#### `verify_attestation_custody`
```python
def verify_attestation_custody(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
"""
Check if ``indexed_attestation`` has valid signature against non-empty custody bits.
"""
attestation = indexed_attestation.attestation
aggregation_bits = attestation.aggregation_bits
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch)
all_pubkeys = []
all_signing_roots = []
for block_index, custody_bits in enumerate(attestation.custody_bits_blocks):
assert len(custody_bits) == len(indexed_attestation.committee)
for participant, aggregation_bit, custody_bit in zip(
indexed_attestation.committee, aggregation_bits, custody_bits
):
if aggregation_bit:
all_pubkeys.append(state.validators[participant].pubkey)
# Note: only 2N distinct message hashes
attestation_wrapper = AttestationCustodyBitWrapper(
attestation_data_root=hash_tree_root(attestation.data),
block_index=block_index,
bit=custody_bit,
)
all_signing_roots.append(compute_signing_root(attestation_wrapper, domain))
else:
assert not custody_bit
return bls.AggregateVerify(all_pubkeys, all_signing_roots, signature=attestation.signature)
```
#### Updated `is_valid_indexed_attestation`
Note that this replaces the Phase 0 `is_valid_indexed_attestation`.
@ -569,53 +625,34 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe
Check if ``indexed_attestation`` has valid indices and signature.
"""
# Verify aggregate signature
all_pubkeys = []
all_signing_roots = []
attestation = indexed_attestation.attestation
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch)
aggregation_bits = attestation.aggregation_bits
if not any(aggregation_bits) or len(aggregation_bits) != len(indexed_attestation.committee):
return False
if len(attestation.custody_bits_blocks) == 0:
# fall back on phase0 behavior if there is no shard data.
for participant, abit in zip(indexed_attestation.committee, aggregation_bits):
if abit:
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch)
all_pubkeys = []
for participant, aggregation_bit in zip(indexed_attestation.committee, aggregation_bits):
if aggregation_bit:
all_pubkeys.append(state.validators[participant].pubkey)
signing_root = compute_signing_root(indexed_attestation.attestation.data, domain)
return bls.FastAggregateVerify(all_pubkeys, signing_root, signature=attestation.signature)
else:
for i, custody_bits in enumerate(attestation.custody_bits_blocks):
assert len(custody_bits) == len(indexed_attestation.committee)
for participant, abit, cbit in zip(indexed_attestation.committee, aggregation_bits, custody_bits):
if abit:
all_pubkeys.append(state.validators[participant].pubkey)
# Note: only 2N distinct message hashes
attestation_wrapper = AttestationCustodyBitWrapper(
attestation_data_root=hash_tree_root(attestation.data),
block_index=i,
bit=cbit
)
all_signing_roots.append(compute_signing_root(attestation_wrapper, domain))
else:
assert not cbit
return bls.AggregateVerify(all_pubkeys, all_signing_roots, signature=attestation.signature)
return verify_attestation_custody(state, indexed_attestation)
```
#### `is_shard_attestation`
#### `is_on_time_attestation`
```python
def is_shard_attestation(state: BeaconState,
attestation: Attestation,
committee_index: CommitteeIndex) -> bool:
if not (
attestation.data.index == committee_index
and attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY == state.slot # Must be on-time attestation
def is_on_time_attestation(state: BeaconState,
attestation: Attestation) -> bool:
"""
Check if the given attestation is on-time.
"""
# TODO: MIN_ATTESTATION_INCLUSION_DELAY should always be 1
):
return False
return True
return attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY == state.slot
```
#### `is_winning_attestation`
@ -675,7 +712,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_eth1_data(state, block.body)
process_light_client_signatures(state, block.body)
process_operations(state, block.body)
verify_shard_transition_false_positives(state, block.body)
```
#### Operations
@ -699,7 +735,7 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
# See custody game spec.
process_custody_game_operations(state, body)
process_crosslinks(state, body.shard_transitions, body.attestations)
process_shard_transitions(state, body.shard_transitions, body.attestations)
# TODO process_operations(body.shard_receipt_proofs, process_shard_receipt_proofs)
```
@ -730,7 +766,7 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
# Type 1: on-time attestations, the custody bits should be non-empty.
if attestation.custody_bits_blocks != []:
# Ensure on-time attestation
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY == state.slot
assert is_on_time_attestation(state, attestation)
# Correct data root count
assert len(attestation.custody_bits_blocks) == len(get_offset_slots(state, shard))
# Correct parent block root
@ -746,6 +782,27 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
```
###### Updated `process_attestation`
```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
validate_attestation(state, attestation)
# Store pending attestation for epoch processing
pending_attestation = PendingAttestation(
aggregation_bits=attestation.aggregation_bits,
data=attestation.data,
inclusion_delay=state.slot - attestation.data.slot,
proposer_index=get_beacon_proposer_index(state),
crosslink_success=False, # To be filled in during process_shard_transitions
)
if attestation.data.target.epoch == get_current_epoch(state):
state.current_epoch_attestations.append(pending_attestation)
else:
state.previous_epoch_attestations.append(pending_attestation)
```
##### Shard transition processing
###### `apply_shard_transition`
```python
@ -767,22 +824,23 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
proposers = []
prev_gasprice = state.shard_states[shard].gasprice
shard_parent_root = state.shard_states[shard].latest_block_root
for i in range(len(offset_slots)):
for i, offset_slot in enumerate(offset_slots):
shard_block_length = transition.shard_block_lengths[i]
shard_state = transition.shard_states[i]
# Verify correct calculation of gas prices and slots
assert shard_state.gasprice == compute_updated_gasprice(prev_gasprice, shard_block_length)
assert shard_state.slot == offset_slots[i]
assert shard_state.slot == offset_slot
# Collect the non-empty proposals result
is_empty_proposal = shard_block_length == 0
if not is_empty_proposal:
proposal_index = get_shard_proposer_index(state, offset_slots[i], shard)
proposal_index = get_shard_proposer_index(state, offset_slot, shard)
# Reconstruct shard headers
header = ShardBlockHeader(
shard_parent_root=shard_parent_root,
beacon_parent_root=get_block_root_at_slot(state, offset_slots[i]),
beacon_parent_root=get_block_root_at_slot(state, offset_slot),
slot=offset_slot,
shard=shard,
proposer_index=proposal_index,
slot=offset_slots[i],
body_root=transition.shard_data_roots[i]
)
shard_parent_root = hash_tree_root(header)
@ -801,7 +859,7 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
# Save updated state
state.shard_states[shard] = transition.shard_states[len(transition.shard_states) - 1]
state.shard_states[shard].slot = state.slot - 1
state.shard_states[shard].slot = compute_previous_slot(state.slot)
```
###### `process_crosslink_for_shard`
@ -823,7 +881,7 @@ def process_crosslink_for_shard(state: BeaconState,
for attestation in transition_attestations:
participants = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
transition_participants = transition_participants.union(participants)
assert attestation.data.head_shard_root == shard_transition.shard_data_roots[
assert attestation.data.shard_head_root == shard_transition.shard_data_roots[
len(shard_transition.shard_data_roots) - 1
]
@ -872,13 +930,12 @@ def process_crosslinks(state: BeaconState,
for committee_index in map(CommitteeIndex, range(committee_count)):
shard = compute_shard_from_committee_index(state, committee_index, state.slot)
# All attestations in the block for this committee/shard and current slot
shard_transition = shard_transitions[shard]
shard_attestations = [
attestation for attestation in attestations
if is_shard_attestation(state, attestation, committee_index)
if is_on_time_attestation(state, attestation) and attestation.data.index == committee_index
]
winning_root = process_crosslink_for_shard(state, committee_index, shard_transition, shard_attestations)
winning_root = process_crosslink_for_shard(state, committee_index, shard_transitions[shard], shard_attestations)
if winning_root != Root():
# Mark relevant pending attestations as creating a successful crosslink
for pending_attestation in state.current_epoch_attestations:
@ -886,23 +943,30 @@ def process_crosslinks(state: BeaconState,
pending_attestation.crosslink_success = True
```
###### `process_attestation`
###### `verify_empty_shard_transition`
```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
validate_attestation(state, attestation)
# Store pending attestation for epoch processing
pending_attestation = PendingAttestation(
aggregation_bits=attestation.aggregation_bits,
data=attestation.data,
inclusion_delay=state.slot - attestation.data.slot,
proposer_index=get_beacon_proposer_index(state),
crosslink_success=False, # To be filled in during process_crosslinks
)
if attestation.data.target.epoch == get_current_epoch(state):
state.current_epoch_attestations.append(pending_attestation)
else:
state.previous_epoch_attestations.append(pending_attestation)
def verify_empty_shard_transition(state: BeaconState, shard_transitions: Sequence[ShardTransition]) -> bool:
"""
Verify that a `shard_transition` in a block is empty if an attestation was not processed for it.
"""
for shard in range(get_active_shard_count(state)):
if state.shard_states[shard].slot != compute_previous_slot(state.slot):
if shard_transitions[shard] != ShardTransition():
return False
return True
```
###### `process_shard_transitions`
```python
def process_shard_transitions(state: BeaconState,
shard_transitions: Sequence[ShardTransition],
attestations: Sequence[Attestation]) -> None:
# Process crosslinks
process_crosslinks(state, shard_transitions, attestations)
# Verify the empty proposal shard states
assert verify_empty_shard_transition(state, shard_transitions)
```
##### New Attester slashing processing
@ -910,11 +974,9 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
```python
def get_indices_from_committee(
committee: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE],
bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE]:
bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Sequence[ValidatorIndex]:
assert len(bits) == len(committee)
return List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE](
[validator_index for i, validator_index in enumerate(committee) if bits[i]]
)
return [validator_index for i, validator_index in enumerate(committee) if bits[i]]
```
```python
@ -947,16 +1009,6 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla
assert slashed_any
```
#### Shard transition false positives
```python
def verify_shard_transition_false_positives(state: BeaconState, block_body: BeaconBlockBody) -> None:
# Verify that a `shard_transition` in a block is empty if an attestation was not processed for it
for shard in range(get_active_shard_count(state)):
if state.shard_states[shard].slot != state.slot - 1:
assert block_body.shard_transitions[shard] == ShardTransition()
```
#### Light client processing
```python
@ -997,7 +1049,7 @@ def process_epoch(state: BeaconState) -> None:
#### Custody game updates
`process_reveal_deadlines` and `process_custody_final_updates` are defined in [the Custody Game spec](./1_custody-game.md),
`process_reveal_deadlines` and `process_custody_final_updates` are defined in [the Custody Game spec](./custody-game.md),
#### Online-tracking
@ -1018,7 +1070,9 @@ def process_online_tracking(state: BeaconState) -> None:
```python
def process_light_client_committee_updates(state: BeaconState) -> None:
# Update light client committees
"""
Update light client committees.
"""
if get_current_epoch(state) % LIGHT_CLIENT_COMMITTEE_PERIOD == 0:
state.current_light_committee = state.next_light_committee
new_committee = get_light_client_committee(state, get_current_epoch(state) + LIGHT_CLIENT_COMMITTEE_PERIOD)

View File

@ -50,6 +50,7 @@ def verify_shard_block_message(beacon_state: BeaconState,
shard: Shard) -> bool:
assert block.shard_parent_root == shard_state.latest_block_root
assert block.slot == slot
assert block.shard == shard
assert block.proposer_index == get_shard_proposer_index(beacon_state, slot, shard)
assert 0 < len(block.body) <= MAX_SHARD_BLOCK_SIZE
return True
@ -70,22 +71,23 @@ def verify_shard_block_signature(beacon_state: BeaconState,
def shard_state_transition(beacon_state: BeaconState,
shard_state: ShardState,
block: ShardBlock) -> None:
# Update shard state
"""
Update ``shard_state`` with shard ``block`` and ``beacon_state`.
"""
shard_state.slot = block.slot
prev_gasprice = shard_state.gasprice
shard_state.gasprice = compute_updated_gasprice(prev_gasprice, len(block.body))
if len(block.body) == 0:
latest_block_root = shard_state.latest_block_root
else:
latest_block_root = hash_tree_root(block)
shard_state.latest_block_root = latest_block_root
shard_state.transition_digest = compute_shard_transition_digest(
beacon_state,
shard_state,
block.beacon_parent_root,
block.body,
hash_tree_root(block.body),
)
shard_state.gasprice = compute_updated_gasprice(prev_gasprice, len(block.body))
shard_state.slot = block.slot
shard_state.latest_block_root = latest_block_root
```
We have a pure function `get_post_shard_state` for describing the fraud proof verification and honest validator behavior.
@ -268,12 +270,8 @@ def get_shard_transition(beacon_state: BeaconState,
shard: Shard,
shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition:
offset_slots = get_offset_slots(beacon_state, shard)
start_slot = offset_slots[0]
proposals, shard_states, shard_data_roots = get_shard_state_transition_result(beacon_state, shard, shard_blocks)
assert len(proposals) > 0
assert len(shard_data_roots) > 0
shard_block_lengths = []
proposer_signatures = []
for proposal in proposals:
@ -287,7 +285,7 @@ def get_shard_transition(beacon_state: BeaconState,
proposer_signature_aggregate = NO_SIGNATURE
return ShardTransition(
start_slot=start_slot,
start_slot=offset_slots[0],
shard_block_lengths=shard_block_lengths,
shard_data_roots=shard_data_roots,
shard_states=shard_states,

View File

@ -1 +1 @@
0.12.0
0.12.1

View File

@ -21,7 +21,7 @@ def test_initialize_beacon_state_from_eth1(spec):
# initialize beacon_state
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
assert state.genesis_time == eth1_timestamp - eth1_timestamp % spec.MIN_GENESIS_DELAY + 2 * spec.MIN_GENESIS_DELAY
assert state.genesis_time == eth1_timestamp + spec.GENESIS_DELAY
assert len(state.validators) == deposit_count
assert state.eth1_data.deposit_root == deposit_root
assert state.eth1_data.deposit_count == deposit_count
@ -57,7 +57,7 @@ def test_initialize_beacon_state_some_small_balances(spec):
# initialize beacon_state
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
assert state.genesis_time == eth1_timestamp - eth1_timestamp % spec.MIN_GENESIS_DELAY + 2 * spec.MIN_GENESIS_DELAY
assert state.genesis_time == eth1_timestamp + spec.GENESIS_DELAY
assert len(state.validators) == small_deposit_count
assert state.eth1_data.deposit_root == deposit_root
assert state.eth1_data.deposit_count == len(deposits)

View File

@ -78,7 +78,7 @@ def build_attestation_data(spec, state, slot, index, shard_transition=None, on_t
if spec.fork == PHASE1:
if shard_transition is not None:
lastest_shard_data_root_index = len(shard_transition.shard_data_roots) - 1
attestation_data.head_shard_root = shard_transition.shard_data_roots[lastest_shard_data_root_index]
attestation_data.shard_head_root = shard_transition.shard_data_roots[lastest_shard_data_root_index]
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
else:
# No shard transition
@ -88,10 +88,10 @@ def build_attestation_data(spec, state, slot, index, shard_transition=None, on_t
next_slot(spec, temp_state)
shard_transition = spec.get_shard_transition(temp_state, shard, [])
lastest_shard_data_root_index = len(shard_transition.shard_data_roots) - 1
attestation_data.head_shard_root = shard_transition.shard_data_roots[lastest_shard_data_root_index]
attestation_data.shard_head_root = shard_transition.shard_data_roots[lastest_shard_data_root_index]
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
else:
attestation_data.head_shard_root = state.shard_states[shard].transition_digest
attestation_data.shard_head_root = state.shard_states[shard].transition_digest
attestation_data.shard_transition_root = spec.Root()
return attestation_data
@ -211,6 +211,9 @@ def sign_indexed_attestation(spec, state, indexed_attestation):
indexed_attestation.attestation.aggregation_bits,
)
data = indexed_attestation.attestation.data
if any(indexed_attestation.attestation.custody_bits_blocks):
sign_on_time_attestation(spec, state, indexed_attestation.attestation)
else:
indexed_attestation.attestation.signature = sign_aggregate_attestation(spec, state, data, participants)

View File

@ -38,6 +38,7 @@ def build_shard_block(spec,
shard_parent_root=shard_state.latest_block_root,
beacon_parent_root=beacon_parent_root,
slot=slot,
shard=shard,
proposer_index=proposer_index,
body=body,
)

View File

@ -1,9 +1,9 @@
from eth2spec.test.context import expect_assertion_error
def run_crosslinks_processing(spec, state, shard_transitions, attestations, valid=True):
def run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=True):
"""
Run ``process_attestation``, yielding:
Run ``process_shard_transitions``, yielding:
- pre-state ('pre')
- shard_transitions ('shard_transitions')
- attestations ('attestations')
@ -17,12 +17,12 @@ def run_crosslinks_processing(spec, state, shard_transitions, attestations, vali
# If the attestation is invalid, processing is aborted, and there is no post-state.
if not valid:
expect_assertion_error(lambda: spec.process_crosslinks(state, shard_transitions, attestations))
expect_assertion_error(lambda: spec.process_shard_transitions(state, shard_transitions, attestations))
yield 'post', None
return
# process crosslinks
spec.process_crosslinks(state, shard_transitions, attestations)
spec.process_shard_transitions(state, shard_transitions, attestations)
# yield post-state
yield 'post', state

View File

@ -1,6 +1,6 @@
from eth2spec.test.context import (
PHASE0, PHASE1,
spec_state_test, expect_assertion_error, always_bls, never_bls, with_all_phases, with_phases
spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
)
from eth2spec.test.helpers.attestations import sign_indexed_attestation
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
@ -89,7 +89,6 @@ def test_success_double(spec, state):
@with_all_phases
@spec_state_test
@never_bls
def test_success_surround(spec, state):
next_epoch_via_block(spec, state)

View File

@ -4,7 +4,7 @@ from eth2spec.test.context import (
spec_state_test,
always_bls,
)
from eth2spec.test.helpers.crosslinks import run_crosslinks_processing
from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing
from eth2spec.test.helpers.shard_block import (
build_attestation_with_shard_transition,
build_shard_block,
@ -46,7 +46,7 @@ def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True):
transition_to(spec, state, state.slot + target_len_offset_slot)
pre_shard_state = state.shard_states[shard]
yield from run_crosslinks_processing(spec, state, shard_transitions, [attestation], valid=valid)
yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation], valid=valid)
if valid:
# After state transition,

View File

@ -184,7 +184,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
if __name__ == "__main__":
gen_runner.run_generator("epoch_processing", [
create_provider('crosslinks', test_process_crosslinks, 'minimal'),
create_provider('final_updates', test_process_final_updates, 'minimal'),
...
])