From a061758a664f74f992d5b7ec32913e8ef8bb768e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 25 Jun 2020 17:52:05 +0800 Subject: [PATCH 1/3] Use `encode_bytes` to implement `int_to_bytes` Rename `bytes_to_int` to `bytes_to_uint64` Use `encode_bytes` to implement `int_to_bytes` Rename `int_to_bytes` to `uint_to_bytes` and move it to `ssz_impl.py` --- setup.py | 8 ++-- specs/phase0/beacon-chain.md | 40 +++++++++---------- specs/phase0/validator.md | 2 +- specs/phase1/beacon-chain.md | 4 +- specs/phase1/validator.md | 2 +- .../pyspec/eth2spec/utils/ssz/ssz_impl.py | 5 +++ 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index cc3b00da9..86985b96d 100644 --- a/setup.py +++ b/setup.py @@ -94,9 +94,9 @@ from dataclasses import ( from lru import LRU -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_impl import hash_tree_root, uint_to_bytes from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint64, + View, boolean, Container, List, Vector, uint8, uint32, uint64, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils import bls @@ -118,9 +118,9 @@ from dataclasses import ( from lru import LRU -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_impl import hash_tree_root, uint_to_bytes from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint64, uint8, bit, + View, boolean, Container, List, Vector, uint8, uint32, uint64, bit, ByteList, ByteVector, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils import bls diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index cd52762d5..df3661810 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -55,8 +55,8 @@ - [Math](#math) - [`integer_squareroot`](#integer_squareroot) - [`xor`](#xor) - - [`int_to_bytes`](#int_to_bytes) - - [`bytes_to_int`](#bytes_to_int) + - [`uint_to_bytes`](#uint_to_bytes) + - [`bytes_to_uint64`](#bytes_to_uint64) - [Crypto](#crypto) - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) @@ -576,20 +576,14 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: return Bytes32(a ^ b for a, b in zip(bytes_1, bytes_2)) ``` -#### `int_to_bytes` +#### `uint_to_bytes` + +`def uint_to_bytes(n: uint8) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type. + +#### `bytes_to_uint64` ```python -def int_to_bytes(n: uint64, length: uint64) -> bytes: - """ - Return the ``length``-byte serialization of ``n`` in ``ENDIANNESS``-endian. - """ - return n.to_bytes(length, ENDIANNESS) -``` - -#### `bytes_to_int` - -```python -def bytes_to_int(data: bytes) -> uint64: +def bytes_to_uint64(data: bytes) -> uint64: """ Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. """ @@ -732,11 +726,15 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in range(SHUFFLE_ROUND_COUNT): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count - flip = (pivot + index_count - index) % index_count + for current_round in map(uint8, range(SHUFFLE_ROUND_COUNT)): + pivot = bytes_to_uint64(hash(seed + uint_to_bytes(current_round))[0:8]) % index_count + flip = uint64((pivot + index_count - index) % index_count) position = max(index, flip) - source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) + source = hash( + seed + + uint_to_bytes(current_round) + + uint_to_bytes(uint32(position // 256)) + ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -756,7 +754,7 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] i = 0 while True: candidate_index = indices[compute_shuffled_index(i % len(indices), len(indices), seed)] - random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] + random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -945,7 +943,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes Return the seed at ``epoch``. """ mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow - return hash(domain_type + int_to_bytes(epoch, length=8) + mix) + return hash(domain_type + uint_to_bytes(epoch) + mix) ``` #### `get_committee_count_per_slot` @@ -986,7 +984,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8)) + seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot)) indices = get_active_validator_indices(state, epoch) return compute_proposer_index(state, indices, seed) ``` diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index e325f75f6..6a895e1fa 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -465,7 +465,7 @@ def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSigna def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool: committee = get_beacon_committee(state, slot, index) modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE) - return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0 + return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0 ``` #### Construct aggregate diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index e103d51f4..43cdcff78 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -581,8 +581,8 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard """ epoch = compute_epoch_at_slot(slot) committee = get_shard_committee(beacon_state, epoch, shard) - seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_COMMITTEE) + int_to_bytes(slot, length=8)) - r = bytes_to_int(seed[:8]) + seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_COMMITTEE) + uint_to_bytes(slot)) + r = bytes_to_uint64(seed[:8]) return committee[r % len(committee)] ``` diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index 8b7ade60b..26e769dff 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -463,7 +463,7 @@ def get_light_client_slot_signature(state: BeaconState, slot: Slot, privkey: int def is_light_client_aggregator(state: BeaconState, slot: Slot, slot_signature: BLSSignature) -> bool: committee = get_light_client_committee(state, compute_epoch_at_slot(slot)) modulo = max(1, len(committee) // TARGET_LIGHT_CLIENT_AGGREGATORS_PER_SLOT) - return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0 + return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0 ``` #### Construct aggregate diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py index 34e6c4ee8..a47c77c33 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py @@ -1,3 +1,4 @@ +from remerkleable.basic import uint from remerkleable.core import View from remerkleable.byte_arrays import Bytes32 @@ -8,3 +9,7 @@ def serialize(obj: View) -> bytes: def hash_tree_root(obj: View) -> Bytes32: return Bytes32(obj.get_backing().merkle_root()) + + +def uint_to_bytes(n: uint) -> bytes: + return serialize(n) From 1d954ee9bd0c2a925b02666ec4dbde237e2857c7 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 29 Jun 2020 12:33:52 +0800 Subject: [PATCH 2/3] PR feedback from @ericsson49 Co-authored-by: Alex Vlasov --- specs/phase0/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index df3661810..ba701c2b1 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -578,7 +578,7 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: #### `uint_to_bytes` -`def uint_to_bytes(n: uint8) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type. +`def uint_to_bytes(n: uint) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type. #### `bytes_to_uint64` From 4b239e94b742a89df26fff5a74feac8815f7463c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 29 Jun 2020 12:50:01 +0800 Subject: [PATCH 3/3] Mix PR feedback from Danny and Proto --- specs/phase0/beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index ba701c2b1..1994b8691 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -726,13 +726,13 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in map(uint8, range(SHUFFLE_ROUND_COUNT)): - pivot = bytes_to_uint64(hash(seed + uint_to_bytes(current_round))[0:8]) % index_count - flip = uint64((pivot + index_count - index) % index_count) + for current_round in range(SHUFFLE_ROUND_COUNT): + pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count + flip = (pivot + index_count - index) % index_count position = max(index, flip) source = hash( seed - + uint_to_bytes(current_round) + + uint_to_bytes(uint8(current_round)) + uint_to_bytes(uint32(position // 256)) ) byte = source[(position % 256) // 8]