From 67c50cb197824a015ff9946fb6fd5c684e9e9639 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Thu, 27 Jun 2019 11:45:35 +0100 Subject: [PATCH] Changed attestation and custody bitfields --- scripts/build_spec.py | 6 +-- specs/core/0_beacon-chain.md | 41 +++---------------- .../pyspec/eth2spec/debug/random_value.py | 6 +-- test_libs/pyspec/eth2spec/fuzzing/decoder.py | 11 +++++ .../eth2spec/test/helpers/attestations.py | 9 ++-- .../pyspec/eth2spec/test/helpers/bitfields.py | 11 ----- .../test_process_attestation.py | 8 +++- .../pyspec/eth2spec/utils/ssz/ssz_typing.py | 8 +++- 8 files changed, 38 insertions(+), 62 deletions(-) delete mode 100644 test_libs/pyspec/eth2spec/test/helpers/bitfields.py diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 0b1512111..0a41fe5c0 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -26,7 +26,7 @@ from eth2spec.utils.ssz.ssz_impl import ( ) from eth2spec.utils.ssz.ssz_typing import ( bit, boolean, Container, List, Vector, Bytes, uint64, - Bytes4, Bytes32, Bytes48, Bytes96, + Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils.bls import ( bls_aggregate_pubkeys, @@ -53,7 +53,7 @@ from eth2spec.utils.ssz.ssz_impl import ( ) from eth2spec.utils.ssz.ssz_typing import ( bit, boolean, Container, List, Vector, Bytes, uint64, - Bytes4, Bytes32, Bytes48, Bytes96, + Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils.bls import ( bls_aggregate_pubkeys, @@ -175,7 +175,7 @@ def combine_constants(old_constants: Dict[str, str], new_constants: Dict[str, st ignored_dependencies = [ 'bit', 'boolean', 'Vector', 'List', 'Container', 'Hash', 'BLSPubkey', 'BLSSignature', 'Bytes', 'BytesN' - 'Bytes4', 'Bytes32', 'Bytes48', 'Bytes96', + 'Bytes4', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector', 'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256', 'bytes' # to be removed after updating spec doc ] diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7b9aeee4b..13ab4cdae 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -80,8 +80,6 @@ - [`bytes_to_int`](#bytes_to_int) - [`get_total_balance`](#get_total_balance) - [`get_domain`](#get_domain) - - [`get_bitfield_bit`](#get_bitfield_bit) - - [`verify_bitfield`](#verify_bitfield) - [`convert_to_indexed`](#convert_to_indexed) - [`validate_indexed_attestation`](#validate_indexed_attestation) - [`is_slashable_attestation_data`](#is_slashable_attestation_data) @@ -354,7 +352,7 @@ class IndexedAttestation(Container): ```python class PendingAttestation(Container): - aggregation_bitfield: Bytes[MAX_INDICES_PER_ATTESTATION // 8] + aggregation_bitfield: Bitlist[MAX_INDICES_PER_ATTESTATION] data: AttestationData inclusion_delay: Slot proposer_index: ValidatorIndex @@ -421,9 +419,9 @@ class AttesterSlashing(Container): ```python class Attestation(Container): - aggregation_bitfield: Bytes[MAX_INDICES_PER_ATTESTATION // 8] + aggregation_bitfield: Bitlist[MAX_INDICES_PER_ATTESTATION] data: AttestationData - custody_bitfield: Bytes[MAX_INDICES_PER_ATTESTATION // 8] + custody_bitfield: Bitlist[MAX_INDICES_PER_ATTESTATION] signature: BLSSignature ``` @@ -865,13 +863,12 @@ def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> S ```python def get_attesting_indices(state: BeaconState, attestation_data: AttestationData, - bitfield: bytes) -> Sequence[ValidatorIndex]: + bitfield: Bitlist[MAX_INDICES_PER_ATTESTATION]) -> Sequence[ValidatorIndex]: """ Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``. """ 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]) + return sorted([index for i, index in enumerate(committee) if bitfield[i]]) ``` ### `int_to_bytes` @@ -912,34 +909,6 @@ def get_domain(state: BeaconState, return bls_domain(domain_type, fork_version) ``` -### `get_bitfield_bit` - -```python -def get_bitfield_bit(bitfield: bytes, i: int) -> int: - """ - Extract the bit in ``bitfield`` at position ``i``. - """ - return (bitfield[i // 8] >> (i % 8)) % 2 -``` - -### `verify_bitfield` - -```python -def verify_bitfield(bitfield: bytes, committee_size: int) -> bool: - """ - Verify ``bitfield`` against the ``committee_size``. - """ - if len(bitfield) != (committee_size + 7) // 8: - return False - - # Check `bitfield` is padded with zero bits only - for i in range(committee_size, len(bitfield) * 8): - if get_bitfield_bit(bitfield, i) == 0b1: - return False - - return True -``` - ### `convert_to_indexed` ```python diff --git a/test_libs/pyspec/eth2spec/debug/random_value.py b/test_libs/pyspec/eth2spec/debug/random_value.py index cdcba343a..95a3ae970 100644 --- a/test_libs/pyspec/eth2spec/debug/random_value.py +++ b/test_libs/pyspec/eth2spec/debug/random_value.py @@ -3,7 +3,7 @@ from enum import Enum from eth2spec.utils.ssz.ssz_typing import ( SSZType, SSZValue, BasicValue, BasicType, uint, Container, Bytes, List, boolean, - Vector, BytesN + Vector, BytesN, Bitlist, Bitvector ) # in bytes @@ -83,12 +83,12 @@ def get_random_ssz_object(rng: Random, return get_max_basic_value(typ) else: return get_random_basic_value(rng, typ) - elif issubclass(typ, Vector): + elif issubclass(typ, Vector) or issubclass(typ, Bitvector): return typ( get_random_ssz_object(rng, typ.elem_type, max_bytes_length, max_list_length, mode, chaos) for _ in range(typ.length) ) - elif issubclass(typ, List): + elif issubclass(typ, List) or issubclass(typ, Bitlist): length = rng.randint(0, min(typ.length, max_list_length)) if mode == RandomizationMode.mode_one_count: length = 1 diff --git a/test_libs/pyspec/eth2spec/fuzzing/decoder.py b/test_libs/pyspec/eth2spec/fuzzing/decoder.py index 130956235..ccca17385 100644 --- a/test_libs/pyspec/eth2spec/fuzzing/decoder.py +++ b/test_libs/pyspec/eth2spec/fuzzing/decoder.py @@ -18,7 +18,14 @@ def translate_typ(typ) -> ssz.BaseSedes: elif issubclass(typ, spec_ssz.Vector): return ssz.Vector(translate_typ(typ.elem_type), typ.length) elif issubclass(typ, spec_ssz.List): + # TODO: Make py-ssz List support the new fixed length list return ssz.List(translate_typ(typ.elem_type)) + elif issubclass(typ, spec_ssz.Bitlist): + # TODO: Once Bitlist implemented in py-ssz, use appropriate type + return ssz.List(translate_typ(typ.elem_type)) + elif issubclass(typ, spec_ssz.Bitvector): + # TODO: Once Bitvector implemented in py-ssz, use appropriate type + return ssz.Vector(translate_typ(typ.elem_type), typ.length) elif issubclass(typ, spec_ssz.boolean): return ssz.boolean elif issubclass(typ, spec_ssz.uint): @@ -68,6 +75,10 @@ def translate_value(value, typ): return value elif issubclass(typ, spec_ssz.Vector): return typ(*(translate_value(elem, typ.elem_type) for elem in value)) + elif issubclass(typ, spec_ssz.Bitlist): + return typ(value) + elif issubclass(typ, spec_ssz.Bitvector): + return typ(value) elif issubclass(typ, spec_ssz.BytesN): return typ(value) elif issubclass(typ, spec_ssz.Bytes): diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index 4c8b5c7eb..2e637d42f 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -1,10 +1,10 @@ from typing import List -from eth2spec.test.helpers.bitfields import set_bitfield_bit from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block from eth2spec.test.helpers.keys import privkeys from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_typing import Bitlist def build_attestation_data(spec, state, slot, shard): @@ -69,9 +69,8 @@ def get_valid_attestation(spec, state, slot=None, signed=False): ) committee_size = len(crosslink_committee) - bitfield_length = (committee_size + 7) // 8 - aggregation_bitfield = b'\x00' * bitfield_length - custody_bitfield = b'\x00' * bitfield_length + aggregation_bitfield = Bitlist[spec.MAX_INDICES_PER_ATTESTATION](*([0] * committee_size)) + custody_bitfield = Bitlist[spec.MAX_INDICES_PER_ATTESTATION](*([0] * committee_size)) attestation = spec.Attestation( aggregation_bitfield=aggregation_bitfield, data=attestation_data, @@ -138,7 +137,7 @@ def fill_aggregate_attestation(spec, state, attestation): attestation.data.crosslink.shard, ) for i in range(len(crosslink_committee)): - attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i) + attestation.aggregation_bitfield[i] = True def add_attestation_to_state(spec, state, attestation, slot): diff --git a/test_libs/pyspec/eth2spec/test/helpers/bitfields.py b/test_libs/pyspec/eth2spec/test/helpers/bitfields.py deleted file mode 100644 index 50e5b6cba..000000000 --- a/test_libs/pyspec/eth2spec/test/helpers/bitfields.py +++ /dev/null @@ -1,11 +0,0 @@ -def set_bitfield_bit(bitfield, i): - """ - 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:] - ) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 2b34ab405..ee76ab23d 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -10,6 +10,7 @@ from eth2spec.test.helpers.state import ( next_slot, ) from eth2spec.test.helpers.block import apply_empty_block +from eth2spec.utils.ssz.ssz_typing import Bitlist def run_attestation_processing(spec, state, attestation, valid=True): @@ -281,7 +282,10 @@ def test_inconsistent_bitfields(spec, state): attestation = get_valid_attestation(spec, state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.custody_bitfield = deepcopy(attestation.aggregation_bitfield) + b'\x00' + custody_bitfield = deepcopy(attestation.aggregation_bitfield) + custody_bitfield.append(False) + + attestation.custody_bitfield = custody_bitfield sign_attestation(spec, state, attestation) @@ -307,7 +311,7 @@ def test_empty_aggregation_bitfield(spec, state): attestation = get_valid_attestation(spec, state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield) + attestation.aggregation_bitfield = Bitlist[spec.MAX_INDICES_PER_ATTESTATION](*([0] * len(attestation.aggregation_bitfield))) sign_attestation(spec, state, attestation) diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py index 047abd4fe..d0aef1b44 100644 --- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -351,11 +351,15 @@ class BaseBitfield(BaseList, metaclass=BitElementsType): class Bitlist(BaseBitfield): - pass + @classmethod + def is_fixed_size(cls): + return False class Bitvector(BaseBitfield): - pass + @classmethod + def is_fixed_size(cls): + return True class List(BaseList):