Merge branch 'dev' into dankrad-custody-256bit-merging

This commit is contained in:
Hsiao-Wei Wang 2020-05-05 15:22:36 +08:00
commit ee4c866575
No known key found for this signature in database
GPG Key ID: 95B070122902DEA4
32 changed files with 900 additions and 332 deletions

View File

@ -79,16 +79,16 @@ jobs:
# Restore git repo at point close to target branch/revision, to speed up checkout
- restore_cache:
keys:
- v2-specs-repo-{{ .Branch }}-{{ .Revision }}
- v2-specs-repo-{{ .Branch }}-
- v2-specs-repo-
- v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- v3-specs-repo-{{ .Branch }}-
- v3-specs-repo-
- checkout
- run:
name: Clean up git repo to reduce cache size
command: git gc
# Save the git checkout as a cache, to make cloning next time faster.
- save_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
paths:
- ~/specs-repo
install_pyspec_test:
@ -97,7 +97,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_pyspec_cached_venv
- run:
name: Install pyspec requirements
@ -109,7 +109,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_pyspec_cached_venv
- run:
name: Run py-tests
@ -140,7 +140,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_pyspec_cached_venv
- run:
name: Run linter
@ -152,7 +152,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_deposit_contract_compiler_cached_venv
- run:
name: Install deposit contract compiler requirements
@ -164,7 +164,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_deposit_contract_tester_cached_venv
- run:
name: Install deposit contract tester requirements
@ -176,7 +176,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_deposit_contract_compiler_cached_venv
- run:
name: Run deposit contract compile test
@ -187,7 +187,7 @@ jobs:
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v2-specs-repo-{{ .Branch }}-{{ .Revision }}
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_deposit_contract_tester_cached_venv
- run:
name: Run deposit contract test

View File

@ -117,7 +117,7 @@ install_deposit_contract_compiler:
compile_deposit_contract:
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); . venv/bin/activate; \
python3.7 deposit_contract/compile.py contracts/validator_registration.vy
python3.7 deposit_contract/compile.py ../contracts/validator_registration.vy
test_compile_deposit_contract:
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); . venv/bin/activate; \

View File

@ -9,7 +9,7 @@ This repository hosts the current Eth2 specifications. Discussions about design
## Specs
Core specifications for Eth2 clients be found in [specs/](specs/). These are divided into phases. Each subsequent phase depends upon the prior. The current phases specified are:
Core specifications for Eth2 clients be found in [specs](specs/). These are divided into phases. Each subsequent phase depends upon the prior. The current phases specified are:
### Phase 0
* [The Beacon Chain](specs/phase0/beacon-chain.md)

View File

@ -161,6 +161,8 @@ DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
# Phase 1: Upgrade from Phase 0
# ---------------------------------------------------------------
PHASE_1_FORK_VERSION: 0x01000000
# [STUB]
PHASE_1_GENESIS_SLOT: 32
INITIAL_ACTIVE_SHARDS: 64
# Phase 1: General

View File

@ -162,6 +162,8 @@ DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
# ---------------------------------------------------------------
# [customized] for testnet distinction
PHASE_1_FORK_VERSION: 0x01000001
# [customized] for testing
PHASE_1_GENESIS_SLOT: 8
# [customized] reduced for testing
INITIAL_ACTIVE_SHARDS: 4

View File

