Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attestation changes + persistent committee changes #1294

Merged
merged 32 commits into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
99a5060
Minimal attestation simplification
vbuterin Jul 13, 2019
14010f5
minor fix
hwwhww Jul 15, 2019
0cfca01
Make the tests pass
hwwhww Jul 15, 2019
f77df3f
Decrease `PLACEHOLDER`, Use `compute_epoch_of_shard_slot`
hwwhww Jul 15, 2019
bcfff59
Fix proposer signature name and use get_seed() to calculate current_s…
hwwhww Jul 15, 2019
be1bc52
Fix linter error
hwwhww Jul 15, 2019
7ca5031
Add the WIP `test_is_valid_shard_block`
hwwhww Jul 15, 2019
ac29581
Add `get_shard_block_attester_committee`
hwwhww Jul 16, 2019
2b2b143
Simplified committee selection
vbuterin Jul 18, 2019
b5de66a
Added some helpers and simplified
vbuterin Jul 19, 2019
8611f91
Update specs/core/1_shard-data-chains.md
vbuterin Jul 19, 2019
eb03f03
Update 1_shard-data-chains.md
vbuterin Jul 19, 2019
1bc65ca
Merge branch 'dev' into vitalik88
hwwhww Jul 23, 2019
8f0ad70
Simplified switchover epochs, changed block structure, changed crossl…
vbuterin Jul 23, 2019
6812e46
Update 1_shard-data-chains.md
vbuterin Jul 24, 2019
827b181
Moved balance dependency to proposer selection
vbuterin Jul 24, 2019
96b3cb9
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
d33ee7c
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
b859b26
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
8299399
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
2af058f
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
4951768
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
7f2e773
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
cc80342
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
8ba1408
Fixed shard header flattening
vbuterin Jul 24, 2019
f523ddd
Update specs/core/1_shard-data-chains.md
vbuterin Jul 24, 2019
c7f8b11
Minor fixes
vbuterin Jul 24, 2019
7df91f8
Update specs/core/1_shard-data-chains.md
vbuterin Jul 25, 2019
e035344
Update specs/core/1_shard-data-chains.md
vbuterin Jul 25, 2019
dc77c06
cleanup testing and lint
djrtwo Jul 25, 2019
ad30945
return none if not active validators in persistent committee
djrtwo Jul 25, 2019
21f4e03
only allow active validators as shard proposer
djrtwo Jul 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 56 additions & 89 deletions specs/core/1_shard-data-chains.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Ethereum 2.0 Phase 1 -- Shard Data Chains](#ethereum-20-phase-1----shard-data-chains)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Configuration](#configuration)
- [Misc](#misc)
- [Initial values](#initial-values)
Expand All @@ -17,20 +18,18 @@
- [TODO PLACEHOLDER](#todo-placeholder)
- [Data structures](#data-structures)
- [`ShardBlockBody`](#shardblockbody)
- [`ShardAttestation`](#shardattestation)
- [`ShardBlock`](#shardblock)
- [`ShardBlockHeader`](#shardblockheader)
- [Helper functions](#helper-functions)
- [`compute_epoch_of_shard_slot`](#compute_epoch_of_shard_slot)
- [`get_period_committee`](#get_period_committee)
- [`get_switchover_epoch`](#get_switchover_epoch)
- [`get_persistent_committee`](#get_persistent_committee)
- [`get_shard_proposer_index`](#get_shard_proposer_index)
- [`get_shard_block_proposer_index`](#get_shard_block_proposer_index)
- [`get_shard_header`](#get_shard_header)
- [`verify_shard_attestation_signature`](#verify_shard_attestation_signature)
- [`compute_crosslink_data_root`](#compute_crosslink_data_root)
- [Object validity](#object-validity)
- [Shard blocks](#shard-blocks)
- [Shard attestations](#shard-attestations)
- [Beacon attestations](#beacon-attestations)
- [Shard fork choice rule](#shard-fork-choice-rule)

Expand All @@ -40,6 +39,14 @@

This document describes the shard data layer and the shard fork choice rule in Phase 1 of Ethereum 2.0.

## Custom types

We define the following Python custom types for type hinting and readability:

| Name | SSZ equivalent | Description |
| - | - | - |
| `ShardSlot` | `uint64` | a slot number in shard chain |

## Configuration

### Misc
Expand All @@ -48,6 +55,8 @@ This document describes the shard data layer and the shard fork choice rule in P
| - | - |
| `BYTES_PER_SHARD_BLOCK_BODY` | `2**14` (= 16,384) |
| `MAX_SHARD_ATTESTIONS` | `2**4` (= 16) |
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
| `SHARD_SLOTS_PER_BEACON_SLOT` | `2**1` (= 2) |
| `SHARD_SLOT_COMMITTEE_SIZE` | `2**5` (= 32) |

### Initial values

Expand Down Expand Up @@ -75,7 +84,7 @@ The following types are defined, mapping into `DomainType` (little endian):

| Name | Value |
| - | - |
| `PLACEHOLDER` | `2**32` |
| `PLACEHOLDER` | `2**3` |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| `PLACEHOLDER` | `2**3` |

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the whole ### TODO PLACEHOLDER section.


## Data structures

Expand All @@ -86,48 +95,45 @@ class ShardBlockBody(Container):
data: Vector[Bytes[PLACEHOLDER], BYTES_PER_SHARD_BLOCK_BODY]
```

### `ShardAttestation`

```python
class ShardAttestation(Container):
class data(Container):
slot: Slot
shard: Shard
shard_block_root: Hash
aggregation_bits: Bitlist[PLACEHOLDER]
aggregate_signature: BLSSignature
```

### `ShardBlock`

```python
class ShardBlock(Container):
slot: Slot
slot: ShardSlot
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
shard: Shard
beacon_chain_root: Hash
parent_root: Hash
data: ShardBlockBody
state_root: Hash
attestations: List[ShardAttestation, PLACEHOLDER]
attester_bitfield: Bitvector[SHARD_SLOT_COMMITTEE_SIZE]
attestation_signature: BLSSignature
signature: BLSSignature
```

### `ShardBlockHeader`

```python
class ShardBlockHeader(Container):
slot: Slot
slot: ShardSlot
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
shard: Shard
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are losing value by removing shard here by not being able to immediately recognize a shard block as being from a particular shard and instead just understanding it in the context of it's parent and the state (that must be calculated) related to the state_root.

We still have 80 bytes to play with in ShardBlockCore now that we have divided it into two 256-byte halves

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that; I'd be ok with putting it back in (though we'd have to take it back out if we decide we want to add another slot as then we would exceed 8; or we could add slot offsets for different shards 😄)

beacon_chain_root: Hash
parent_root: Hash
body_root: Hash
state_root: Hash
attestations: List[ShardAttestation, PLACEHOLDER]
attester_bitfield: Bitvector[SHARD_SLOT_COMMITTEE_SIZE]
attestation_signature: BLSSignature
signature: BLSSignature
```

## Helper functions

### `compute_epoch_of_shard_slot`

```python
def compute_epoch_of_shard_slot(slot: ShardSlot) -> Epoch:
return Epoch(slot // SHARD_SLOTS_PER_BEACON_SLOT // SLOTS_PER_EPOCH)
```

### `get_period_committee`

```python
Expand Down Expand Up @@ -161,11 +167,11 @@ def get_switchover_epoch(state: BeaconState, epoch: Epoch, index: ValidatorIndex
```python
def get_persistent_committee(state: BeaconState,
shard: Shard,
slot: Slot) -> Sequence[ValidatorIndex]:
slot: ShardSlot) -> Sequence[ValidatorIndex]:
"""
Return the persistent committee for the given ``shard`` at the given ``slot``.
"""
epoch = compute_epoch_of_slot(slot)
epoch = compute_epoch_of_shard_slot(slot)
earlier_start_epoch = Epoch(epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2)
later_start_epoch = Epoch(epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD)

Expand All @@ -188,15 +194,17 @@ def get_persistent_committee(state: BeaconState,
)))
```

### `get_shard_proposer_index`
### `get_shard_block_proposer_index`

```python
def get_shard_proposer_index(state: BeaconState,
shard: Shard,
slot: Slot) -> Optional[ValidatorIndex]:
def get_shard_block_proposer_index(state: BeaconState,
shard: Shard,
slot: ShardSlot) -> Optional[ValidatorIndex]:
# Randomly shift persistent committee
persistent_committee = list(get_persistent_committee(state, shard, slot))
seed = hash(state.current_shuffling_seed + int_to_bytes(shard, length=8) + int_to_bytes(slot, length=8))
seed = hash(
get_seed(state, get_current_epoch(state)) + int_to_bytes(shard, length=8) + int_to_bytes(slot, length=8)
)
random_index = bytes_to_int(seed[0:8]) % len(persistent_committee)
persistent_committee = persistent_committee[random_index:] + persistent_committee[:random_index]

Expand All @@ -220,32 +228,11 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader:
parent_root=block.parent_root,
body_root=hash_tree_root(block.body),
state_root=block.state_root,
attestations=block.attestations,
attestation_signature=block.attestation_signature,
signature=block.signature,
)
```

### `verify_shard_attestation_signature`

```python
def verify_shard_attestation_signature(state: BeaconState,
attestation: ShardAttestation) -> None:
data = attestation.data
persistent_committee = get_persistent_committee(state, data.shard, data.slot)
pubkeys = []
for i, index in enumerate(persistent_committee):
if attestation.aggregation_bits[i]:
validator = state.validators[index]
assert is_active_validator(validator, get_current_epoch(state))
pubkeys.append(validator.pubkey)
assert bls_verify(
pubkey=bls_aggregate_pubkeys(pubkeys),
message_hash=data.shard_block_root,
signature=attestation.aggregate_signature,
domain=get_domain(state, DOMAIN_SHARD_ATTESTER, compute_epoch_of_slot(data.slot))
)
```

### `compute_crosslink_data_root`

```python
Expand Down Expand Up @@ -290,8 +277,8 @@ Let:
- `candidate` be a candidate `ShardBlock` for which validity is to be determined by running `is_valid_shard_block`

```python
def is_valid_shard_block(beacon_blocks: Sequence[BeaconBlock],
beacon_state: BeaconState,
def is_valid_shard_block(beacon_state: BeaconState,
beacon_blocks: Sequence[BeaconBlock],
valid_shard_blocks: Sequence[ShardBlock],
candidate: ShardBlock) -> bool:
# Check if block is already determined valid
Expand Down Expand Up @@ -327,53 +314,33 @@ def is_valid_shard_block(beacon_blocks: Sequence[BeaconBlock],
assert signing_root(beacon_blocks[parent_block.slot]) == parent_block.beacon_chain_root

# Check attestations
assert len(candidate.attestations) <= MAX_SHARD_ATTESTIONS
for _, attestation in enumerate(candidate.attestations):
assert max(GENESIS_SHARD_SLOT, candidate.slot - SLOTS_PER_EPOCH) <= attestation.data.slot
assert attestation.data.slot <= candidate.slot - MIN_ATTESTATION_INCLUSION_DELAY
assert attestation.data.crosslink.shard == candidate.shard
verify_shard_attestation_signature(beacon_state, attestation)

# Check signature
proposer_index = get_shard_proposer_index(beacon_state, candidate.shard, candidate.slot)
persistent_committee = get_persistent_committee(beacon_state, block.shard, block.slot)
pubkeys = []
for i, index in enumerate(persistent_committee):
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
if block.attester_bitfield[i]:
validator = beacon_state.validators[index]
assert is_active_validator(validator, get_current_epoch(beacon_state))
pubkeys.append(validator.pubkey)
assert bls_verify(
pubkey=bls_aggregate_pubkeys(pubkeys),
message_hash=candidate.parent_root,
signature=candidate.attestation_signature,
domain=get_domain(beacon_state, DOMAIN_SHARD_ATTESTER, compute_epoch_of_shard_slot(candidate.slot))
)

# Check proposer
proposer_index = get_shard_block_proposer_index(beacon_state, candidate.shard, candidate.slot)
assert proposer_index is not None
assert bls_verify(
pubkey=beacon_state.validators[proposer_index].pubkey,
message_hash=signing_root(candidate),
signature=candidate.signature,
domain=get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_slot(candidate.slot)),
domain=get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(candidate.slot)),
)

return True
```

### Shard attestations

Let:

- `valid_shard_blocks` be the list of valid `ShardBlock`
- `beacon_state` be the canonical `BeaconState`
- `candidate` be a candidate `ShardAttestation` for which validity is to be determined by running `is_valid_shard_attestation`

```python
def is_valid_shard_attestation(valid_shard_blocks: Sequence[ShardBlock],
beacon_state: BeaconState,
candidate: ShardAttestation) -> bool:
# Check shard block
shard_block = next(
(block for block in valid_shard_blocks if signing_root(block) == candidate.data.shard_block_root),
None,
)
assert shard_block is not None
assert shard_block.slot == candidate.data.slot
assert shard_block.shard == candidate.data.shard

# Check signature
verify_shard_attestation_signature(beacon_state, candidate)

return True
```

### Beacon attestations

Let:
Expand Down
Empty file.
43 changes: 43 additions & 0 deletions test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils.bls import (
bls_sign,
only_with_bls,
)
from eth2spec.utils.ssz.ssz_impl import (
signing_root,
)


@only_with_bls()
def sign_shard_block(spec, state, block, proposer_index=None):
if proposer_index is None:
proposer_index = spec.get_shard_block_proposer_index(state, block.shard, block.slot)

privkey = privkeys[proposer_index]

block.signature = bls_sign(
message_hash=signing_root(block),
privkey=privkey,
domain=spec.get_domain(
state,
spec.DOMAIN_SHARD_PROPOSER,
spec.compute_epoch_of_shard_slot(block.slot),
)
)


def build_empty_shard_block(spec, state, slot, shard, parent_root, signed=False):
if slot is None:
slot = state.slot
block = spec.ShardBlock(
slot=slot,
shard=shard,
beacon_chain_root=state.block_roots[state.slot // spec.SLOTS_PER_HISTORICAL_ROOT],
parent_root=parent_root,
signature=b'\x12' * 96,
)

if signed:
sign_shard_block(spec, state, block)

return block
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from eth2spec.test.helpers.phase1.shard_block import (
build_empty_shard_block,
)
from eth2spec.test.context import (
with_all_phases_except,
spec_state_test,
always_bls,
)


@with_all_phases_except(['phase0'])
@always_bls
@spec_state_test
def test_is_valid_shard_block(spec, state):
block = build_empty_shard_block(
spec,
state,
slot=spec.Slot(spec.PERSISTENT_COMMITTEE_PERIOD * 100),
shard=spec.Shard(1),
parent_root=spec.Hash(),
signed=True,
)

# TODO: test `is_valid_shard_block`

yield 'blocks', (block,)