Merge pull request #522 from ethereum/JustinDrake-patch-1
Attester slashing fixes
This commit is contained in:
commit
f5039a0e59
|
@ -26,7 +26,7 @@
|
||||||
- [`ProposerSlashing`](#proposerslashing)
|
- [`ProposerSlashing`](#proposerslashing)
|
||||||
- [Attester slashings](#attester-slashings)
|
- [Attester slashings](#attester-slashings)
|
||||||
- [`AttesterSlashing`](#attesterslashing)
|
- [`AttesterSlashing`](#attesterslashing)
|
||||||
- [`SlashableVote`](#slashablevote)
|
- [`SlashableAttestation`](#slashableattestation)
|
||||||
- [Attestations](#attestations)
|
- [Attestations](#attestations)
|
||||||
- [`Attestation`](#attestation)
|
- [`Attestation`](#attestation)
|
||||||
- [`AttestationData`](#attestationdata)
|
- [`AttestationData`](#attestationdata)
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
- [`get_domain`](#get_domain)
|
- [`get_domain`](#get_domain)
|
||||||
- [`get_bitfield_bit`](#get_bitfield_bit)
|
- [`get_bitfield_bit`](#get_bitfield_bit)
|
||||||
- [`verify_bitfield`](#verify_bitfield)
|
- [`verify_bitfield`](#verify_bitfield)
|
||||||
- [`verify_slashable_vote`](#verify_slashable_vote)
|
- [`verify_slashable_attestation`](#verify_slashable_attestation)
|
||||||
- [`is_double_vote`](#is_double_vote)
|
- [`is_double_vote`](#is_double_vote)
|
||||||
- [`is_surround_vote`](#is_surround_vote)
|
- [`is_surround_vote`](#is_surround_vote)
|
||||||
- [`integer_squareroot`](#integer_squareroot)
|
- [`integer_squareroot`](#integer_squareroot)
|
||||||
|
@ -298,23 +298,23 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# First batch of votes
|
# First slashable attestation
|
||||||
'slashable_vote_1': SlashableVote,
|
'slashable_attestation_1': SlashableAttestation,
|
||||||
# Second batch of votes
|
# Second slashable attestation
|
||||||
'slashable_vote_2': SlashableVote,
|
'slashable_attestation_2': SlashableAttestation,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### `SlashableVote`
|
##### `SlashableAttestation`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Validator indices
|
# Validator indices
|
||||||
'validator_indices': '[uint64]',
|
'validator_indices': '[uint64]',
|
||||||
# Custody bitfield
|
|
||||||
'custody_bitfield': 'bytes',
|
|
||||||
# Attestation data
|
# Attestation data
|
||||||
'data': AttestationData,
|
'data': AttestationData,
|
||||||
|
# Custody bitfield
|
||||||
|
'custody_bitfield': 'bytes',
|
||||||
# Aggregate signature
|
# Aggregate signature
|
||||||
'aggregate_signature': 'bytes96',
|
'aggregate_signature': 'bytes96',
|
||||||
}
|
}
|
||||||
|
@ -326,10 +326,10 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Attestation data
|
|
||||||
'data': AttestationData,
|
|
||||||
# Attester aggregation bitfield
|
# Attester aggregation bitfield
|
||||||
'aggregation_bitfield': 'bytes',
|
'aggregation_bitfield': 'bytes',
|
||||||
|
# Attestation data
|
||||||
|
'data': AttestationData,
|
||||||
# Custody bitfield
|
# Custody bitfield
|
||||||
'custody_bitfield': 'bytes',
|
'custody_bitfield': 'bytes',
|
||||||
# BLS aggregate signature
|
# BLS aggregate signature
|
||||||
|
@ -552,14 +552,14 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# Signed data
|
|
||||||
'data': AttestationData,
|
|
||||||
# Attester aggregation bitfield
|
# Attester aggregation bitfield
|
||||||
'aggregation_bitfield': 'bytes',
|
'aggregation_bitfield': 'bytes',
|
||||||
|
# Attestation data
|
||||||
|
'data': AttestationData,
|
||||||
# Custody bitfield
|
# Custody bitfield
|
||||||
'custody_bitfield': 'bytes',
|
'custody_bitfield': 'bytes',
|
||||||
# Slot the attestation was included
|
# Inclusion slot
|
||||||
'slot_included': 'uint64',
|
'inclusion_slot': 'uint64',
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1012,33 +1012,33 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool:
|
||||||
return True
|
return True
|
||||||
```
|
```
|
||||||
|
|
||||||
### `verify_slashable_vote`
|
### `verify_slashable_attestation`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def verify_slashable_vote(state: BeaconState, slashable_vote: SlashableVote) -> bool:
|
def verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation) -> bool:
|
||||||
"""
|
"""
|
||||||
Verify validity of ``slashable_vote`` fields.
|
Verify validity of ``slashable_attestation`` fields.
|
||||||
"""
|
"""
|
||||||
if slashable_vote.custody_bitfield != b'\x00' * len(slashable_vote.custody_bitfield): # [TO BE REMOVED IN PHASE 1]
|
if slashable_attestation.custody_bitfield != b'\x00' * len(slashable_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1]
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(slashable_vote.validator_indices) == 0:
|
if len(slashable_attestation.validator_indices) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for i in range(len(slashable_vote.validator_indices) - 1):
|
for i in range(len(slashable_attestation.validator_indices) - 1):
|
||||||
if slashable_vote.validator_indices[i] >= slashable_vote.validator_indices[i + 1]:
|
if slashable_attestation.validator_indices[i] >= slashable_attestation.validator_indices[i + 1]:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not verify_bitfield(slashable_vote.custody_bitfield, len(slashable_vote.validator_indices)):
|
if not verify_bitfield(slashable_attestation.custody_bitfield, len(slashable_attestation.validator_indices)):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(slashable_vote.validator_indices) > MAX_INDICES_PER_SLASHABLE_VOTE:
|
if len(slashable_attestation.validator_indices) > MAX_INDICES_PER_SLASHABLE_VOTE:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
custody_bit_0_indices = []
|
custody_bit_0_indices = []
|
||||||
custody_bit_1_indices = []
|
custody_bit_1_indices = []
|
||||||
for i, validator_index in enumerate(slashable_vote.validator_indices):
|
for i, validator_index in enumerate(slashable_attestation.validator_indices):
|
||||||
if get_bitfield_bit(slashable_vote.custody_bitfield, i) == 0b0:
|
if get_bitfield_bit(slashable_attestation.custody_bitfield, i) == 0b0:
|
||||||
custody_bit_0_indices.append(validator_index)
|
custody_bit_0_indices.append(validator_index)
|
||||||
else:
|
else:
|
||||||
custody_bit_1_indices.append(validator_index)
|
custody_bit_1_indices.append(validator_index)
|
||||||
|
@ -1049,10 +1049,10 @@ def verify_slashable_vote(state: BeaconState, slashable_vote: SlashableVote) ->
|
||||||
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]),
|
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]),
|
||||||
],
|
],
|
||||||
messages=[
|
messages=[
|
||||||
hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_vote.data, custody_bit=0b0)),
|
hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_attestation.data, custody_bit=0b0)),
|
||||||
hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_vote.data, custody_bit=0b1)),
|
hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_attestation.data, custody_bit=0b1)),
|
||||||
],
|
],
|
||||||
signature=slashable_vote.aggregate_signature,
|
signature=slashable_attestation.aggregate_signature,
|
||||||
domain=get_domain(
|
domain=get_domain(
|
||||||
state.fork,
|
state.fork,
|
||||||
slot_to_epoch(vote_data.data.slot),
|
slot_to_epoch(vote_data.data.slot),
|
||||||
|
@ -1067,9 +1067,7 @@ def verify_slashable_vote(state: BeaconState, slashable_vote: SlashableVote) ->
|
||||||
def is_double_vote(attestation_data_1: AttestationData,
|
def is_double_vote(attestation_data_1: AttestationData,
|
||||||
attestation_data_2: AttestationData) -> bool
|
attestation_data_2: AttestationData) -> bool
|
||||||
"""
|
"""
|
||||||
Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``.
|
Checks if the two ``AttestationData`` have the same target.
|
||||||
Returns True if the provided ``AttestationData`` are slashable
|
|
||||||
due to a 'double vote'.
|
|
||||||
"""
|
"""
|
||||||
target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
|
target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
|
||||||
target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
|
target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
|
||||||
|
@ -1082,11 +1080,7 @@ def is_double_vote(attestation_data_1: AttestationData,
|
||||||
def is_surround_vote(attestation_data_1: AttestationData,
|
def is_surround_vote(attestation_data_1: AttestationData,
|
||||||
attestation_data_2: AttestationData) -> bool:
|
attestation_data_2: AttestationData) -> bool:
|
||||||
"""
|
"""
|
||||||
Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``.
|
Checks if ``attestation_data_1`` surrounds ``attestation_data_2``.
|
||||||
Returns True if the provided ``AttestationData`` are slashable
|
|
||||||
due to a 'surround vote'.
|
|
||||||
Note: parameter order matters as this function only checks
|
|
||||||
that ``attestation_data_1`` surrounds ``attestation_data_2``.
|
|
||||||
"""
|
"""
|
||||||
source_epoch_1 = attestation_data_1.justified_epoch
|
source_epoch_1 = attestation_data_1.justified_epoch
|
||||||
source_epoch_2 = attestation_data_2.justified_epoch
|
source_epoch_2 = attestation_data_2.justified_epoch
|
||||||
|
@ -1598,15 +1592,15 @@ Verify that `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS`.
|
||||||
|
|
||||||
For each `attester_slashing` in `block.body.attester_slashings`:
|
For each `attester_slashing` in `block.body.attester_slashings`:
|
||||||
|
|
||||||
* Let `slashable_vote_1 = attester_slashing.slashable_vote_1`.
|
* Let `slashable_attestation_1 = attester_slashing.slashable_attestation_1`.
|
||||||
* Let `slashable_vote_2 = attester_slashing.slashable_vote_2`.
|
* Let `slashable_attestation_2 = attester_slashing.slashable_attestation_2`.
|
||||||
* Let `intersection = [x for x in slashable_vote_1.validator_indices if x in slashable_vote_2.validator_indices]`.
|
* Verify that `slashable_attestation_1.data != slashable_attestation_2.data`.
|
||||||
* Verify that `len(intersection) >= 1`.
|
* Verify that `is_double_vote(slashable_attestation_1.data, slashable_attestation_2.data)` or `is_surround_vote(slashable_attestation_1.data, slashable_attestation_2.data)`.
|
||||||
* Verify that `slashable_vote_1.data != slashable_vote_2.data`.
|
* Verify that `verify_slashable_attestation(state, slashable_attestation_1)`.
|
||||||
* Verify that `is_double_vote(slashable_vote_1.data, slashable_vote_2.data)` or `is_surround_vote(slashable_vote_1.data, slashable_vote_2.data)`.
|
* Verify that `verify_slashable_attestation(state, slashable_attestation_2)`.
|
||||||
* Verify that `verify_slashable_vote(state, slashable_vote_1)`.
|
* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].penalized_epoch > get_current_epoch(state)]`.
|
||||||
* Verify that `verify_slashable_vote(state, slashable_vote_2)`.
|
* Verify that `len(slashable_indices) >= 1`.
|
||||||
* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_epoch > get_current_epoch(state)`.
|
* Run `penalize_validator(state, index)` for each `index` in `slashable_indices`.
|
||||||
|
|
||||||
##### Attestations
|
##### Attestations
|
||||||
|
|
||||||
|
@ -1647,7 +1641,7 @@ For each `attestation` in `block.body.attestations`:
|
||||||
```
|
```
|
||||||
|
|
||||||
* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`.
|
* [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.shard_block_root == ZERO_HASH`.
|
||||||
* Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot)` to `state.latest_attestations`.
|
* Append `PendingAttestation(data=attestation.data, aggregation_bitfield=attestation.aggregation_bitfield, custody_bitfield=attestation.custody_bitfield, inclusion_slot=state.slot)` to `state.latest_attestations`.
|
||||||
|
|
||||||
##### Deposits
|
##### Deposits
|
||||||
|
|
||||||
|
@ -1748,8 +1742,8 @@ For every `slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_s
|
||||||
|
|
||||||
Define the following helpers to process attestation inclusion rewards and inclusion distance reward/penalty. For every attestation `a` in `previous_epoch_attestations`:
|
Define the following helpers to process attestation inclusion rewards and inclusion distance reward/penalty. For every attestation `a` in `previous_epoch_attestations`:
|
||||||
|
|
||||||
* Let `inclusion_slot(state, index) = a.slot_included` for the attestation `a` where `index` is in `get_attestation_participants(state, a.data, a.aggregation_bitfield)`. If multiple attestations are applicable, the attestation with lowest `slot_included` is considered.
|
* Let `inclusion_slot(state, index) = a.inclusion_slot` for the attestation `a` where `index` is in `get_attestation_participants(state, a.data, a.aggregation_bitfield)`. If multiple attestations are applicable, the attestation with lowest `inclusion_slot` is considered.
|
||||||
* Let `inclusion_distance(state, index) = a.slot_included - a.data.slot` where `a` is the above attestation.
|
* Let `inclusion_distance(state, index) = a.inclusion_slot - a.data.slot` where `a` is the above attestation.
|
||||||
|
|
||||||
#### Eth1 data
|
#### Eth1 data
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue