Skip to content

Commit

Permalink
Merge pull request #1044 from ethereum/JustinDrake-patch-21
Browse files Browse the repository at this point in the history
Crosslink in AttestationData
  • Loading branch information
djrtwo authored May 8, 2019
2 parents 67e733d + 13d2ee6 commit af2bb7d
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 144 deletions.
4 changes: 2 additions & 2 deletions configs/constant_presets/mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**11 (= 2,048) epochs 9 days
PERSISTENT_COMMITTEE_PERIOD: 2048
# 2**6 (= 64) epochs ~7 hours
MAX_CROSSLINK_EPOCHS: 64
MAX_EPOCHS_PER_CROSSLINK: 64
# 2**2 (= 4) epochs 25.6 minutes
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4

Expand Down Expand Up @@ -124,4 +124,4 @@ DOMAIN_RANDAO: 1
DOMAIN_ATTESTATION: 2
DOMAIN_DEPOSIT: 3
DOMAIN_VOLUNTARY_EXIT: 4
DOMAIN_TRANSFER: 5
DOMAIN_TRANSFER: 5
4 changes: 2 additions & 2 deletions configs/constant_presets/minimal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**11 (= 2,048) epochs 9 days
PERSISTENT_COMMITTEE_PERIOD: 2048
# 2**6 (= 64) epochs ~7 hours
MAX_CROSSLINK_EPOCHS: 64
MAX_EPOCHS_PER_CROSSLINK: 64
# 2**2 (= 4) epochs 25.6 minutes
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4

Expand Down Expand Up @@ -123,4 +123,4 @@ DOMAIN_RANDAO: 1
DOMAIN_ATTESTATION: 2
DOMAIN_DEPOSIT: 3
DOMAIN_VOLUNTARY_EXIT: 4
DOMAIN_TRANSFER: 5
DOMAIN_TRANSFER: 5
91 changes: 38 additions & 53 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ These configurations are updated for releases, but may be out of sync during `de
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours |
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours |
| `MAX_EPOCHS_PER_CROSSLINK` | `2**6` (= 64) | epochs | ~7 hours |
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes |

* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`
* `MAX_EPOCHS_PER_CROSSLINK` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`

### State list lengths

Expand Down Expand Up @@ -282,12 +282,14 @@ The types are defined topologically to aid in facilitating an executable version

```python
{
# Shard number
'shard': 'uint64',
# Epoch number
'epoch': 'uint64',
# Root of the previous crosslink
'previous_crosslink_root': 'bytes32',
'parent_root': 'bytes32',
# Root of the crosslinked shard data since the previous crosslink
'crosslink_data_root': 'bytes32',
'data_root': 'bytes32',
}
```

Expand Down Expand Up @@ -318,9 +320,7 @@ The types are defined topologically to aid in facilitating an executable version
'target_root': 'bytes32',

# Crosslink vote
'shard': 'uint64',
'previous_crosslink_root': 'bytes32',
'crosslink_data_root': 'bytes32',
'crosslink': Crosslink,
}
```

Expand Down Expand Up @@ -369,9 +369,9 @@ The types are defined topologically to aid in facilitating an executable version
```python
{
'slot': 'uint64',
'previous_block_root': 'bytes32',
'parent_root': 'bytes32',
'state_root': 'bytes32',
'block_body_root': 'bytes32',
'body_root': 'bytes32',
'signature': 'bytes96',
}
```
Expand Down Expand Up @@ -536,7 +536,7 @@ The types are defined topologically to aid in facilitating an executable version
{
# Header
'slot': 'uint64',
'previous_block_root': 'bytes32',
'parent_root': 'bytes32',
'state_root': 'bytes32',
'body': BeaconBlockBody,
'signature': 'bytes96',
Expand Down Expand Up @@ -767,7 +767,7 @@ def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
```python
def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
committee_count = get_epoch_committee_count(state, data.target_epoch)
offset = (data.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT
offset = (data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT
return get_epoch_start_slot(data.target_epoch) + offset // (committee_count // SLOTS_PER_EPOCH)
```

Expand Down Expand Up @@ -929,7 +929,7 @@ def get_attesting_indices(state: BeaconState,
"""
Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
"""
committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.shard)
committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard)
assert verify_bitfield(bitfield, len(committee))
return sorted([index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1])
```
Expand Down Expand Up @@ -1305,34 +1305,19 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat
return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
```

