From 3ee9fc0cc775a05042f7acbfc46e03ec24d14104 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 22 Mar 2019 06:10:44 -0500 Subject: [PATCH 1/9] Merge attestation verification logic Also rename slashable attestation to standalone attestation to reflect its broader functionality in phase 1. --- specs/core/0_beacon-chain.md | 84 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c29aa113d..a4d5f5ec6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -28,7 +28,7 @@ - [`Eth1DataVote`](#eth1datavote) - [`AttestationData`](#attestationdata) - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - - [`SlashableAttestation`](#slashableattestation) + - [`StandaloneAttestation`](#standaloneattestation) - [`DepositInput`](#depositinput) - [`DepositData`](#depositdata) - [`BeaconBlockHeader`](#beaconblockheader) @@ -90,7 +90,8 @@ - [`get_domain`](#get_domain) - [`get_bitfield_bit`](#get_bitfield_bit) - [`verify_bitfield`](#verify_bitfield) - - [`verify_slashable_attestation`](#verify_slashable_attestation) + - [`convert_to_standalone`](#convert_to_standalone) + - [`verify_standalone_attestation`](#verify_standalone_attestation) - [`is_double_vote`](#is_double_vote) - [`is_surround_vote`](#is_surround_vote) - [`integer_squareroot`](#integer_squareroot) @@ -187,7 +188,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,7 +370,7 @@ The types are defined topologically to aid in facilitating an executable version } ``` -#### `SlashableAttestation` +#### `StandaloneAttestation` ```python { @@ -489,10 +490,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': StandaloneAttestation, + # Second attestation + 'attestation_2': StandaloneAttestation, } ``` @@ -1116,7 +1117,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) ``` ### `is_power_of_two` @@ -1214,30 +1215,45 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: return True ``` -### `verify_slashable_attestation` +### `convert_to_standalone` ```python -def verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation) -> bool: +def convert_to_standalone(state: BeaconState, attestation: Attestation): """ - Verify validity of ``slashable_attestation`` fields. + Converts an attestation to (almost) standalone-verifiable form """ - if slashable_attestation.custody_bitfield != b'\x00' * len(slashable_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1] + return StandaloneAttestation( + validator_indices=get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield), + data=attestation.data, + custody_bitfield=attestation.custody_bitfield, + aggregate_signature=attestation.aggregate_signature + ) +``` + +### `verify_standalone_attestation` + +```python +def verify_standalone_attestation(state: BeaconState, standalone_attestation: StandaloneAttestation) -> bool: + """ + Verify validity of ``standalone_attestation`` fields. + """ + if standalone_attestation.custody_bitfield != b'\x00' * len(standalone_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1] return False - if not (1 <= len(slashable_attestation.validator_indices) <= MAX_SLASHABLE_ATTESTATION_PARTICIPANTS): + if not (1 <= len(standalone_attestation.validator_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]: + for i in range(len(standalone_attestation.validator_indices) - 1): + if standalone_attestation.validator_indices[i] >= standalone_attestation.validator_indices[i + 1]: return False - if not verify_bitfield(slashable_attestation.custody_bitfield, len(slashable_attestation.validator_indices)): + if not verify_bitfield(standalone_attestation.custody_bitfield, len(standalone_attestation.validator_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: + for i, validator_index in enumerate(standalone_attestation.validator_indices): + if get_bitfield_bit(standalone_attestation.custody_bitfield, i) == 0b0: custody_bit_0_indices.append(validator_index) else: custody_bit_1_indices.append(validator_index) @@ -1248,11 +1264,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=standalone_attestation.data, custody_bit=0b0)), + hash_tree_root(AttestationDataAndCustodyBit(data=standalone_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=standalone_attestation.aggregate_signature, + domain=get_domain(state.fork, slot_to_epoch(standalone_attestation.data.slot), DOMAIN_ATTESTATION), ) ``` @@ -2408,16 +2424,16 @@ 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_standalone_attestation(state, attestation1) + assert verify_standalone_attestation(state, attestation2) slashable_indices = [ index for index in attestation1.validator_indices if ( @@ -2462,18 +2478,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_standalone_attestation(state, convert_to_standalone(state, attestation)) # Cache pending attestation pending_attestation = PendingAttestation( From ce18bde5c9cb81a85105bbd6f93980f29dbe714b Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 22 Mar 2019 06:20:38 -0500 Subject: [PATCH 2/9] Simplified sorted index check --- specs/core/0_beacon-chain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a4d5f5ec6..94784e625 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1243,10 +1243,9 @@ def verify_standalone_attestation(state: BeaconState, standalone_attestation: St if not (1 <= len(standalone_attestation.validator_indices) <= MAX_ATTESTATION_PARTICIPANTS): return False - for i in range(len(standalone_attestation.validator_indices) - 1): - if standalone_attestation.validator_indices[i] >= standalone_attestation.validator_indices[i + 1]: - return False - + if standalone_attestation.validator_indices != sorted(standalone_attestation.validator_indices): + return False + if not verify_bitfield(standalone_attestation.custody_bitfield, len(standalone_attestation.validator_indices)): return False From 80e2553afd675f508a42b42a44a224b97fe2b6f1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 22 Mar 2019 09:32:21 -0400 Subject: [PATCH 3/9] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 94784e625..3ae2c7e13 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1220,7 +1220,7 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: ```python def convert_to_standalone(state: BeaconState, attestation: Attestation): """ - Converts an attestation to (almost) standalone-verifiable form + Convert an attestation to (almost) standalone-verifiable form """ return StandaloneAttestation( validator_indices=get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield), From 5b40baa69eaac7151a6c90b9ce292cef827339b5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 23 Mar 2019 11:58:20 +0800 Subject: [PATCH 4/9] Adjust the sanity test for attestation verification integration --- tests/phase0/test_sanity.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 444075a13..f7670c126 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -227,22 +227,18 @@ def test_attestation(state, pubkeys, privkeys): crosslink_committees = get_crosslink_committees_at_slot(state, slot) crosslink_committee = [committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard][0] - committee_size = len(crosslink_committee) - bitfield_length = (committee_size + 7) // 8 - aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1) - custody_bitfield = b'\x00' * bitfield_length + # Select the first validator to be the attester + participants = [crosslink_committee[0]] + aggregation_bitfield_length = (len(crosslink_committee) + 7) // 8 + custody_bitfield_length = (len(participants) + 7) // 8 + aggregation_bitfield = b'\x01' + b'\x00' * (aggregation_bitfield_length - 1) + custody_bitfield = b'\x00' * custody_bitfield_length attestation = Attestation( aggregation_bitfield=aggregation_bitfield, data=attestation_data, custody_bitfield=custody_bitfield, aggregate_signature=EMPTY_SIGNATURE, ) - participants = get_attestation_participants( - test_state, - attestation.data, - attestation.aggregation_bitfield, - ) - assert len(participants) == 1 validator_index = participants[0] privkey = privkeys[validator_index] From 63e7346cfbc4b000c28b981710f43b9ec48a284a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 26 Mar 2019 13:40:19 -0600 Subject: [PATCH 5/9] standaline -> indexed --- specs/core/0_beacon-chain.md | 52 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2acf7ddbe..2e2c3ad59 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -28,7 +28,7 @@ - [`Eth1DataVote`](#eth1datavote) - [`AttestationData`](#attestationdata) - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - - [`StandaloneAttestation`](#standaloneattestation) + - [`IndexedAttestation`](#indexedattestation) - [`DepositData`](#depositdata) - [`BeaconBlockHeader`](#beaconblockheader) - [`Validator`](#validator) @@ -86,8 +86,8 @@ - [`get_domain`](#get_domain) - [`get_bitfield_bit`](#get_bitfield_bit) - [`verify_bitfield`](#verify_bitfield) - - [`convert_to_standalone`](#convert_to_standalone) - - [`verify_standalone_attestation`](#verify_standalone_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) @@ -370,7 +370,7 @@ The types are defined topologically to aid in facilitating an executable version } ``` -#### `StandaloneAttestation` +#### `IndexedAttestation` ```python { @@ -480,9 +480,9 @@ The types are defined topologically to aid in facilitating an executable version ```python { # First attestation - 'attestation_1': StandaloneAttestation, + 'attestation_1': IndexedAttestation, # Second attestation - 'attestation_2': StandaloneAttestation, + 'attestation_2': IndexedAttestation, } ``` @@ -1148,14 +1148,14 @@ def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: return True ``` -### `convert_to_standalone` +### `convert_to_indexed` ```python -def convert_to_standalone(state: BeaconState, attestation: Attestation): +def convert_to_indexed(state: BeaconState, attestation: Attestation): """ - Convert an attestation to (almost) standalone-verifiable form + Convert an attestation to (almost) indexed-verifiable form """ - return StandaloneAttestation( + return IndexedAttestation( validator_indices=get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield), data=attestation.data, custody_bitfield=attestation.custody_bitfield, @@ -1163,29 +1163,29 @@ def convert_to_standalone(state: BeaconState, attestation: Attestation): ) ``` -### `verify_standalone_attestation` +### `verify_indexed_attestation` ```python -def verify_standalone_attestation(state: BeaconState, standalone_attestation: StandaloneAttestation) -> bool: +def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool: """ - Verify validity of ``standalone_attestation`` fields. + Verify validity of ``indexed_attestation`` fields. """ - if standalone_attestation.custody_bitfield != b'\x00' * len(standalone_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1] + if indexed_attestation.custody_bitfield != b'\x00' * len(indexed_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1] return False - if not (1 <= len(standalone_attestation.validator_indices) <= MAX_ATTESTATION_PARTICIPANTS): + if not (1 <= len(indexed_attestation.validator_indices) <= MAX_ATTESTATION_PARTICIPANTS): return False - if standalone_attestation.validator_indices != sorted(standalone_attestation.validator_indices): + if indexed_attestation.validator_indices != sorted(indexed_attestation.validator_indices): return False - if not verify_bitfield(standalone_attestation.custody_bitfield, len(standalone_attestation.validator_indices)): + if not verify_bitfield(indexed_attestation.custody_bitfield, len(indexed_attestation.validator_indices)): return False custody_bit_0_indices = [] custody_bit_1_indices = [] - for i, validator_index in enumerate(standalone_attestation.validator_indices): - if get_bitfield_bit(standalone_attestation.custody_bitfield, i) == 0b0: + for i, validator_index in enumerate(indexed_attestation.validator_indices): + if get_bitfield_bit(indexed_attestation.custody_bitfield, i) == 0b0: custody_bit_0_indices.append(validator_index) else: custody_bit_1_indices.append(validator_index) @@ -1196,11 +1196,11 @@ def verify_standalone_attestation(state: BeaconState, standalone_attestation: St bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]), ], message_hashes=[ - hash_tree_root(AttestationDataAndCustodyBit(data=standalone_attestation.data, custody_bit=0b0)), - hash_tree_root(AttestationDataAndCustodyBit(data=standalone_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=standalone_attestation.aggregate_signature, - domain=get_domain(state.fork, slot_to_epoch(standalone_attestation.data.slot), DOMAIN_ATTESTATION), + signature=indexed_attestation.aggregate_signature, + domain=get_domain(state.fork, slot_to_epoch(indexed_attestation.data.slot), DOMAIN_ATTESTATION), ) ``` @@ -2318,8 +2318,8 @@ def process_attester_slashing(state: BeaconState, is_double_vote(attestation1.data, attestation2.data) or is_surround_vote(attestation1.data, attestation2.data) ) - assert verify_standalone_attestation(state, attestation1) - assert verify_standalone_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 ( @@ -2366,7 +2366,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: } # Check signature and bitfields - assert verify_standalone_attestation(state, convert_to_standalone(state, attestation)) + assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) # Cache pending attestation pending_attestation = PendingAttestation( From fbb09795ed3dca6e98eb9ef97c572f4e590293cf Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 27 Mar 2019 08:31:56 -0600 Subject: [PATCH 6/9] fix convert_to_indexed custody bitfield bug --- specs/core/0_beacon-chain.md | 63 +++++++++++++++---- .../test_process_attestation.py | 2 +- tests/phase0/helpers.py | 36 +++++++---- 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2e2c3ad59..0bdfafb79 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -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) @@ -85,6 +86,7 @@ - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - [`get_bitfield_bit`](#get_bitfield_bit) + - [`set_bitfield_bit`](#set_bitfield_bit) - [`verify_bitfield`](#verify_bitfield) - [`convert_to_indexed`](#convert_to_indexed) - [`verify_indexed_attestation`](#verify_indexed_attestation) @@ -1037,6 +1039,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 @@ -1046,11 +1062,7 @@ def get_attestation_participants(state: BeaconState, """ Return the 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)) @@ -1060,7 +1072,7 @@ def get_attestation_participants(state: BeaconState, aggregation_bit = get_bitfield_bit(bitfield, i) if aggregation_bit == 0b1: participants.append(validator_index) - return sorted(participants) + return participants ``` ### `int_to_bytes1`, `int_to_bytes2`, ... @@ -1130,6 +1142,22 @@ def get_bitfield_bit(bitfield: bytes, i: int) -> int: return (bitfield[i // 8] >> (i % 8)) % 2 ``` +### `set_bitfield_bit` + +```python +def set_bitfield_bit(bitfield: bytes, i: int) -> int: + """ + Set the bit in ``bitfield`` at position ``i`` to ``1``. + """ + byte_index = i // 8 + bit_index = i % 8 + return ( + bitfield[:byte_index] + + bytes([bitfield[byte_index] | (1 << bit_index)]) + + bitfield[byte_index+1:] + ) +``` + ### `verify_bitfield` ```python @@ -1155,10 +1183,21 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation): """ Convert an attestation to (almost) indexed-verifiable form """ + attesting_indices = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield) + + # reconstruct custody bitfield for the truncated attesting_indices + custody_bit_1_indices = get_attestation_participants(state, attestation.data, attestation.custody_bitfield) + custody_bitfield = b'\x00' * ((len(attesting_indices) + 7) // 8) + + crosslink_committee = get_crosslink_committee_for_attestation(state, attestation.data) + for i, validator_index in enumerate(crosslink_committee): + if get_bitfield_bit(attestation.custody_bitfield, i): + custody_bitfield = set_bitfield_bit(custody_bitfield, attesting_indices.index(validator_index)) + return IndexedAttestation( - validator_indices=get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield), + validator_indices=attesting_indices, data=attestation.data, - custody_bitfield=attestation.custody_bitfield, + custody_bitfield=custody_bitfield, aggregate_signature=attestation.aggregate_signature ) ``` @@ -1176,9 +1215,6 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA if not (1 <= len(indexed_attestation.validator_indices) <= MAX_ATTESTATION_PARTICIPANTS): return False - if indexed_attestation.validator_indices != sorted(indexed_attestation.validator_indices): - return False - if not verify_bitfield(indexed_attestation.custody_bitfield, len(indexed_attestation.validator_indices)): return False @@ -2318,6 +2354,11 @@ def process_attester_slashing(state: BeaconState, is_double_vote(attestation1.data, attestation2.data) or is_surround_vote(attestation1.data, attestation2.data) ) + + # check that indices are sorted + assert attestation1.validator_indices == sorted(attestation1.validator_indices) + assert attestation2.validator_indices == sorted(attestation2.validator_indices) + assert verify_indexed_attestation(state, attestation1) assert verify_indexed_attestation(state, attestation2) slashable_indices = [ diff --git a/tests/phase0/block_processing/test_process_attestation.py b/tests/phase0/block_processing/test_process_attestation.py index 08cab11ff..ca6933ce7 100644 --- a/tests/phase0/block_processing/test_process_attestation.py +++ b/tests/phase0/block_processing/test_process_attestation.py @@ -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) diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index d7f4ae6e8..08ea6ca04 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -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,36 @@ 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 From 1f657cfec50b1c41e53a9183193047fc420d3d8d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 28 Mar 2019 11:26:04 -0600 Subject: [PATCH 7/9] remove custody_bitfield from indexedattestation. add two separate arrays for 0 and 1 bit --- specs/core/0_beacon-chain.md | 45 +++++++++++++----------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0bdfafb79..057772293 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -377,11 +377,10 @@ The types are defined topologically to aid in facilitating an executable version ```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', } @@ -1060,7 +1059,7 @@ 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``. """ crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data) @@ -1072,7 +1071,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`, ... @@ -1184,20 +1183,13 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation): Convert an attestation to (almost) indexed-verifiable form """ attesting_indices = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield) - - # reconstruct custody bitfield for the truncated attesting_indices custody_bit_1_indices = get_attestation_participants(state, attestation.data, attestation.custody_bitfield) - custody_bitfield = b'\x00' * ((len(attesting_indices) + 7) // 8) - - crosslink_committee = get_crosslink_committee_for_attestation(state, attestation.data) - for i, validator_index in enumerate(crosslink_committee): - if get_bitfield_bit(attestation.custody_bitfield, i): - custody_bitfield = set_bitfield_bit(custody_bitfield, attesting_indices.index(validator_index)) + custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices] return IndexedAttestation( - validator_indices=attesting_indices, + custody_bit_0_indices=custody_bit_0_indices, + custody_bit_1_indices=custody_bit_1_indices, data=attestation.data, - custody_bitfield=custody_bitfield, aggregate_signature=attestation.aggregate_signature ) ``` @@ -1209,22 +1201,21 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA """ Verify validity of ``indexed_attestation`` fields. """ - if indexed_attestation.custody_bitfield != b'\x00' * len(indexed_attestation.custody_bitfield): # [TO BE REMOVED IN PHASE 1] + 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(indexed_attestation.validator_indices) <= MAX_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 - if not verify_bitfield(indexed_attestation.custody_bitfield, len(indexed_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(indexed_attestation.validator_indices): - if get_bitfield_bit(indexed_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=[ @@ -2355,10 +2346,6 @@ def process_attester_slashing(state: BeaconState, is_surround_vote(attestation1.data, attestation2.data) ) - # check that indices are sorted - assert attestation1.validator_indices == sorted(attestation1.validator_indices) - assert attestation2.validator_indices == sorted(attestation2.validator_indices) - assert verify_indexed_attestation(state, attestation1) assert verify_indexed_attestation(state, attestation2) slashable_indices = [ From ba47a8f4c44adebf613f5507ca48d022141a389c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 28 Mar 2019 11:28:38 -0600 Subject: [PATCH 8/9] remove unused set_bitfield_bit hlper --- specs/core/0_beacon-chain.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 057772293..8363d9b22 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -86,7 +86,6 @@ - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - [`get_bitfield_bit`](#get_bitfield_bit) - - [`set_bitfield_bit`](#set_bitfield_bit) - [`verify_bitfield`](#verify_bitfield) - [`convert_to_indexed`](#convert_to_indexed) - [`verify_indexed_attestation`](#verify_indexed_attestation) @@ -1141,22 +1140,6 @@ def get_bitfield_bit(bitfield: bytes, i: int) -> int: return (bitfield[i // 8] >> (i % 8)) % 2 ``` -### `set_bitfield_bit` - -```python -def set_bitfield_bit(bitfield: bytes, i: int) -> int: - """ - Set the bit in ``bitfield`` at position ``i`` to ``1``. - """ - byte_index = i // 8 - bit_index = i % 8 - return ( - bitfield[:byte_index] + - bytes([bitfield[byte_index] | (1 << bit_index)]) + - bitfield[byte_index+1:] - ) -``` - ### `verify_bitfield` ```python From eb229089c842cac0445ab5393fe04b28c552b0ce Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 28 Mar 2019 11:31:12 -0600 Subject: [PATCH 9/9] lint --- tests/phase0/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 08ea6ca04..e5e335d80 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -280,7 +280,6 @@ def get_valid_attestation(state, slot=None): ) ) - attestation.aggregation_signature = bls.aggregate_signatures(signatures) return attestation