@ -375,7 +375,7 @@ class PySpecCommand(Command):
specs/phase0/fork-choice.md
specs/phase1/custody-game.md
specs/phase1/beacon-chain.md
specs/phase1/fraud-proofs.md
specs/phase1/shard-transition.md
specs/phase1/fork-choice.md
specs/phase1/phase1-fork.md
"""
@ -499,7 +499,7 @@ setup(
"pycryptodome==3.9.4",
"py_ecc==2.0.0",
"dataclasses==0.6",
"remerkleable==0.1.12",
"remerkleable==0.1.13",
"ruamel.yaml==0.16.5",
"lru-dict==1.1.6"
]

View File

@ -684,14 +684,10 @@ def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationDa
```python
def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
"""
Check if ``indexed_attestation`` has valid indices and signature.
Check if ``indexed_attestation`` has sorted and unique indices and a valid aggregate signature.
"""
indices = indexed_attestation.attesting_indices
# Verify max number of indices
if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
return False
# Verify indices are sorted and unique
indices = indexed_attestation.attesting_indices
if not indices == sorted(set(indices)):
return False
# Verify aggregate signature
@ -1174,6 +1170,8 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
return state
```
*Note*: The ETH1 block with `eth1_timestamp` meeting the minimum genesis active validator count criteria can also occur before `MIN_GENESIS_TIME`.
### Genesis state
Let `genesis_state = candidate_state` whenever `is_valid_genesis_state(candidate_state) is True` for the first time.
@ -1195,7 +1193,7 @@ Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
## Beacon chain state transition function
The post-state corresponding to a pre-state `state` and a signed block `signed_block` is defined as `state_transition(state, signed_block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid.
The post-state corresponding to a pre-state `state` and a signed block `signed_block` is defined as `state_transition(state, signed_block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. State transitions that cause a `uint64` overflow or underflow are also considered invalid.
```python
def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> BeaconState:

View File

@ -273,13 +273,12 @@ def validate_on_attestation(store: Store, attestation: Attestation) -> None:
current_epoch = compute_epoch_at_slot(get_current_slot(store))
# Use GENESIS_EPOCH for previous when genesis to avoid underflow
previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
# If attestation target is from a future epoch, delay consideration until the epoch arrives
assert target.epoch in [current_epoch, previous_epoch]
assert target.epoch == compute_epoch_at_slot(attestation.data.slot)
# Attestations target be for a known block. If target block is unknown, delay consideration until the block is found
assert target.root in store.blocks
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives
assert get_current_slot(store) >= compute_start_slot_at_epoch(target.epoch)
# Attestations must be for a known block. If block is unknown, delay consideration until the block is found
assert attestation.data.beacon_block_root in store.blocks

View File

@ -105,6 +105,7 @@ It consists of four main sections:
- [Discovery](#discovery)
- [Why are we using discv5 and not libp2p Kademlia DHT?](#why-are-we-using-discv5-and-not-libp2p-kademlia-dht)
- [What is the difference between an ENR and a multiaddr, and why are we using ENRs?](#what-is-the-difference-between-an-enr-and-a-multiaddr-and-why-are-we-using-enrs)
- [Why do we not form ENRs and find peers until genesis block/state is known?](#why-do-we-not-form-enrs-and-find-peers-until-genesis-blockstate-is-known)
- [Compression/Encoding](#compressionencoding)
- [Why are we using SSZ for encoding?](#why-are-we-using-ssz-for-encoding)
- [Why are we compressing, and at which layers?](#why-are-we-compressing-and-at-which-layers)
@ -247,6 +248,8 @@ Topics are plain UTF-8 strings and are encoded on the wire as determined by prot
- `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.
*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.
Each gossipsub [message](https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.proto#L17-L24) has a maximum size of `GOSSIP_MAX_SIZE`. Clients MUST reject (fail validation) messages that are over this size limit. Likewise, clients MUST NOT emit or propagate messages larger than this limit.
The `message-id` of a gossipsub message MUST be:
@ -286,8 +289,8 @@ There are two primary global topics used to propagate beacon blocks and aggregat
- The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). If the `proposer_index` cannot immediately be verified against the expected shuffling, the block MAY be queued for later processing while proposers for the block's branch are calculated.
- `beacon_aggregate_and_proof` - This topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. (We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message` and `aggregate = aggregate_and_proof.aggregate`)
- `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot` (a client MAY queue future aggregates for processing at the appropriate slot).
- The aggregate attestation defined by `hash_tree_root(aggregate)` has _not_ already been seen (via aggregate gossip, within a verified block, or through the creation of an equivalent aggregate locally).
- The `aggregate` is the first valid aggregate received for the aggregator with index `aggregate_and_proof.aggregator_index` for the slot `aggregate.data.slot`.
- The valid aggregate attestation defined by `hash_tree_root(aggregate)` has _not_ already been seen (via aggregate gossip, within a verified block, or through the creation of an equivalent aggregate locally).
- The `aggregate` is the first valid aggregate received for the aggregator with index `aggregate_and_proof.aggregator_index` for the epoch `aggregate.data.target.epoch`.
- The block being voted for (`aggregate.data.beacon_block_root`) passes validation.
- `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_aggregator(state, aggregate.data.slot, aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`.
- The aggregator's validator index is within the aggregate's committee -- i.e. `aggregate_and_proof.aggregator_index in get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)`.
@ -316,7 +319,7 @@ Attestation subnets are used to propagate unaggregated attestations to subsectio
- The attestation's committee index (`attestation.data.index`) is for the correct subnet.
- `attestation.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (within a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot` (a client MAY queue future attestations for processing at the appropriate slot).
- The attestation is unaggregated -- that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit == 0b1]) == 1`).
- The attestation is the first valid attestation received for the participating validator for the slot, `attestation.data.slot`.
- There has been no other valid attestation seen on an attestation subnet that has an identical `attestation.data.target.epoch` and participating validator index.
- The block being voted for (`attestation.data.beacon_block_root`) passes validation.
- The signature of `attestation` is valid.
@ -342,7 +345,9 @@ Topics are post-fixed with an encoding. Encodings define how the payload of a go
#### Mainnet
- `ssz_snappy` - All objects are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy). Example: The beacon aggregate attestation topic string is `/eth2/beacon_aggregate_and_proof/ssz_snappy`, and the data field of a gossipsub message is an `AggregateAndProof` that has been SSZ-encoded and then compressed with Snappy.
- `ssz_snappy` - All objects are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy) block compression. Example: The beacon aggregate attestation topic string is `/eth2/446a7232/beacon_aggregate_and_proof/ssz_snappy`, the fork digest is `446a7232` and the data field of a gossipsub message is an `AggregateAndProof` that has been SSZ-encoded and then compressed with Snappy.
Snappy has two formats: "block" and "frames" (streaming). Gossip messages remain relatively small (100s of bytes to 100s of kilobytes) so [basic snappy block compression](https://github.com/google/snappy/blob/master/format_description.txt) is used to avoid the additional overhead associated with snappy frames.
Implementations MUST use a single encoding. Changing an encoding will require coordination between participating implementations.
@ -445,7 +450,7 @@ Here, `result` represents the 1-byte response code.
The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction. Two values are possible at this time:
- `ssz`: the contents are [SSZ-encoded](../../ssz/simple-serialize.md). This encoding type MUST be supported by all clients. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Root`'s.
- `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy). MAY be supported in the interoperability testnet; MUST be supported in mainnet.
- `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy) frames compression. MAY be supported in the interoperability testnet; MUST be supported in mainnet.
#### SSZ-encoding strategy (with or without Snappy)
@ -458,7 +463,7 @@ Snappy has two formats: "block" and "frames" (streaming). To support large reque
Since snappy frame contents [have a maximum size of `65536` bytes](https://github.com/google/snappy/blob/master/framing_format.txt#L104)
and frame headers are just `identifier (1) + checksum (4)` bytes, the expected buffering of a single frame is acceptable.
**Encoding-dependent header:** Req/Resp protocols using the `ssz` or `ssz_snappy` encoding strategies MUST encode the length of the raw SSZ bytes, encoded as an unsigned [protobuf varint](https://developers.google.com/protocol-buffers/docs/encoding#varints).
**Encoding-dependent header:** Req/Resp protocols using the `ssz` or `ssz_snappy` encoding strategies MUST encode the length of the raw SSZ bytes, encoded as an unsigned [protobuf varint](https://developers.google.com/protocol-buffers/docs/encoding#varints).
*Writing*: By first computing and writing the SSZ byte length, the SSZ encoder can then directly write the chunk contents to the stream.
If Snappy is applied, it can be passed through a buffered Snappy writer to compress frame by frame.
@ -572,7 +577,7 @@ Response Content:
)
```
Requests count beacon blocks from the peer starting from `start_slot`, leading up to the current head block as selected by fork choice. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at slots [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`.
Requests beacon blocks in the slot range `[start_slot, start_slot + count * step)`, leading up to the current head block as selected by fork choice. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at slots [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …].
`BeaconBlocksByRange` is primarily used to sync historical blocks.
@ -752,6 +757,8 @@ where the fields of `ENRForkID` are defined as
* `next_fork_version` is the fork version corresponding to the next planned hard fork at a future epoch. If no future fork is planned, set `next_fork_version = current_fork_version` to signal this fact
* `next_fork_epoch` is the epoch at which the next fork is planned and the `current_fork_version` will be updated. If no future fork is planned, set `next_fork_epoch = FAR_FUTURE_EPOCH` to signal this fact
*Note*: `fork_digest` is composed of values that are not not known until the genesis block/state are available. Due to this, clients SHOULD NOT form ENRs and begin peer discovery until genesis values are known. One notable exception to this rule is the distribution of bootnode ENRs prior to genesis. In this case, bootnode ENRs SHOULD be initially distributed with `eth2` field set as `ENRForkID(fork_digest=compute_fork_digest(GENESIS_FORK_VERSION, b'\x00'*32), next_fork_version=GENESIS_FORK_VERSION, next_fork_epoch=FAR_FUTURE_EPOCH)`. After genesis values are known, the bootnodes SHOULD update ENRs to participate in normal discovery operations.
Clients SHOULD connect to peers with `fork_digest`, `next_fork_version`, and `next_fork_epoch` that match local values.
Clients MAY connect to peers with the same `fork_digest` but a different `next_fork_version`/`next_fork_epoch`. Unless `ENRForkID` is manually updated to matching prior to the earlier `next_fork_epoch` of the two clients, these connecting clients will be unable to successfully interact starting at the earlier `next_fork_epoch`.
@ -1092,6 +1099,12 @@ discv5 uses ENRs and we will presumably need to:
1. Add `multiaddr` to the dictionary, so that nodes can advertise their multiaddr under a reserved namespace in ENRs. and/or
2. Define a bi-directional conversion function between multiaddrs and the corresponding denormalized fields in an ENR (ip, ip6, tcp, tcp6, etc.), for compatibility with nodes that do not support multiaddr natively (e.g. Eth 1.0 nodes).
### Why do we not form ENRs and find peers until genesis block/state is known?
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.
## Compression/Encoding
### Why are we using SSZ for encoding?

View File

@ -524,6 +524,8 @@ Because Phase 0 does not have shards and thus does not have Shard Committees, th
* Maintain advertisement of the randomly selected subnets in their node's ENR `attnets` entry by setting the randomly selected `subnet_id` bits to `True` (e.g. `ENR["attnets"][subnet_id] = True`) for all persistent attestation subnets
* Set the lifetime of each random subscription to a random number of epochs between `EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION` and `2 * EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION]`. At the end of life for a subscription, select a new random subnet, update subnet subscriptions, and publish an updated ENR
*Note*: Short lived beacon committee assignments should not be added in into the ENR `attnets` entry.
*Note*: When preparing for a hard fork, a validator must select and subscribe to random subnets of the future fork versioning at least `EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION` epochs in advance of the fork. These new subnets for the fork are maintained in addition to those for the current fork until the fork occurs. After the fork occurs, let the subnets from the previous fork reach the end of life with no replacements.
## How to avoid slashing

View File

@ -24,43 +24,45 @@
- [Extended `AttestationData`](#extended-attestationdata)
- [Extended `Attestation`](#extended-attestation)
- [Extended `PendingAttestation`](#extended-pendingattestation)
- [`IndexedAttestation`](#indexedattestation)
- [Extended `AttesterSlashing`](#extended-attesterslashing)
- [Extended `IndexedAttestation`](#extended-indexedattestation)
- [Extended `AttesterSlashing`](#extended-attesterslashing)
- [Extended `Validator`](#extended-validator)
- [Extended `BeaconBlockBody`](#extended-beaconblockbody)
- [Extended `BeaconBlock`](#extended-beaconblock)
- [Extended `SignedBeaconBlock`](#extended-signedbeaconblock)
- [Extended `BeaconState`](#extended-beaconstate)
- [New containers](#new-containers-1)
- [`ShardBlockWrapper`](#shardblockwrapper)
- [`ShardSignableHeader`](#shardsignableheader)
- [`ShardBlock`](#shardblock)
- [`SignedShardBlock`](#signedshardblock)
- [`ShardBlockHeader`](#shardblockheader)
- [`ShardState`](#shardstate)
- [`ShardTransition`](#shardtransition)
- [`CompactCommittee`](#compactcommittee)
- [`AttestationCustodyBitWrapper`](#attestationcustodybitwrapper)
- [Helper functions](#helper-functions)
- [Misc](#misc-1)
- [`get_previous_slot`](#get_previous_slot)
- [`compute_previous_slot`](#compute_previous_slot)
- [`pack_compact_validator`](#pack_compact_validator)
- [`unpack_compact_validator`](#unpack_compact_validator)
- [`committee_to_compact_committee`](#committee_to_compact_committee)
- [`compute_shard_from_committee_index`](#compute_shard_from_committee_index)
- [`compute_offset_slots`](#compute_offset_slots)
- [`compute_updated_gasprice`](#compute_updated_gasprice)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_active_shard_count`](#get_active_shard_count)
- [`get_online_validator_indices`](#get_online_validator_indices)
- [`get_shard_committee`](#get_shard_committee)
- [`get_shard_proposer_index`](#get_shard_proposer_index)
- [`get_light_client_committee`](#get_light_client_committee)
- [`get_shard_proposer_index`](#get_shard_proposer_index)
- [`get_indexed_attestation`](#get_indexed_attestation)
- [`get_updated_gasprice`](#get_updated_gasprice)
- [`get_start_shard`](#get_start_shard)
- [`get_shard`](#get_shard)
- [`get_latest_slot_for_shard`](#get_latest_slot_for_shard)
- [`get_offset_slots`](#get_offset_slots)
- [Predicates](#predicates)
- [`is_winning_attestation`](#is_winning_attestation)
- [Updated `is_valid_indexed_attestation`](#updated-is_valid_indexed_attestation)
- [`is_shard_attestation`](#is_shard_attestation)
- [`is_winning_attestation`](#is_winning_attestation)
- [Block processing](#block-processing)
- [Operations](#operations)
- [New Attestation processing](#new-attestation-processing)
@ -253,7 +255,7 @@ class PendingAttestation(Container):
crosslink_success: boolean
```
### `IndexedAttestation`
### Extended `IndexedAttestation`
```python
class IndexedAttestation(Container):
@ -261,7 +263,7 @@ class IndexedAttestation(Container):
attestation: Attestation
```
#### Extended `AttesterSlashing`
### Extended `AttesterSlashing`
Note that the `attestation_1` and `attestation_2` have a new `IndexedAttestation` definition.
@ -396,26 +398,33 @@ class BeaconState(Container):
The following containers are new in Phase 1.
### `ShardBlockWrapper`
_Wrapper for being broadcasted over the network._
### `ShardBlock`
```python
class ShardBlockWrapper(Container):
class ShardBlock(Container):
shard_parent_root: Root
beacon_parent_root: Root
slot: Slot
proposer_index: ValidatorIndex
body: ByteList[MAX_SHARD_BLOCK_SIZE]
```
### `SignedShardBlock`
```python
class SignedShardBlock(Container):
message: ShardBlock
signature: BLSSignature
```
### `ShardSignableHeader`
### `ShardBlockHeader`
```python
class ShardSignableHeader(Container):
class ShardBlockHeader(Container):
shard_parent_root: Root
beacon_parent_root: Root
slot: Slot
proposer_index: ValidatorIndex
body_root: Root
```
@ -425,7 +434,7 @@ class ShardSignableHeader(Container):
class ShardState(Container):
slot: Slot
gasprice: Gwei
data: Bytes32
transition_digest: Bytes32
latest_block_root: Root
```
@ -467,10 +476,10 @@ class AttestationCustodyBitWrapper(Container):
### Misc
#### `get_previous_slot`
#### `compute_previous_slot`
```python
def get_previous_slot(slot: Slot) -> Slot:
def compute_previous_slot(slot: Slot) -> Slot:
if slot > 0:
return Slot(slot - 1)
else:
@ -537,6 +546,20 @@ def compute_offset_slots(start_slot: Slot, end_slot: Slot) -> Sequence[Slot]:
return [Slot(start_slot + x) for x in SHARD_BLOCK_OFFSETS if start_slot + x < end_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)
// TARGET_SHARD_BLOCK_SIZE // GASPRICE_ADJUSTMENT_COEFFICIENT)
return min(prev_gasprice + delta, MAX_GASPRICE)
else:
delta = (prev_gasprice * (TARGET_SHARD_BLOCK_SIZE - length)
// TARGET_SHARD_BLOCK_SIZE // GASPRICE_ADJUSTMENT_COEFFICIENT)
return max(prev_gasprice, MIN_GASPRICE + delta) - delta
```
### Beacon state accessors
#### `get_active_shard_count`
@ -558,12 +581,35 @@ 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
source_epoch = epoch - epoch % SHARD_COMMITTEE_PERIOD
if source_epoch > 0:
source_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)
return compute_committee(active_validator_indices, seed, shard, get_active_shard_count(beacon_state))
active_shard_count = get_active_shard_count(beacon_state)
return compute_committee(
indices=active_validator_indices,
seed=seed,
index=shard,
count=active_shard_count,
)
```
#### `get_light_client_committee`
```python
def get_light_client_committee(beacon_state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
source_epoch = epoch - epoch % LIGHT_CLIENT_COMMITTEE_PERIOD
if source_epoch > 0:
source_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(
indices=active_validator_indices,
seed=seed,
index=0,
count=get_active_shard_count(beacon_state),
)[:TARGET_COMMITTEE_SIZE]
```
#### `get_shard_proposer_index`
@ -575,19 +621,6 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard
return committee[r % len(committee)]
```
#### `get_light_client_committee`
```python
def get_light_client_committee(beacon_state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
source_epoch = epoch - epoch % LIGHT_CLIENT_COMMITTEE_PERIOD
if source_epoch > 0:
source_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)
active_shards = get_active_shard_count(beacon_state)
return compute_committee(active_validator_indices, seed, 0, active_shards)[:TARGET_COMMITTEE_SIZE]
```
#### `get_indexed_attestation`
```python
@ -599,20 +632,6 @@ def get_indexed_attestation(beacon_state: BeaconState, attestation: Attestation)
)
```
#### `get_updated_gasprice`
```python
def get_updated_gasprice(prev_gasprice: Gwei, length: uint8) -> Gwei:
if length > TARGET_SHARD_BLOCK_SIZE:
delta = (prev_gasprice * (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)
// TARGET_SHARD_BLOCK_SIZE // GASPRICE_ADJUSTMENT_COEFFICIENT)
return max(prev_gasprice, MIN_GASPRICE + delta) - delta
```
#### `get_start_shard`
```python
@ -644,24 +663,6 @@ def get_offset_slots(state: BeaconState, shard: Shard) -> Sequence[Slot]:
### Predicates
#### `is_winning_attestation`
```python
def is_winning_attestation(state: BeaconState,
attestation: PendingAttestation,
committee_index: CommitteeIndex,
winning_root: Root) -> bool:
"""
Check if ``attestation`` helped contribute to the successful crosslink of
``winning_root`` formed by ``committee_index`` committee at the current slot.
"""
return (
attestation.slot == state.slot
and attestation.data.index == committee_index
and attestation.data.shard_transition_root == winning_root
)
```
#### Updated `is_valid_indexed_attestation`
Note that this replaces the Phase 0 `is_valid_indexed_attestation`.
@ -704,6 +705,39 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe
return bls.AggregateVerify(zip(all_pubkeys, all_signing_roots), signature=attestation.signature)
```
#### `is_shard_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
# TODO: MIN_ATTESTATION_INCLUSION_DELAY should always be 1
):
return False
return True
```
#### `is_winning_attestation`
```python
def is_winning_attestation(state: BeaconState,
attestation: PendingAttestation,
committee_index: CommitteeIndex,
winning_root: Root) -> bool:
"""
Check if ``attestation`` helped contribute to the successful crosslink of
``winning_root`` formed by ``committee_index`` committee at the current slot.
"""
return (
attestation.data.slot == state.slot
and attestation.data.index == committee_index
and attestation.data.shard_transition_root == winning_root
)
```
### Block processing
@ -712,12 +746,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
process_randao(state, block.body)
process_eth1_data(state, block.body)
verify_shard_transition_false_positives(state, block.body)
process_light_client_signatures(state, block.body)
process_operations(state, block.body)
verify_shard_transition_false_positives(state, block.body)
```
#### Operations
```python
@ -774,7 +807,7 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
# Correct data root count
assert len(attestation.custody_bits_blocks) == len(get_offset_slots(state, shard))
# Correct parent block root
assert data.beacon_block_root == get_block_root_at_slot(state, get_previous_slot(state.slot))
assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot))
# Type 2: no shard transition, no custody bits
else:
# Ensure delayed attestation
@ -790,6 +823,9 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
```python
def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTransition) -> None:
# TODO: only need to check it once when phase 1 starts
assert state.slot > PHASE_1_GENESIS_SLOT
# Correct data root count
offset_slots = get_offset_slots(state, shard)
assert (
@ -800,28 +836,32 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
)
assert transition.start_slot == offset_slots[0]
# Reconstruct shard headers
headers = []
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)):
if any(transition.shard_data_roots):
headers.append(ShardSignableHeader(
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]
# 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)
# Reconstruct shard headers
header = ShardBlockHeader(
shard_parent_root=shard_parent_root,
parent_hash=get_block_root_at_slot(state, get_previous_slot(state.slot)),
beacon_parent_root=get_block_root_at_slot(state, offset_slots[i]),
proposer_index=proposal_index,
slot=offset_slots[i],
body_root=transition.shard_data_roots[i]
))
proposers.append(get_shard_proposer_index(state, shard, offset_slots[i]))
shard_parent_root = hash_tree_root(headers[-1])
)
shard_parent_root = hash_tree_root(header)
headers.append(header)
proposers.append(proposal_index)
# Verify correct calculation of gas prices and slots
prev_gasprice = state.shard_states[shard].gasprice
for i in range(len(offset_slots)):
shard_state = transition.shard_states[i]
block_length = transition.shard_block_lengths[i]
assert shard_state.gasprice == get_updated_gasprice(prev_gasprice, block_length)
assert shard_state.slot == offset_slots[i]
prev_gasprice = shard_state.gasprice
pubkeys = [state.validators[proposer].pubkey for proposer in proposers]
@ -833,7 +873,7 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
assert bls.AggregateVerify(zip(pubkeys, signing_roots), signature=transition.proposer_signature_aggregate)
# Save updated state
state.shard_states[shard] = transition.shard_states[-1]
state.shard_states[shard] = transition.shard_states[len(transition.shard_states) - 1]
state.shard_states[shard].slot = state.slot - 1
```
@ -856,6 +896,9 @@ 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[
len(shard_transition.shard_data_roots) - 1
]
enough_online_stake = (
get_total_balance(state, online_indices.intersection(transition_participants)) * 3 >=
@ -867,7 +910,6 @@ def process_crosslink_for_shard(state: BeaconState,
# Attestation <-> shard transition consistency
assert shard_transition_root == hash_tree_root(shard_transition)
assert attestation.data.head_shard_root == shard_transition.shard_data_roots[-1]
# Apply transition
apply_shard_transition(state, shard, shard_transition)
@ -878,11 +920,11 @@ def process_crosslink_for_shard(state: BeaconState,
increase_balance(state, beacon_proposer_index, proposer_reward)
states_slots_lengths = zip(
shard_transition.shard_states,
get_offset_slots(state, get_latest_slot_for_shard(state, shard)),
get_offset_slots(state, shard),
shard_transition.shard_block_lengths
)
for shard_state, slot, length in states_slots_lengths:
proposer_index = get_shard_proposer_index(state, shard, slot)
proposer_index = get_shard_proposer_index(state, slot, shard)
decrease_balance(state, proposer_index, shard_state.gasprice * length)
# Return winning transition root
@ -903,11 +945,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 attestation.data.index == committee_index and attestation.data.slot == state.slot
if is_shard_attestation(state, attestation, committee_index)
]
shard_transition = shard_transitions[shard]
winning_root = process_crosslink_for_shard(state, committee_index, shard_transition, shard_attestations)
if winning_root != Root():
# Mark relevant pending attestations as creating a successful crosslink
@ -1055,13 +1098,17 @@ def process_light_client_signatures(state: BeaconState, block_body: BeaconBlockB
total_reward += get_base_reward(state, participant_index)
increase_balance(state, get_beacon_proposer_index(state), Gwei(total_reward // PROPOSER_REWARD_QUOTIENT))
slot = get_previous_slot(state.slot)
signing_root = compute_signing_root(get_block_root_at_slot(state, slot),
get_domain(state, DOMAIN_LIGHT_CLIENT, compute_epoch_at_slot(slot)))
assert bls.FastAggregateVerify(signer_pubkeys, signing_root, signature=block_body.light_client_signature)
```
slot = compute_previous_slot(state.slot)
signing_root = compute_signing_root(get_block_root_at_slot(state, slot),
get_domain(state, DOMAIN_LIGHT_CLIENT, compute_epoch_at_slot(slot)))
if len(signer_pubkeys) == 0:
# TODO: handle the empty light_client_signature case?
assert block_body.light_client_signature == BLSSignature()
return
else:
assert bls.FastAggregateVerify(signer_pubkeys, signing_root, signature=block_body.light_client_signature)
```
### Epoch transition

View File

@ -1,70 +0,0 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Ethereum 2.0 Phase 1 -- Shard Transition and Fraud Proofs](#ethereum-20-phase-1----shard-transition-and-fraud-proofs)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Fraud proofs](#fraud-proofs)
- [Shard state transition function](#shard-state-transition-function)
- [Honest committee member behavior](#honest-committee-member-behavior)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Ethereum 2.0 Phase 1 -- Shard Transition and Fraud Proofs
**Notice**: This document is a work-in-progress for researchers and implementers.
## Table of contents
<!-- TOC -->
TODO
<!-- /TOC -->
## Introduction
This document describes the shard transition function and fraud proofs as part of Phase 1 of Ethereum 2.0.
## Fraud proofs
TODO. The intent is to have a single universal fraud proof type, which contains the following parts:
1. An on-time attestation on some `shard` signing a `ShardTransition`
2. An index `i` of a particular position to focus on
3. The `ShardTransition` itself
4. The full body of the block
5. A Merkle proof to the `shard_states` in the parent block the attestation is referencing
The proof verifies that one of the two conditions is false:
1. `custody_bits[i][j] != generate_custody_bit(subkey, block_contents)` for any `j`
2. `execute_state_transition(shard, slot, transition.shard_states[i-1].data, hash_tree_root(parent), get_shard_proposer_index(state, shard, slot), block_contents) != transition.shard_states[i].data` (if `i=0` then instead use `parent.shard_states[shard][-1].data`)
## Shard state transition function
```python
def shard_state_transition(shard: Shard,
slot: Slot,
pre_state: Root,
previous_beacon_root: Root,
proposer_pubkey: BLSPubkey,
block_data: ByteList[MAX_SHARD_BLOCK_SIZE]) -> Root:
# We will add something more substantive in phase 2
return hash(pre_state + hash_tree_root(previous_beacon_root) + hash_tree_root(block_data))
```
## Honest committee member behavior
Suppose you are a committee member on shard `shard` at slot `current_slot`. Let `state` be the head beacon state you are building on, and let `QUARTER_PERIOD = SECONDS_PER_SLOT // 4`. `2 * QUARTER_PERIOD` seconds into slot `slot`, run the following procedure:
* Initialize `proposals = []`, `shard_states = []`, `shard_state = state.shard_states[shard][-1]`, `start_slot = shard_state.slot`.
* For `slot in get_offset_slots(state, start_slot)`, do the following:
* Look for all valid proposals for `slot`; that is, a Bytes `proposal` where `shard_state_transition(shard, slot, shard_state, get_block_root_at_slot(state, state.slot - 1), get_shard_proposer_index(state, shard, slot), proposal)` returns a result and does not throw an exception. Let `choices` be the set of non-empty valid proposals you discover.
* If `len(choices) == 0`, do `proposals.append(make_empty_proposal(shard_state, slot))`
* If `len(choices) == 1`, do `proposals.append(choices[0])`
* If `len(choices) > 1`, let `winning_proposal` be the proposal with the largest number of total attestations from slots in `state.shard_next_slots[shard]....slot-1` supporting it or any of its descendants, breaking ties by choosing the first proposal locally seen. Do `proposals.append(winning_proposal)`.
* If `proposals[-1]` is NOT an empty proposal, set `shard_state = shard_state_transition(shard, slot, shard_state, get_block_root_at_slot(state, state.slot - 1), get_shard_proposer_index(state, shard, slot), proposals[-1])` and do `shard_states.append(shard_state)`. If it is an empty proposal, leave `shard_state` unchanged.
Make an attestation using `shard_data_roots = [hash_tree_root(proposal) for proposal in proposals]` and `shard_state_roots = shard_states`.

View File

@ -7,7 +7,7 @@
- [Introduction](#introduction)
- [Configuration](#configuration)
- [Fork to Phase 1](#fork-to-phase-1)
- [Fork trigger.](#fork-trigger)
- [Fork trigger](#fork-trigger)
- [Upgrading the state](#upgrading-the-state)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -35,17 +35,18 @@ Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `PHASE_1_FORK_VERSION` | `Version('0x01000000')` |
| `PHASE_1_GENESIS_SLOT` | `2**5` **TBD** |
| `INITIAL_ACTIVE_SHARDS` | `2**6` (= 64) |
## Fork to Phase 1
### Fork trigger.
### Fork trigger
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork.
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_GENESIS_SLOT`, where `PHASE_1_GENESIS_SLOT % SLOTS_PER_EPOCH == 0`.
### Upgrading the state
After `process_slots` of Phase 0 finishes, but before the first Phase 1 block is processed, an irregular state change is made to upgrade to Phase 1.
After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_GENESIS_SLOT`, an irregular state change is made to upgrade to Phase 1.
```python
def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
@ -102,7 +103,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
ShardState(
slot=pre.slot,
gasprice=MIN_GASPRICE,
data=Root(),
transition_digest=Root(),
latest_block_root=Root(),
) for i in range(INITIAL_ACTIVE_SHARDS)
),
@ -110,7 +111,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
current_light_committee=CompactCommittee(), # computed after state creation
next_light_committee=CompactCommittee(),
# Custody game
custody_challenge_index=0,
exposed_derived_secrets=[] * EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS,
# exposed_derived_secrets will fully default to zeroes
)
next_epoch = Epoch(epoch + 1)

View File

@ -0,0 +1,292 @@
# Ethereum 2.0 Phase 1 -- Shard Transition and Fraud Proofs
**Notice**: This document is a work-in-progress for researchers and implementers.
## Table of contents
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Introduction](#introduction)
- [Helper functions](#helper-functions)
- [Misc](#misc)
- [Shard block verification functions](#shard-block-verification-functions)
- [Shard state transition](#shard-state-transition)
- [Fraud proofs](#fraud-proofs)
- [Verifying the proof](#verifying-the-proof)
- [Honest committee member behavior](#honest-committee-member-behavior)
- [Helper functions](#helper-functions-1)
- [Make attestations](#make-attestations)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Introduction
This document describes the shard transition function and fraud proofs as part of Phase 1 of Ethereum 2.0.
## Helper functions
### Misc
```python
def compute_shard_transition_digest(beacon_state: BeaconState,
shard_state: ShardState,
beacon_parent_root: Root,
shard_body_root: Root) -> Bytes32:
# TODO: use SSZ hash tree root
return hash(
hash_tree_root(shard_state) + beacon_parent_root + shard_body_root
)
```
### Shard block verification functions
```python
def verify_shard_block_message(beacon_state: BeaconState,
shard_state: ShardState,
block: ShardBlock,
slot: Slot,
shard: Shard) -> bool:
assert block.shard_parent_root == shard_state.latest_block_root
assert block.slot == slot
assert block.proposer_index == get_shard_proposer_index(beacon_state, slot, shard)
assert 0 < len(block.body) <= MAX_SHARD_BLOCK_SIZE
return True
```
```python
def verify_shard_block_signature(beacon_state: BeaconState,
signed_block: SignedShardBlock) -> bool:
proposer = beacon_state.validators[signed_block.message.proposer_index]
domain = get_domain(beacon_state, DOMAIN_SHARD_PROPOSAL, compute_epoch_at_slot(signed_block.message.slot))
signing_root = compute_signing_root(signed_block.message, domain)
return bls.Verify(proposer.pubkey, signing_root, signed_block.signature)
```
## Shard state transition
```python
def shard_state_transition(beacon_state: BeaconState,
shard_state: ShardState,
block: ShardBlock) -> None:
# Update shard state
prev_gasprice = shard_state.gasprice
if len(block.body) == 0:
latest_block_root = shard_state.latest_block_root
else:
latest_block_root = hash_tree_root(block)
shard_state.transition_digest = compute_shard_transition_digest(
beacon_state,
shard_state,
block.beacon_parent_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.
```python
def get_post_shard_state(beacon_state: BeaconState,
shard_state: ShardState,
block: ShardBlock) -> ShardState:
"""
A pure function that returns a new post ShardState instead of modifying the given `shard_state`.
"""
post_state = shard_state.copy()
shard_state_transition(beacon_state, post_state, block)
return post_state
```
## Fraud proofs
### Verifying the proof
TODO. The intent is to have a single universal fraud proof type, which contains the following parts:
1. An on-time attestation `attestation` on some shard `shard` signing a `transition: ShardTransition`
2. An index `offset_index` of a particular position to focus on
3. The `transition: ShardTransition` itself
4. The full body of the shard block `shard_block`
5. A Merkle proof to the `shard_states` in the parent block the attestation is referencing
6. The `subkey` to generate the custody bit
Call the following function to verify the proof:
```python
def is_valid_fraud_proof(beacon_state: BeaconState,
attestation: Attestation,
offset_index: uint64,
transition: ShardTransition,
block: ShardBlock,
subkey: BLSPubkey,
beacon_parent_block: BeaconBlock) -> bool:
# 1. Check if `custody_bits[offset_index][j] != generate_custody_bit(subkey, block_contents)` for any `j`.
custody_bits = attestation.custody_bits_blocks
for j in range(custody_bits[offset_index]):
if custody_bits[offset_index][j] != generate_custody_bit(subkey, block):
return True
# 2. Check if the shard state transition result is wrong between
# `transition.shard_states[offset_index - 1]` to `transition.shard_states[offset_index]`.
if offset_index == 0:
shard = get_shard(beacon_state, attestation)
shard_state = beacon_parent_block.shard_transitions[shard].shard_states[-1]
else:
shard_state = transition.shard_states[offset_index - 1] # Not doing the actual state updates here.
shard_state = get_post_shard_state(beacon_state, shard_state, block)
if shard_state.transition_digest != transition.shard_states[offset_index].transition_digest:
return True
return False
```
```python
def generate_custody_bit(subkey: BLSPubkey, block: ShardBlock) -> bool:
# TODO
...
```
## Honest committee member behavior
### Helper functions
```python
def get_winning_proposal(beacon_state: BeaconState, proposals: Sequence[SignedShardBlock]) -> SignedShardBlock:
# TODO: Let `winning_proposal` be the proposal with the largest number of total attestations from slots in
# `state.shard_next_slots[shard]....slot-1` supporting it or any of its descendants, breaking ties by choosing
# the first proposal locally seen. Do `proposals.append(winning_proposal)`.
return proposals[-1] # stub
```
```python
def compute_shard_body_roots(proposals: Sequence[SignedShardBlock]) -> Sequence[Root]:
return [hash_tree_root(proposal.message.body) for proposal in proposals]
```
```python
def get_proposal_choices_at_slot(beacon_state: BeaconState,
shard_state: ShardState,
slot: Slot,
shard: Shard,
shard_blocks: Sequence[SignedShardBlock],
validate_signature: bool=True) -> Sequence[SignedShardBlock]:
"""
Return the valid shard blocks at the given ``slot``.
Note that this function doesn't change the state.
"""
choices = []
shard_blocks_at_slot = [block for block in shard_blocks if block.message.slot == slot]
for block in shard_blocks_at_slot:
try:
# Verify block message and signature
# TODO these validations should have been checked upon receiving shard blocks.
assert verify_shard_block_message(beacon_state, shard_state, block.message, slot, shard)
if validate_signature:
assert verify_shard_block_signature(beacon_state, block)
shard_state = get_post_shard_state(beacon_state, shard_state, block.message)
except Exception:
pass # TODO: throw error in the test helper
else:
choices.append(block)
return choices
```
```python
def get_proposal_at_slot(beacon_state: BeaconState,
shard_state: ShardState,
slot: Shard,
shard: Shard,
shard_blocks: Sequence[SignedShardBlock],
validate_signature: bool=True) -> Tuple[SignedShardBlock, ShardState]:
"""
Return ``proposal``, ``shard_state`` of the given ``slot``.
Note that this function doesn't change the state.
"""
choices = get_proposal_choices_at_slot(
beacon_state=beacon_state,
shard_state=shard_state,
slot=slot,
shard=shard,
shard_blocks=shard_blocks,
validate_signature=validate_signature,
)
if len(choices) == 0:
block = ShardBlock(slot=slot)
proposal = SignedShardBlock(message=block)
elif len(choices) == 1:
proposal = choices[0]
else:
proposal = get_winning_proposal(beacon_state, choices)
# Apply state transition
shard_state = get_post_shard_state(beacon_state, shard_state, proposal.message)
return proposal, shard_state
```
```python
def get_shard_state_transition_result(
beacon_state: BeaconState,
shard: Shard,
shard_blocks: Sequence[SignedShardBlock],
validate_signature: bool=True,
) -> Tuple[Sequence[SignedShardBlock], Sequence[ShardState], Sequence[Root]]:
proposals = []
shard_states = []
shard_state = beacon_state.shard_states[shard]
for slot in get_offset_slots(beacon_state, shard):
proposal, shard_state = get_proposal_at_slot(
beacon_state=beacon_state,
shard_state=shard_state,
slot=slot,
shard=shard,
shard_blocks=shard_blocks,
validate_signature=validate_signature,
)
shard_states.append(shard_state)
proposals.append(proposal)
shard_data_roots = compute_shard_body_roots(proposals)
return proposals, shard_states, shard_data_roots
```
### Make attestations
Suppose you are a committee member on shard `shard` at slot `current_slot` and you have received shard blocks `shard_blocks` since the latest successful crosslink for `shard` into the beacon chain. Let `state` be the head beacon state you are building on, and let `QUARTER_PERIOD = SECONDS_PER_SLOT // 4`. `2 * QUARTER_PERIOD` seconds into slot `current_slot`, run `get_shard_transition(beacon_state, shard, shard_blocks)` to get `shard_transition`.
```python
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:
shard_block_lengths.append(len(proposal.message.body))
if proposal.signature != BLSSignature():
proposer_signatures.append(proposal.signature)
proposer_signature_aggregate = bls.Aggregate(proposer_signatures)
return ShardTransition(
start_slot=start_slot,
shard_block_lengths=shard_block_lengths,
shard_data_roots=shard_data_roots,
shard_states=shard_states,
proposer_signature_aggregate=proposer_signature_aggregate,
)
```

View File

@ -1 +1 @@
0.11.1
0.11.2

View File

@ -8,13 +8,18 @@ config: Dict[str, Any] = {}
# Access to overwrite spec constants based on configuration
# This is called by the spec module after declaring its globals, and applies the loaded presets.
def apply_constants_config(spec_globals: Dict[str, Any]) -> None:
def apply_constants_config(spec_globals: Dict[str, Any], warn_if_unknown: bool = False) -> None:
global config
for k, v in config.items():
if k.startswith('DOMAIN_'):
spec_globals[k] = spec_globals['DomainType'](v) # domain types are defined as bytes in the configs
# the spec should have default values for everything, if not, the config key is invalid.
if k in spec_globals:
# Keep the same type as the default value indicates (which may be an SSZ basic type subclass, e.g. 'Gwei')
spec_globals[k] = spec_globals[k].__class__(v)
else:
spec_globals[k] = v
# Note: Phase 0 spec will not know the phase 1 config values.
# Yet, during debugging you can enable explicit warnings.
if warn_if_unknown:
print(f"WARNING: unknown config key: '{k}' with value: '{v}'")
# Load presets from a file, and then prepares the global config setting. This does not apply the config.

View File

@ -1,12 +1,11 @@
from typing import List
from eth2spec.test.context import expect_assertion_error, PHASE0
from eth2spec.test.helpers.state import state_transition_and_sign_block
from eth2spec.test.context import expect_assertion_error, PHASE0, PHASE1
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_slot, transition_to
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.ssz.ssz_typing import Bitlist
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.test.helpers.custody import get_custody_test_vector
@ -45,7 +44,7 @@ def run_attestation_processing(spec, state, attestation, valid=True):
yield 'post', state
def build_attestation_data(spec, state, slot, index, shard_transition_root=None):
def build_attestation_data(spec, state, slot, index, shard_transition=None, on_time=True):
assert state.slot >= slot
if slot == state.slot:
@ -68,15 +67,34 @@ def build_attestation_data(spec, state, slot, index, shard_transition_root=None)
source_epoch = state.current_justified_checkpoint.epoch
source_root = state.current_justified_checkpoint.root
return spec.AttestationData(
attestation_data = spec.AttestationData(
slot=slot,
index=index,
beacon_block_root=block_root,
source=spec.Checkpoint(epoch=source_epoch, root=source_root),
target=spec.Checkpoint(epoch=spec.compute_epoch_at_slot(slot), root=epoch_boundary_root),
shard_transition_root=shard_transition_root if shard_transition_root else spec.Root(),
)
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_transition_root = shard_transition.hash_tree_root()
else:
# No shard transition
shard = spec.get_shard(state, spec.Attestation(data=attestation_data))
if on_time:
temp_state = state.copy()
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_transition_root = shard_transition.hash_tree_root()
else:
attestation_data.head_shard_root = state.shard_states[shard].transition_digest
attestation_data.shard_transition_root = spec.Root()
return attestation_data
def convert_to_valid_on_time_attestation(spec, state, attestation, signed=False, shard_transition=None,
valid_custody_bits=None):
@ -114,8 +132,8 @@ def convert_to_valid_on_time_attestation(spec, state, attestation, signed=False,
return attestation
def get_valid_on_time_attestation(spec, state, slot=None, index=None, signed=False,
shard_transition=None, valid_custody_bits=None):
def get_valid_on_time_attestation(spec, state, slot=None, index=None,
shard_transition=None, valid_custody_bits=None, signed=False):
'''
Construct on-time attestation for next slot
'''
@ -124,9 +142,16 @@ def get_valid_on_time_attestation(spec, state, slot=None, index=None, signed=Fal
if index is None:
index = 0
return get_valid_attestation(spec, state, slot=slot, index=index,
signed=signed, on_time=True, shard_transition=shard_transition,
valid_custody_bits=valid_custody_bits)
return get_valid_attestation(
spec,
state,
slot=slot,
index=index,
shard_transition=shard_transition,
valid_custody_bits=valid_custody_bits,
signed=signed,
on_time=True,
)
def get_valid_late_attestation(spec, state, slot=None, index=None, signed=False, shard_transition=None):
@ -142,15 +167,23 @@ def get_valid_late_attestation(spec, state, slot=None, index=None, signed=False,
signed=signed, on_time=False, shard_transition=shard_transition)
def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signed=False, on_time=True,
shard_transition=None, valid_custody_bits=None):
def get_valid_attestation(spec,
state,
slot=None,
index=None,
shard_transition=None,
valid_custody_bits=None,
empty=False,
signed=False,
on_time=True):
if slot is None:
slot = state.slot
if index is None:
index = 0
attestation_data = build_attestation_data(spec, state, slot, index,
shard_transition_root=hash_tree_root(shard_transition) if shard_transition else spec.Root())
attestation_data = build_attestation_data(
spec, state, slot=slot, index=index, shard_transition=shard_transition, on_time=on_time
)
beacon_committee = spec.get_beacon_committee(
state,
@ -169,10 +202,13 @@ def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signe
if signed:
sign_attestation(spec, state, attestation)
if spec.fork == 'phase1' and on_time:
attestation = convert_to_valid_on_time_attestation(spec, state, attestation, signed,
shard_transition=shard_transition,
valid_custody_bits=valid_custody_bits)
if spec.fork == PHASE1 and on_time:
attestation = convert_to_valid_on_time_attestation(
spec, state, attestation,
shard_transition=shard_transition,
valid_custody_bits=valid_custody_bits,
signed=signed,
)
return attestation
@ -243,7 +279,7 @@ def get_attestation_custody_signature(spec, state, attestation_data, block_index
def sign_attestation(spec, state, attestation):
if spec.fork == 'phase1' and any(attestation.custody_bits_blocks):
if spec.fork == PHASE1 and any(attestation.custody_bits_blocks):
sign_on_time_attestation(spec, state, attestation)
return
@ -307,7 +343,21 @@ def next_epoch_with_attestations(spec,
spec, post_state, slot_to_attest, index=index, signed=True, on_time=False)
block.body.attestations.append(prev_attestation)
if spec.fork == PHASE1:
fill_block_shard_transitions_by_attestations(spec, post_state, block)
signed_block = state_transition_and_sign_block(spec, post_state, block)
signed_blocks.append(signed_block)
return state, signed_blocks, post_state
def fill_block_shard_transitions_by_attestations(spec, state, block):
block.body.shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS
for attestation in block.body.attestations:
shard = spec.get_shard(state, attestation)
if attestation.data.slot == state.slot:
temp_state = state.copy()
transition_to(spec, temp_state, slot=block.slot)
shard_transition = spec.get_shard_transition(temp_state, shard, [])
block.body.shard_transitions[shard] = shard_transition

View File

@ -71,24 +71,31 @@ def build_empty_block(spec, state, slot=None):
"""
if slot is None:
slot = state.slot
if slot < state.slot:
raise Exception("build_empty_block cannot build blocks for past slots")
if slot > state.slot:
# transition forward in copied state to grab relevant data from state
state = state.copy()
spec.process_slots(state, slot)
state, parent_block_root = get_state_and_beacon_parent_root_at_slot(spec, state, slot)
empty_block = spec.BeaconBlock()
empty_block.slot = slot
empty_block.proposer_index = spec.get_beacon_proposer_index(state)
empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index
previous_block_header = state.latest_block_header.copy()
if previous_block_header.state_root == spec.Root():
previous_block_header.state_root = hash_tree_root(state)
empty_block.parent_root = hash_tree_root(previous_block_header)
empty_block.parent_root = parent_block_root
apply_randao_reveal(spec, state, empty_block)
return empty_block
def build_empty_block_for_next_slot(spec, state):
return build_empty_block(spec, state, state.slot + 1)
def get_state_and_beacon_parent_root_at_slot(spec, state, slot):
if slot < state.slot:
raise Exception("Cannot build blocks for past slots")
if slot > state.slot:
# transition forward in copied state to grab relevant data from state
state = state.copy()
spec.process_slots(state, slot)
previous_block_header = state.latest_block_header.copy()
if previous_block_header.state_root == spec.Root():
previous_block_header.state_root = hash_tree_root(state)
beacon_parent_root = hash_tree_root(previous_block_header)
return state, beacon_parent_root