```python
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
return Crosslink(
epoch=min(data.target_epoch, state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
previous_crosslink_root=data.previous_crosslink_root,
crosslink_data_root=data.crosslink_data_root,
)
```

```python
def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
shard_attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.shard == shard]
shard_crosslinks = [get_crosslink_from_attestation_data(state, a.data) for a in shard_attestations]
candidate_crosslinks = [
c for c in shard_crosslinks
if hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c))
]
if len(candidate_crosslinks) == 0:
return Crosslink(), []

def get_attestations_for(crosslink: Crosslink) -> List[PendingAttestation]:
return [a for a in shard_attestations if get_crosslink_from_attestation_data(state, a.data) == crosslink]
# Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
winning_crosslink = max(candidate_crosslinks, key=lambda crosslink: (
get_attesting_balance(state, get_attestations_for(crosslink)), crosslink.crosslink_data_root
attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard]
crosslinks = list(filter(
lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)),
[a.data.crosslink for a in attestations]
))

return winning_crosslink, get_unslashed_attesting_indices(state, get_attestations_for(winning_crosslink))
# Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
winning_crosslink = max(crosslinks, key=lambda c: (
get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root
), default=Crosslink())
winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink]
return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations)
```

#### Justification and finalization
Expand Down Expand Up @@ -1604,12 +1589,12 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
# Verify that the slots match
assert block.slot == state.slot
# Verify that the parent matches
assert block.previous_block_root == signing_root(state.latest_block_header)
assert block.parent_root == signing_root(state.latest_block_header)
# Save current block as the new latest block
state.latest_block_header = BeaconBlockHeader(
slot=block.slot,
previous_block_root=block.previous_block_root,
block_body_root=hash_tree_root(block.body),
parent_root=block.parent_root,
body_root=hash_tree_root(block.body),
)
# Verify proposer is not slashed
proposer = state.validator_registry[get_beacon_proposer_index(state)]
Expand Down Expand Up @@ -1715,29 +1700,29 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
attestation_slot = get_attestation_data_slot(state, data)
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH

# Check target epoch, source epoch, source root, and source crosslink
assert (data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) in {
(get_current_epoch(state), state.current_justified_epoch, state.current_justified_root, hash_tree_root(state.current_crosslinks[data.shard])),
(get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])),
}

# Check crosslink data root
assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1]

# Check signature and bitfields
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))

# Cache pending attestation
pending_attestation = PendingAttestation(
data=data,
aggregation_bitfield=attestation.aggregation_bitfield,
inclusion_delay=state.slot - attestation_slot,
proposer_index=get_beacon_proposer_index(state),
)

assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state))
if data.target_epoch == get_current_epoch(state):
ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state))
parent_crosslink = state.current_crosslinks[data.crosslink.shard]
state.current_epoch_attestations.append(pending_attestation)
else:
ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state))
parent_crosslink = state.previous_crosslinks[data.crosslink.shard]
state.previous_epoch_attestations.append(pending_attestation)

# Check FFG data, crosslink data, and signature
assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch)
assert data.crosslink.epoch == min(data.target_epoch, parent_crosslink.epoch + MAX_EPOCHS_PER_CROSSLINK)
assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1]
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))
```

##### Deposits
Expand Down
2 changes: 1 addition & 1 deletion specs/core/0_fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ All terminology, constants, functions, and protocol mechanics defined in the [Ph

Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Clients download and process blocks and maintain a view of what is the current "canonical chain", terminating at the current "head". For a beacon block, `block`, to be processed by a node, the following conditions must be met:

* The parent block with root `block.previous_block_root` has been processed and accepted.
* The parent block with root `block.parent_root` has been processed and accepted.
* An Ethereum 1.0 block pointed to by the `state.latest_eth1_data.block_hash` has been processed and accepted.
* The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`.

