mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-01-11 19:24:15 +00:00
Merge pull request #831 from ethereum/vbuterin-patch-3
Merge attestation verification logic
This commit is contained in:
commit
30e29324e9
@ -28,7 +28,7 @@
|
||||
- [`Eth1DataVote`](#eth1datavote)
|
||||
- [`AttestationData`](#attestationdata)
|
||||
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
|
||||
- [`SlashableAttestation`](#slashableattestation)
|
||||
- [`IndexedAttestation`](#indexedattestation)
|
||||
- [`DepositData`](#depositdata)
|
||||
- [`BeaconBlockHeader`](#beaconblockheader)
|
||||
- [`Validator`](#validator)
|
||||
@ -77,6 +77,7 @@
|
||||
- [`generate_seed`](#generate_seed)
|
||||
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
|
||||
- [`verify_merkle_branch`](#verify_merkle_branch)
|
||||
- [`get_crosslink_committee_for_attestation`](#get_crosslink_committee_for_attestation)
|
||||
- [`get_attestation_participants`](#get_attestation_participants)
|
||||
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
|
||||
- [`bytes_to_int`](#bytes_to_int)
|
||||
@ -86,7 +87,8 @@
|
||||
- [`get_domain`](#get_domain)
|
||||
- [`get_bitfield_bit`](#get_bitfield_bit)
|
||||
- [`verify_bitfield`](#verify_bitfield)
|
||||
- [`verify_slashable_attestation`](#verify_slashable_attestation)
|
||||
- [`convert_to_indexed`](#convert_to_indexed)
|
||||
- [`verify_indexed_attestation`](#verify_indexed_attestation)
|
||||
- [`is_double_vote`](#is_double_vote)
|
||||
- [`is_surround_vote`](#is_surround_vote)
|
||||
- [`integer_squareroot`](#integer_squareroot)
|
||||
@ -183,7 +185,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
|
||||
| `SHARD_COUNT` | `2**10` (= 1,024) |
|
||||
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) |
|
||||
| `MAX_SLASHABLE_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
|
||||
| `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
|
||||
| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) |
|
||||
| `SHUFFLE_ROUND_COUNT` | 90 |
|
||||
|
||||
@ -369,16 +371,15 @@ The types are defined topologically to aid in facilitating an executable version
|
||||
}
|
||||
```
|
||||
|
||||
#### `SlashableAttestation`
|
||||
#### `IndexedAttestation`
|
||||
|
||||
```python
|
||||
{
|
||||
# Validator indices
|
||||
'validator_indices': ['uint64'],
|
||||
'custody_bit_0_indices': ['uint64'],
|
||||
'custody_bit_1_indices': ['uint64'],
|
||||
# Attestation data
|
||||
'data': AttestationData,
|
||||
# Custody bitfield
|
||||
'custody_bitfield': 'bytes',
|
||||
# Aggregate signature
|
||||
'aggregate_signature': 'bytes96',
|
||||
}
|
||||
@ -478,10 +479,10 @@ The types are defined topologically to aid in facilitating an executable version
|
||||
|
||||
```python
|
||||
{
|
||||
# First slashable attestation
|
||||
'slashable_attestation_1': SlashableAttestation,
|
||||
# Second slashable attestation
|
||||
'slashable_attestation_2': SlashableAttestation,
|
||||
# First attestation
|
||||
'attestation_1': IndexedAttestation,
|
||||
# Second attestation
|
||||
'attestation_2': IndexedAttestation,
|
||||
}
|
||||
```
|
||||
|
||||
@ -1036,6 +1037,20 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index:
|
||||
return value == root
|
||||
```
|
||||
|
||||
### `get_crosslink_committee_for_attestation`
|
||||
|
||||
```python
|
||||
def get_crosslink_committee_for_attestation(state: BeaconState,
|
||||
attestation_data: AttestationData) -> List[ValidatorIndex]:
|
||||
# Find the committee in the list with the desired shard
|
||||
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
||||
|
||||
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
|
||||
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
||||
|
||||
return crosslink_committee
|
||||
```
|
||||
|
||||
### `get_attestation_participants`
|
||||
|
||||
```python
|
||||
@ -1043,13 +1058,9 @@ def get_attestation_participants(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: bytes) -> List[ValidatorIndex]:
|
||||
"""
|
||||
Return the participant indices corresponding to ``attestation_data`` and ``bitfield``.
|
||||
Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``.
|
||||
"""
|
||||
# Find the committee in the list with the desired shard
|
||||
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
||||
|
||||
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
|
||||
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
||||
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
|
||||
|
||||
assert verify_bitfield(bitfield, len(crosslink_committee))
|
||||
|
||||
@ -1059,7 +1070,7 @@ def get_attestation_participants(state: BeaconState,
|
||||
aggregation_bit = get_bitfield_bit(bitfield, i)
|
||||
if aggregation_bit == 0b1:
|
||||
participants.append(validator_index)
|
||||
return participants
|
||||
return sorted(participants)
|
||||
```
|
||||
|
||||
### `int_to_bytes1`, `int_to_bytes2`, ...
|
||||
@ -1147,33 +1158,47 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool:
|
||||
return True
|
||||
```
|
||||
|
||||
### `verify_slashable_attestation`
|
||||
### `convert_to_indexed`
|
||||
|
||||
```python
|
||||
def verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation) -> bool:
|
||||
def convert_to_indexed(state: BeaconState, attestation: Attestation):
|
||||
"""
|
||||
Verify validity of ``slashable_attestation`` fields.
|
||||
Convert an attestation to (almost) indexed-verifiable form
|
||||
"""
|
||||
if slashable_attestation.custody_bitfield != b'\x00' * len(slashable_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1]
|
||||
attesting_indices = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
|
||||
custody_bit_1_indices = get_attestation_participants(state, attestation.data, attestation.custody_bitfield)
|
||||
custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
|
||||
|
||||
return IndexedAttestation(
|
||||
custody_bit_0_indices=custody_bit_0_indices,
|
||||
custody_bit_1_indices=custody_bit_1_indices,
|
||||
data=attestation.data,
|
||||
aggregate_signature=attestation.aggregate_signature
|
||||
)
|
||||
```
|
||||
|
||||
### `verify_indexed_attestation`
|
||||
|
||||
```python
|
||||
def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
||||
"""
|
||||
Verify validity of ``indexed_attestation`` fields.
|
||||
"""
|
||||
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
|
||||
custody_bit_1_indices = indexed_attestation.custody_bit_1_indices
|
||||
|
||||
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
|
||||
return False
|
||||
|
||||
if not (1 <= len(slashable_attestation.validator_indices) <= MAX_SLASHABLE_ATTESTATION_PARTICIPANTS):
|
||||
total_attesting_indices = len(custody_bit_0_indices + custody_bit_1_indices)
|
||||
if not (1 <= total_attesting_indices <= MAX_ATTESTATION_PARTICIPANTS):
|
||||
return False
|
||||
|
||||
for i in range(len(slashable_attestation.validator_indices) - 1):
|
||||
if slashable_attestation.validator_indices[i] >= slashable_attestation.validator_indices[i + 1]:
|
||||
return False
|
||||
|
||||
if not verify_bitfield(slashable_attestation.custody_bitfield, len(slashable_attestation.validator_indices)):
|
||||
if custody_bit_0_indices != sorted(custody_bit_0_indices):
|
||||
return False
|
||||
|
||||
custody_bit_0_indices = []
|
||||
custody_bit_1_indices = []
|
||||
for i, validator_index in enumerate(slashable_attestation.validator_indices):
|
||||
if get_bitfield_bit(slashable_attestation.custody_bitfield, i) == 0b0:
|
||||
custody_bit_0_indices.append(validator_index)
|
||||
else:
|
||||
custody_bit_1_indices.append(validator_index)
|
||||
if custody_bit_1_indices != sorted(custody_bit_1_indices):
|
||||
return False
|
||||
|
||||
return bls_verify_multiple(
|
||||
pubkeys=[
|
||||
@ -1181,11 +1206,11 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas
|
||||
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]),
|
||||
],
|
||||
message_hashes=[
|
||||
hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b0)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
|
||||
],
|
||||
signature=slashable_attestation.aggregate_signature,
|
||||
domain=get_domain(state.fork, slot_to_epoch(slashable_attestation.data.slot), DOMAIN_ATTESTATION),
|
||||
signature=indexed_attestation.aggregate_signature,
|
||||
domain=get_domain(state.fork, slot_to_epoch(indexed_attestation.data.slot), DOMAIN_ATTESTATION),
|
||||
)
|
||||
```
|
||||
|
||||
@ -2251,16 +2276,17 @@ def process_attester_slashing(state: BeaconState,
|
||||
Process ``AttesterSlashing`` transaction.
|
||||
Note that this function mutates ``state``.
|
||||
"""
|
||||
attestation1 = attester_slashing.slashable_attestation_1
|
||||
attestation2 = attester_slashing.slashable_attestation_2
|
||||
attestation1 = attester_slashing.attestation_1
|
||||
attestation2 = attester_slashing.attestation_2
|
||||
# Check that the attestations are conflicting
|
||||
assert attestation1.data != attestation2.data
|
||||
assert (
|
||||
is_double_vote(attestation1.data, attestation2.data) or
|
||||
is_surround_vote(attestation1.data, attestation2.data)
|
||||
)
|
||||
assert verify_slashable_attestation(state, attestation1)
|
||||
assert verify_slashable_attestation(state, attestation2)
|
||||
|
||||
assert verify_indexed_attestation(state, attestation1)
|
||||
assert verify_indexed_attestation(state, attestation2)
|
||||
slashable_indices = [
|
||||
index for index in attestation1.validator_indices
|
||||
if (
|
||||
@ -2306,18 +2332,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
),
|
||||
}
|
||||
|
||||
# Check custody bits [to be generalised in phase 1]
|
||||
assert attestation.custody_bitfield == b'\x00' * len(attestation.custody_bitfield)
|
||||
|
||||
# Check aggregate signature [to be generalised in phase 1]
|
||||
participants = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
|
||||
assert len(participants) != 0
|
||||
assert bls_verify(
|
||||
pubkey=bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in participants]),
|
||||
message_hash=hash_tree_root(AttestationDataAndCustodyBit(data=attestation.data, custody_bit=0b0)),
|
||||
signature=attestation.aggregate_signature,
|
||||
domain=get_domain(state.fork, target_epoch, DOMAIN_ATTESTATION),
|
||||
)
|
||||
# Check signature and bitfields
|
||||
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))
|
||||
|
||||
# Cache pending attestation
|
||||
pending_attestation = PendingAttestation(
|
||||
|
@ -135,7 +135,7 @@ def test_non_empty_custody_bitfield(state):
|
||||
attestation = get_valid_attestation(state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.custody_bitfield = b'\x01' + attestation.custody_bitfield[1:]
|
||||
attestation.custody_bitfield = deepcopy(attestation.aggregation_bitfield)
|
||||
|
||||
pre_state, post_state = run_attestation_processing(state, attestation, False)
|
||||
|
||||
|
@ -22,12 +22,14 @@ from build.phase0.spec import (
|
||||
get_active_validator_indices,
|
||||
get_attestation_participants,
|
||||
get_block_root,
|
||||
get_crosslink_committee_for_attestation,
|
||||
get_crosslink_committees_at_slot,
|
||||
get_current_epoch,
|
||||
get_domain,
|
||||
get_empty_block,
|
||||
get_epoch_start_slot,
|
||||
get_genesis_beacon_state,
|
||||
slot_to_epoch,
|
||||
verify_merkle_branch,
|
||||
hash,
|
||||
)
|
||||
@ -248,12 +250,11 @@ def get_valid_attestation(state, slot=None):
|
||||
shard = state.latest_start_shard
|
||||
attestation_data = build_attestation_data(state, slot, shard)
|
||||
|
||||
crosslink_committees = get_crosslink_committees_at_slot(state, slot)
|
||||
crosslink_committee = [committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard][0]
|
||||
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
|
||||
|
||||
committee_size = len(crosslink_committee)
|
||||
bitfield_length = (committee_size + 7) // 8
|
||||
aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1)
|
||||
aggregation_bitfield = b'\xC0' + b'\x00' * (bitfield_length - 1)
|
||||
custody_bitfield = b'\x00' * bitfield_length
|
||||
attestation = Attestation(
|
||||
aggregation_bitfield=aggregation_bitfield,
|
||||
@ -266,23 +267,35 @@ def get_valid_attestation(state, slot=None):
|
||||
attestation.data,
|
||||
attestation.aggregation_bitfield,
|
||||
)
|
||||
assert len(participants) == 1
|
||||
assert len(participants) == 2
|
||||
|
||||
validator_index = participants[0]
|
||||
privkey = privkeys[validator_index]
|
||||
signatures = []
|
||||
for validator_index in participants:
|
||||
privkey = privkeys[validator_index]
|
||||
signatures.append(
|
||||
get_attestation_signature(
|
||||
state,
|
||||
attestation.data,
|
||||
privkey
|
||||
)
|
||||
)
|
||||
|
||||
attestation.aggregation_signature = bls.aggregate_signatures(signatures)
|
||||
return attestation
|
||||
|
||||
|
||||
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0):
|
||||
message_hash = AttestationDataAndCustodyBit(
|
||||
data=attestation.data,
|
||||
custody_bit=0b0,
|
||||
data=attestation_data,
|
||||
custody_bit=custody_bit,
|
||||
).hash_tree_root()
|
||||
|
||||
attestation.aggregation_signature = bls.sign(
|
||||
return bls.sign(
|
||||
message_hash=message_hash,
|
||||
privkey=privkey,
|
||||
domain=get_domain(
|
||||
fork=state.fork,
|
||||
epoch=get_current_epoch(state),
|
||||
epoch=slot_to_epoch(attestation_data.slot),
|
||||
domain_type=spec.DOMAIN_ATTESTATION,
|
||||
)
|
||||
)
|
||||
return attestation
|
||||
|
Loading…
x
Reference in New Issue
Block a user