View File

@ -0,0 +1,28 @@
from eth2spec.test.context import expect_assertion_error
def run_crosslinks_processing(spec, state, shard_transitions, attestations, valid=True):
"""
Run ``process_attestation``, yielding:
- pre-state ('pre')
- shard_transitions ('shard_transitions')
- attestations ('attestations')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
# yield pre-state
yield 'pre', state
yield 'shard_transitions', shard_transitions
yield 'attestations', attestations
# 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))
yield 'post', None
return
# process crosslinks
spec.process_crosslinks(state, shard_transitions, attestations)
# yield post-state
yield 'post', state

View File

@ -1,71 +0,0 @@
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
from eth2spec.utils.ssz.ssz_impl import (
hash_tree_root,
)
from .attestations import (
sign_shard_attestation,
)
@only_with_bls()
def sign_shard_block(spec, beacon_state, shard_state, block, proposer_index=None):
if proposer_index is None:
proposer_index = spec.get_shard_proposer_index(beacon_state, shard_state.shard, block.slot)
privkey = privkeys[proposer_index]
domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_PROPOSER, spec.compute_epoch_of_shard_slot(block.slot))
signing_root = spec.compute_signing_root(block, domain)
block.signature = bls.Sign(privkey, signing_root)
def build_empty_shard_block(spec,
beacon_state,
shard_state,
slot,
signed=False,
full_attestation=False):
if slot is None:
slot = shard_state.slot
previous_beacon_header = beacon_state.latest_block_header.copy()
if previous_beacon_header.state_root == spec.Bytes32():
previous_beacon_header.state_root = beacon_state.hash_tree_root()
beacon_block_root = hash_tree_root(previous_beacon_header)
previous_block_header = shard_state.latest_block_header.copy()
if previous_block_header.state_root == spec.Bytes32():
previous_block_header.state_root = shard_state.hash_tree_root()
parent_root = hash_tree_root(previous_block_header)
block = spec.ShardBlock(
shard=shard_state.shard,
slot=slot,
beacon_block_root=beacon_block_root,
parent_root=parent_root,
block_size_sum=shard_state.block_size_sum + spec.SHARD_HEADER_SIZE,
)
if full_attestation:
shard_committee = spec.get_shard_committee(beacon_state, shard_state.shard, block.slot)
block.aggregation_bits = list(
(True,) * len(shard_committee) +
(False,) * (spec.MAX_PERIOD_COMMITTEE_SIZE * 2 - len(shard_committee))
)
else:
shard_committee = []
block.attestations = sign_shard_attestation(
spec,
beacon_state,
shard_state,
block,
participants=shard_committee,
)
if signed:
sign_shard_block(spec, beacon_state, shard_state, block)
return block

View File

@ -1,18 +0,0 @@
from eth2spec.test.helpers.phase1.shard_block import sign_shard_block
def configure_shard_state(spec, beacon_state, shard=0):
beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH)
shard_state = spec.get_genesis_shard_state(spec.Shard(shard))
shard_state.slot = spec.ShardSlot(spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH)
return beacon_state, shard_state
def shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block):
"""
Shard state transition via the provided ``block``
then package the block with the state root and signature.
"""
spec.shard_state_transition(beacon_state, shard_state, block)
block.state_root = shard_state.hash_tree_root()
sign_shard_block(spec, beacon_state, shard_state, block)

View File

@ -0,0 +1,85 @@
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
from eth2spec.test.helpers.block import get_state_and_beacon_parent_root_at_slot
from eth2spec.test.helpers.state import transition_to
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
@only_with_bls()
def sign_shard_block(spec, beacon_state, shard, block, proposer_index=None):
slot = block.message.slot
if proposer_index is None:
proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard)
privkey = privkeys[proposer_index]
domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_PROPOSAL, spec.compute_epoch_at_slot(slot))
signing_root = spec.compute_signing_root(block.message, domain)
block.signature = bls.Sign(privkey, signing_root)
def build_shard_block(spec,
beacon_state,
shard,
slot=None,
body=None,
signed=False):
shard_state = beacon_state.shard_states[shard]
if slot is None:
slot = shard_state.slot + 1
if body is None:
body = b'\x56' * 128
proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard)
beacon_state, beacon_parent_root = get_state_and_beacon_parent_root_at_slot(spec, beacon_state, slot)
block = spec.ShardBlock(
shard_parent_root=shard_state.latest_block_root,
beacon_parent_root=beacon_parent_root,
slot=slot,
proposer_index=proposer_index,
body=body,
)
signed_block = spec.SignedShardBlock(
message=block,
)
if signed:
sign_shard_block(spec, beacon_state, shard, signed_block, proposer_index=proposer_index)
return signed_block
def build_shard_transitions_till_slot(spec, state, shard_blocks, on_time_slot):
temp_state = state.copy()
transition_to(spec, temp_state, on_time_slot)
shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS
for shard, blocks in shard_blocks.items():
offset_slots = spec.get_offset_slots(temp_state, shard)
len_offset_slots = len(offset_slots)
assert len_offset_slots == on_time_slot - state.shard_states[shard].slot - 1
shard_transition = spec.get_shard_transition(temp_state, shard, blocks)
if len(blocks) > 0:
shard_block_root = blocks[-1].message.hash_tree_root()
assert shard_transition.shard_states[len_offset_slots - 1].latest_block_root == shard_block_root
assert shard_transition.shard_states[len_offset_slots - 1].slot == offset_slots[-1]
shard_transitions[shard] = shard_transition
return shard_transitions
def build_attestation_with_shard_transition(spec, state, index, on_time_slot, shard_transition=None):
temp_state = state.copy()
transition_to(spec, temp_state, on_time_slot - 1)
attestation = get_valid_on_time_attestation(
spec,
temp_state,
index=index,
shard_transition=shard_transition,
signed=True,
)
assert attestation.data.slot == temp_state.slot
if shard_transition is not None:
assert attestation.data.shard_transition_root == shard_transition.hash_tree_root()
return attestation

View File

@ -30,6 +30,16 @@ def transition_to(spec, state, slot):
assert state.slot == slot
def transition_to_valid_shard_slot(spec, state):
"""
Transition to slot `spec.PHASE_1_GENESIS_SLOT + 1` and fork at `spec.PHASE_1_GENESIS_SLOT`.
"""
transition_to(spec, state, spec.PHASE_1_GENESIS_SLOT)
state = spec.upgrade_to_phase1(state) # `upgrade_to_phase1` is a pure function
next_slot(spec, state)
return state
def next_epoch(spec, state):
"""
Transition to the start slot of the next epoch