Expand Down
24 changes: 12 additions & 12 deletions specs/core/1_custody-game.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
'challenger_index': ValidatorIndex,
'responder_index': ValidatorIndex,
'deadline': Epoch,
'crosslink_data_root': Hash,
'data_root': Hash,
'depth': 'uint64',
'chunk_index': 'uint64',
}
Expand All @@ -161,7 +161,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
'challenger_index': ValidatorIndex,
'responder_index': ValidatorIndex,
'deadline': Epoch,
'crosslink_data_root': Hash,
'data_root': Hash,
'chunk_count': 'uint64',
'chunk_bits_merkle_root': Hash,
'responder_key': BLSSignature,
Expand Down Expand Up @@ -265,7 +265,7 @@ The `empty` function accepts and SSZ type as input and returns an object of that
def get_custody_chunk_count(attestation: Attestation) -> int:
crosslink_start_epoch = attestation.data.latest_crosslink.epoch
crosslink_end_epoch = slot_to_epoch(attestation.data.slot)
crosslink_crosslink_length = min(MAX_CROSSLINK_EPOCHS, end_epoch - start_epoch)
crosslink_crosslink_length = min(MAX_EPOCHS_PER_CROSSLINK, end_epoch - start_epoch)
chunks_per_epoch = 2 * BYTES_PER_SHARD_BLOCK * SLOTS_PER_EPOCH // BYTES_PER_CUSTODY_CHUNK
return crosslink_crosslink_length * chunks_per_epoch
```
Expand Down Expand Up @@ -426,10 +426,10 @@ def process_early_derived_secret_reveal(state: BeaconState,
# round key
slash_validator(state, reveal.revealed_index, reveal.masker_index)
else:
# Only a small penalty proportional to proposer slot reward for RANDAO reveal
# Only a small penalty proportional to proposer slot reward for RANDAO reveal
# that does not interfere with the custody period
# The penalty is proportional to the max proposer reward
# The penalty is proportional to the max proposer reward

# Calculate penalty
max_proposer_slot_reward = (
get_base_reward(state, reveal.revealed_index) *
Expand All @@ -448,7 +448,7 @@ def process_early_derived_secret_reveal(state: BeaconState,
increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward)
decrease_balance(state, reveal.revealed_index, penalty)

# Mark this derived secret as exposed so validator cannot be punished repeatedly
# Mark this derived secret as exposed so validator cannot be punished repeatedly
state.exposed_derived_secrets[reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS].append(reveal.revealed_index)

```
Expand All @@ -474,7 +474,7 @@ def process_chunk_challenge(state: BeaconState,
# Verify the challenge is not a duplicate
for record in state.custody_chunk_challenge_records:
assert (
record.crosslink_data_root != challenge.attestation.data.crosslink_data_root or
record.data_root != challenge.attestation.data.crosslink.data_root or
record.chunk_index != challenge.chunk_index
)
# Verify depth
Expand All @@ -486,7 +486,7 @@ def process_chunk_challenge(state: BeaconState,
challenger_index=get_beacon_proposer_index(state),
responder_index=challenge.responder_index
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
data_root=challenge.attestation.data.crosslink.data_root,
depth=depth,
chunk_index=challenge.chunk_index,
)
Expand Down Expand Up @@ -564,7 +564,7 @@ def process_bit_challenge(state: BeaconState,
challenger_index=challenge.challenger_index,
responder_index=challenge.responder_index,
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
data_root=challenge.attestation.data.crosslink.data_root,
chunk_count=chunk_count,
chunk_bits_merkle_root=merkle_root(pad_to_power_of_2((challenge.chunk_bits))),
responder_key=challenge.responder_key,
Expand Down Expand Up @@ -610,7 +610,7 @@ def process_chunk_challenge_response(state: BeaconState,
branch=response.data_branch,
depth=challenge.depth,
index=response.chunk_index,
root=challenge.crosslink_data_root,
root=challenge.data_root,
)
# Clear the challenge
records = state.custody_chunk_challenge_records
Expand All @@ -632,7 +632,7 @@ def process_bit_challenge_response(state: BeaconState,
branch=response.data_branch,
depth=math.log2(next_power_of_two(challenge.chunk_count)),
index=response.chunk_index,
root=challenge.crosslink_data_root,
root=challenge.data_root,
)
# Verify the chunk bit leaf matches the challenge data
assert verify_merkle_branch(
Expand Down
Loading

0 comments on commit af2bb7d

Please sign in to comment.