View File

@ -24,7 +24,7 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}")
total_balance = spec.get_total_active_balance(state)
remaining_balance = total_balance * 2 // 3
remaining_balance = int(total_balance * 2 // 3) # can become negative
start_slot = spec.compute_start_slot_at_epoch(epoch)
for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH):
@ -42,7 +42,7 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
aggregation_bits = [0] * len(committee)
for v in range(len(committee) * 2 // 3 + 1):
if remaining_balance > 0:
remaining_balance -= state.validators[v].effective_balance
remaining_balance -= int(state.validators[v].effective_balance)
aggregation_bits[v] = 1
else:
break

View File

@ -8,10 +8,13 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_e
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, get_indexed_attestation_participants
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.attestations import get_valid_attestation, fill_block_shard_transitions_by_attestations
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error, always_bls, with_phases
from eth2spec.test.context import (
spec_state_test, with_all_phases, expect_assertion_error, always_bls, with_phases,
PHASE1
)
@with_all_phases
@ -420,12 +423,14 @@ def test_attestation(spec, state):
yield 'pre', state
attestation = get_valid_attestation(spec, state, signed=True)
attestation = get_valid_attestation(spec, state, signed=True, on_time=True)
# Add to state via block transition
pre_current_attestations_len = len(state.current_epoch_attestations)
attestation_block = build_empty_block(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
attestation_block.body.attestations.append(attestation)
if spec.fork == PHASE1:
fill_block_shard_transitions_by_attestations(spec, state, attestation_block)
signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1

View File

@ -0,0 +1,73 @@
from eth2spec.test.context import (
PHASE0,
with_all_phases_except,
spec_state_test,
always_bls,
)
from eth2spec.test.helpers.crosslinks import run_crosslinks_processing
from eth2spec.test.helpers.shard_block import (
build_attestation_with_shard_transition,
build_shard_block,
build_shard_transitions_till_slot,
)
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot
def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True):
state = transition_to_valid_shard_slot(spec, state)
# At the beginning, let `x = state.slot`, `state.shard_states[shard].slot == x - 1`
slot_x = state.slot
committee_index = spec.CommitteeIndex(0)
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
assert state.shard_states[shard].slot == slot_x - 1
# Create SignedShardBlock
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
shard_block = build_shard_block(spec, state, shard, body=body, signed=True)
shard_blocks = [shard_block]
# Create a shard_transitions that would be included at beacon block `state.slot + target_len_offset_slot`
shard_transitions = build_shard_transitions_till_slot(
spec,
state,
shard_blocks={shard: shard_blocks},
on_time_slot=state.slot + target_len_offset_slot,
)
shard_transition = shard_transitions[shard]
# Create an attestation that would be included at beacon block `state.slot + target_len_offset_slot`
attestation = build_attestation_with_shard_transition(
spec,
state,
index=committee_index,
on_time_slot=state.slot + target_len_offset_slot,
shard_transition=shard_transition,
)
pre_gasprice = state.shard_states[shard].gasprice
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)
if valid:
# After state transition,
assert state.slot == slot_x + target_len_offset_slot
shard_state = state.shard_states[shard]
assert shard_state != pre_shard_state
assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1]
if target_len_offset_slot == 1:
assert shard_state.gasprice > pre_gasprice
@with_all_phases_except([PHASE0])
@spec_state_test
@always_bls
def test_basic_crosslinks(spec, state):
yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=1, valid=True)
@with_all_phases_except([PHASE0])
@spec_state_test
@always_bls
def test_multiple_offset_slots(spec, state):
yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=3, valid=True)

View File

@ -0,0 +1,107 @@
from typing import Dict, Sequence
from eth2spec.test.context import (
PHASE0,
with_all_phases_except,
spec_state_test,
always_bls,
)
from eth2spec.test.helpers.block import build_empty_block
from eth2spec.test.helpers.shard_block import (
build_attestation_with_shard_transition,
build_shard_block,
build_shard_transitions_till_slot,
)
from eth2spec.test.helpers.state import state_transition_and_sign_block, transition_to_valid_shard_slot
def run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_offset_slot, committee_index, valid=True):
shard_transitions = build_shard_transitions_till_slot(
spec, state, shard_blocks, on_time_slot=state.slot + target_len_offset_slot
)
attestations = [
build_attestation_with_shard_transition(
spec,
state,
on_time_slot=state.slot + target_len_offset_slot,
index=committee_index,
shard_transition=shard_transitions[shard],
)
for shard in shard_blocks.keys()
]
# Propose beacon block at slot `x + 1`
beacon_block = build_empty_block(spec, state, slot=state.slot + target_len_offset_slot)
beacon_block.body.attestations = attestations
beacon_block.body.shard_transitions = shard_transitions
pre_shard_states = state.shard_states.copy()
yield 'pre', state.copy()
yield 'block', beacon_block
state_transition_and_sign_block(spec, state, beacon_block)
if valid:
yield 'post', state
else:
yield 'post', None
return
for shard in range(spec.get_active_shard_count(state)):
post_shard_state = state.shard_states[shard]
if shard in shard_blocks:
# Shard state has been changed to state_transition result
assert post_shard_state == shard_transitions[shard].shard_states[
len(shard_transitions[shard].shard_states) - 1
]
assert beacon_block.slot == shard_transitions[shard].shard_states[0].slot + target_len_offset_slot
assert post_shard_state.slot == state.slot - 1
if len(shard_blocks[shard]) == 0:
# `latest_block_root` is the same
assert post_shard_state.latest_block_root == pre_shard_states[shard].latest_block_root
@with_all_phases_except([PHASE0])
@spec_state_test
@always_bls
def test_process_beacon_block_with_normal_shard_transition(spec, state):
state = transition_to_valid_shard_slot(spec, state)
target_len_offset_slot = 1
committee_index = spec.CommitteeIndex(0)
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
assert state.shard_states[shard].slot == state.slot - 1
pre_gasprice = state.shard_states[shard].gasprice
# Create SignedShardBlock at slot `shard_state.slot + 1`
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
shard_block = build_shard_block(spec, state, shard, body=body, signed=True)
shard_blocks: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]}
yield from run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_offset_slot, committee_index)
shard_state = state.shard_states[shard]
if target_len_offset_slot == 1 and len(shard_blocks) > 0:
assert shard_state.gasprice > pre_gasprice
@with_all_phases_except([PHASE0])
@spec_state_test
@always_bls
def test_process_beacon_block_with_empty_proposal_transition(spec, state):
state = transition_to_valid_shard_slot(spec, state)
target_len_offset_slot = 1
committee_index = spec.CommitteeIndex(0)
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
assert state.shard_states[shard].slot == state.slot - 1
# No new shard block
shard_blocks = {}
pre_gasprice = state.shard_states[shard].gasprice
yield from run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_offset_slot, committee_index)
if target_len_offset_slot == 1 and len(shard_blocks) > 0:
assert state.shard_states[shard].gasprice > pre_gasprice

View File

@ -1,3 +1,4 @@
py_ecc==2.0.0
eth-utils==1.6.0
../../core/gen_helpers
../../../

View File

@ -5,7 +5,7 @@ from gen_base import gen_runner, gen_typing
from gen_from_tests.gen import generate_from_tests
from eth2spec.test.context import PHASE0
from eth2spec.test.sanity import test_blocks, test_slots
from eth2spec.test.phase_0.sanity import test_blocks, test_slots
from eth2spec.config import config_util
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.phase1 import spec as spec_phase1