diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index ab8aab3c4..15b749b9d 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -74,6 +74,10 @@ MAX_EPOCHS_PER_CROSSLINK: 4 MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4 # [customized] 2**12 (= 4,096) epochs EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096 +# 2**2 (= 4) epochs +EPOCHS_PER_CUSTODY_PERIOD: 4 +# 2**2 (= 4) epochs +CUSTODY_PERIOD_TO_RANDAO_PADDING: 4 # State vector lengths diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 52642c8f4..133834429 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -58,10 +58,15 @@ from eth2spec.utils.bls import ( bls_aggregate_pubkeys, bls_verify, bls_verify_multiple, + bls_signature_to_G2, ) from eth2spec.utils.hash_function import hash ''' +SUNDRY_CONSTANTS_FUNCTIONS = ''' +def ceillog2(x: uint64) -> int: + return (x - 1).bit_length() +''' SUNDRY_FUNCTIONS = ''' # Monkey patch hash cache _hash = hash @@ -111,6 +116,13 @@ def apply_constants_preset(preset: Dict[str, Any]) -> None: ''' +def remove_for_phase1(functions: Dict[str, str]): + for key, value in functions.items(): + lines = value.split("\n") + lines = filter(lambda s: "[to be removed in phase 1]" not in s, lines) + functions[key] = "\n".join(lines) + + def strip_comments(raw: str) -> str: comment_line_regex = re.compile(r'^\s+# ') lines = raw.split('\n') @@ -141,10 +153,15 @@ def objects_to_spec(functions: Dict[str, str], ] ) ) + for k in list(functions): + if "ceillog2" in k: + del functions[k] functions_spec = '\n\n'.join(functions.values()) for k in list(constants.keys()): if k.startswith('DOMAIN_'): constants[k] = f"DomainType(({constants[k]}).to_bytes(length=4, byteorder='little'))" + if k == "BLS12_381_Q": + constants[k] += " # noqa: E501" constants_spec = '\n'.join(map(lambda x: '%s = %s' % (x, constants[x]), constants)) ssz_objects_instantiation_spec = '\n\n'.join(ssz_objects.values()) ssz_objects_reinitialization_spec = ( @@ -157,6 +174,7 @@ def objects_to_spec(functions: Dict[str, str], spec = ( imports + '\n\n' + new_type_definitions + + '\n' + SUNDRY_CONSTANTS_FUNCTIONS + '\n\n' + constants_spec + '\n\n\n' + ssz_objects_instantiation_spec + '\n\n' + functions_spec @@ -186,7 +204,7 @@ ignored_dependencies = [ 'bit', 'boolean', 'Vector', 'List', 'Container', 'Hash', 'BLSPubkey', 'BLSSignature', 'Bytes', 'BytesN' 'Bytes1', 'Bytes4', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector', 'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256', - 'bytes' # to be removed after updating spec doc + 'bytes', 'byte', 'BytesN' # to be removed after updating spec doc ] @@ -268,6 +286,7 @@ def build_phase1_spec(phase0_sourcefile: str, fork_choice_sourcefile: str, outfile: str=None) -> Optional[str]: phase0_spec = get_spec(phase0_sourcefile) + remove_for_phase1(phase0_spec[0]) phase1_custody = get_spec(phase1_custody_sourcefile) phase1_shard_data = get_spec(phase1_shard_sourcefile) fork_choice_spec = get_spec(fork_choice_sourcefile) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f0169f1d2..1eddae58e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -660,8 +660,8 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe bit_1_indices = indexed_attestation.custody_bit_1_indices # Verify no index has custody bit equal to 1 [to be removed in phase 1] - if not len(bit_1_indices) == 0: - return False + if not len(bit_1_indices) == 0: # [to be removed in phase 1] + return False # [to be removed in phase 1] # Verify max number of indices if not len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE: return False @@ -1661,6 +1661,11 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: proposer_index=get_beacon_proposer_index(state), ) + # Check bitlist lengths + committee_size = get_committee_count(state, attestation.data.target.epoch) + assert len(attestation.aggregation_bits) == committee_size + assert len(attestation.custody_bits) == committee_size + if data.target.epoch == get_current_epoch(state): assert data.source == state.current_justified_checkpoint parent_crosslink = state.current_crosslinks[data.crosslink.shard] diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index f79977442..3e0a38102 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -12,6 +12,7 @@ - [Terminology](#terminology) - [Constants](#constants) - [Misc](#misc) + - [Custody game parameters](#custody-game-parameters) - [Time parameters](#time-parameters) - [Max operations per block](#max-operations-per-block) - [Reward and penalty quotients](#reward-and-penalty-quotients) @@ -33,12 +34,14 @@ - [`BeaconBlockBody`](#beaconblockbody) - [Helpers](#helpers) - [`ceillog2`](#ceillog2) + - [`is_valid_merkle_branch_with_mixin`](#is_valid_merkle_branch_with_mixin) - [`get_crosslink_chunk_count`](#get_crosslink_chunk_count) - - [`get_bit`](#get_bit) + - [`legendre_bit`](#legendre_bit) + - [`custody_subchunkify`](#custody_subchunkify) - [`get_custody_chunk_bit`](#get_custody_chunk_bit) - [`get_chunk_bits_root`](#get_chunk_bits_root) - [`get_randao_epoch_for_custody_period`](#get_randao_epoch_for_custody_period) - - [`get_reveal_period`](#get_reveal_period) + - [`get_custody_period_for_validator`](#get_custody_period_for_validator) - [`replace_empty_or_append`](#replace_empty_or_append) - [Per-block processing](#per-block-processing) - [Operations](#operations) @@ -75,11 +78,20 @@ This document details the beacon chain additions and changes in Phase 1 of Ether ### Misc +| `BLS12_381_Q` | `4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787` | +| `MINOR_REWARD_QUOTIENT` | `2**8` (= 256) | + +### Custody game parameters + | Name | Value | | - | - | | `BYTES_PER_SHARD_BLOCK` | `2**14` (= 16,384) | | `BYTES_PER_CUSTODY_CHUNK` | `2**9` (= 512) | -| `MINOR_REWARD_QUOTIENT` | `2**8` (= 256) | +| `BYTES_PER_CUSTODY_SUBCHUNK` | `48` | +| `CHUNKS_PER_EPOCH` | `2 * BYTES_PER_SHARD_BLOCK * SLOTS_PER_EPOCH // BYTES_PER_CUSTODY_CHUNK` | +| `MAX_CUSTODY_CHUNKS` | `MAX_EPOCHS_PER_CROSSLINK * CHUNKS_PER_EPOCH` | +| `CUSTODY_DATA_DEPTH` | `ceillog2(MAX_CUSTODY_CHUNKS) + 1` | +| `CUSTODY_CHUNK_BIT_DEPTH` | `ceillog2(MAX_EPOCHS_PER_CROSSLINK * CHUNKS_PER_EPOCH // 256) + 2` | ### Time parameters @@ -144,7 +156,7 @@ class CustodyBitChallenge(Container): attestation: Attestation challenger_index: ValidatorIndex responder_key: BLSSignature - chunk_bits: Bytes[PLACEHOLDER] + chunk_bits: Bitlist[MAX_CUSTODY_CHUNKS] signature: BLSSignature ``` @@ -181,10 +193,10 @@ class CustodyBitChallengeRecord(Container): class CustodyResponse(Container): challenge_index: uint64 chunk_index: uint64 - chunk: Vector[Bytes[PLACEHOLDER], BYTES_PER_CUSTODY_CHUNK] - data_branch: List[Hash, PLACEHOLDER] - chunk_bits_branch: List[Hash, PLACEHOLDER] - chunk_bits_leaf: Hash + chunk: BytesN[BYTES_PER_CUSTODY_CHUNK] + data_branch: List[Hash, CUSTODY_DATA_DEPTH] + chunk_bits_branch: List[Hash, CUSTODY_CHUNK_BIT_DEPTH] + chunk_bits_leaf: Bitvector[256] ``` ### New beacon operations @@ -225,11 +237,11 @@ Add the following fields to the end of the specified container objects. Fields w ```python class Validator(Container): - # next_custody_reveal_period is initialised to the custody period + # next_custody_secret_to_reveal is initialised to the custody period # (of the particular validator) in which the validator is activated - # = get_reveal_period(...) - next_custody_reveal_period: uint64 - max_reveal_lateness: uint64 + # = get_custody_period_for_validator(...) + next_custody_secret_to_reveal: uint64 + max_reveal_lateness: Epoch ``` #### `BeaconState` @@ -263,7 +275,26 @@ class BeaconBlockBody(Container): ```python def ceillog2(x: uint64) -> int: - return x.bit_length() + return (x - 1).bit_length() +``` + +### `is_valid_merkle_branch_with_mixin` + +```python +def is_valid_merkle_branch_with_mixin(leaf: Hash, + branch: Sequence[Hash], + depth: uint64, + index: uint64, + root: Hash, + mixin: uint64) -> bool: + value = leaf + for i in range(depth): + if index // (2**i) % 2: + value = hash(branch[i] + value) + else: + value = hash(value + branch[i]) + value = hash(value + mixin.to_bytes(32, "little")) + return value == root ``` ### `get_crosslink_chunk_count` @@ -271,37 +302,69 @@ def ceillog2(x: uint64) -> int: ```python def get_custody_chunk_count(crosslink: Crosslink) -> int: crosslink_length = min(MAX_EPOCHS_PER_CROSSLINK, crosslink.end_epoch - crosslink.start_epoch) - chunks_per_epoch = 2 * BYTES_PER_SHARD_BLOCK * SLOTS_PER_EPOCH // BYTES_PER_CUSTODY_CHUNK - return crosslink_length * chunks_per_epoch + return crosslink_length * CHUNKS_PER_EPOCH ``` -### `get_bit` +### `legendre_bit` + +Returns the Legendre symbol `(a/q)` normalizes as a bit (i.e. `((a/q) + 1) // 2`). In a production implementation, a well-optimized library (e.g. GMP) should be used for this. ```python -def get_bit(serialization: bytes, i: uint64) -> int: - """ - Extract the bit in ``serialization`` at position ``i``. - """ - return (serialization[i // 8] >> (i % 8)) % 2 +def legendre_bit(a: int, q: int) -> int: + if a >= q: + return legendre_bit(a % q, q) + if a == 0: + return 0 + assert(q > a > 0 and q % 2 == 1) + t = 1 + n = q + while a != 0: + while a % 2 == 0: + a //= 2 + r = n % 8 + if r == 3 or r == 5: + t = -t + a, n = n, a + if a % 4 == n % 4 == 3: + t = -t + a %= n + if n == 1: + return (t + 1) // 2 + else: + return 0 +``` + +### ```custody_subchunkify``` + +Given one proof of custody chunk, returns the proof of custody subchunks of the correct sizes. + +```python +def custody_subchunkify(bytez: bytes) -> list: + bytez += b'\x00' * (-len(bytez) % BYTES_PER_CUSTODY_SUBCHUNK) + return [bytez[i:i + BYTES_PER_CUSTODY_SUBCHUNK] + for i in range(0, len(bytez), BYTES_PER_CUSTODY_SUBCHUNK)] ``` ### `get_custody_chunk_bit` ```python def get_custody_chunk_bit(key: BLSSignature, chunk: bytes) -> bool: - # TODO: Replace with something MPC-friendly, e.g. the Legendre symbol - return bool(get_bit(hash(key + chunk), 0)) + full_G2_element = bls_signature_to_G2(key) + s = full_G2_element[0].coeffs + bits = [legendre_bit((i + 1) * s[i % 2] + int.from_bytes(subchunk, "little"), BLS12_381_Q) + for i, subchunk in enumerate(custody_subchunkify(chunk))] + + return bool(sum(bits) % 2) ``` ### `get_chunk_bits_root` ```python -def get_chunk_bits_root(chunk_bits: bytes) -> Hash: - aggregated_bits = bytearray([0] * 32) - for i in range(0, len(chunk_bits), 32): - for j in range(32): - aggregated_bits[j] ^= chunk_bits[i + j] - return hash(aggregated_bits) +def get_chunk_bits_root(chunk_bits: Bitlist[MAX_CUSTODY_CHUNKS]) -> bit: + aggregated_bits = 0 + for i, b in enumerate(chunk_bits): + aggregated_bits += 2**i * b + return legendre_bit(aggregated_bits, BLS12_381_Q) ``` ### `get_randao_epoch_for_custody_period` @@ -312,10 +375,10 @@ def get_randao_epoch_for_custody_period(period: uint64, validator_index: Validat return Epoch(next_period_start + CUSTODY_PERIOD_TO_RANDAO_PADDING) ``` -### `get_reveal_period` +### `get_custody_period_for_validator` ```python -def get_reveal_period(state: BeaconState, validator_index: ValidatorIndex, epoch: Epoch=None) -> int: +def get_custody_period_for_validator(state: BeaconState, validator_index: ValidatorIndex, epoch: Epoch=None) -> int: ''' Return the reveal period for a given validator. ''' @@ -354,9 +417,9 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> Note that this function mutates ``state``. """ revealer = state.validators[reveal.revealer_index] - epoch_to_sign = get_randao_epoch_for_custody_period(revealer.next_custody_reveal_period, reveal.revealed_index) + epoch_to_sign = get_randao_epoch_for_custody_period(revealer.next_custody_secret_to_reveal, reveal.revealer_index) - assert revealer.next_custody_reveal_period < get_reveal_period(state, reveal.revealed_index) + assert revealer.next_custody_secret_to_reveal < get_custody_period_for_validator(state, reveal.revealer_index) # Revealed validator is active or exited, but not withdrawn assert is_slashable_validator(revealer, get_current_epoch(state)) @@ -374,15 +437,19 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> ) # Decrement max reveal lateness if response is timely - if revealer.next_custody_reveal_period == get_reveal_period(state, reveal.revealer_index) - 2: - revealer.max_reveal_lateness -= MAX_REVEAL_LATENESS_DECREMENT - revealer.max_reveal_lateness = max( - revealer.max_reveal_lateness, - get_reveal_period(state, reveal.revealed_index) - revealer.next_custody_reveal_period - ) + if epoch_to_sign + EPOCHS_PER_CUSTODY_PERIOD >= get_current_epoch(state): + if revealer.max_reveal_lateness >= MAX_REVEAL_LATENESS_DECREMENT: + revealer.max_reveal_lateness -= MAX_REVEAL_LATENESS_DECREMENT + else: + revealer.max_reveal_lateness = 0 + else: + revealer.max_reveal_lateness = max( + revealer.max_reveal_lateness, + get_current_epoch(state) - epoch_to_sign - EPOCHS_PER_CUSTODY_PERIOD + ) # Process reveal - revealer.next_custody_reveal_period += 1 + revealer.next_custody_secret_to_reveal += 1 # Reward Block Preposer proposer_index = get_beacon_proposer_index(state) @@ -520,7 +587,7 @@ For each `challenge` in `block.body.custody_bit_challenges`, run the following f ```python def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> None: attestation = challenge.attestation - epoch = compute_epoch_of_slot(attestation.data.slot) + epoch = attestation.data.target.epoch shard = attestation.data.crosslink.shard # Verify challenge signature @@ -533,7 +600,10 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) # Verify attestation is eligible for challenging responder = state.validators[challenge.responder_index] - assert epoch + responder.max_reveal_lateness <= get_reveal_period(state, challenge.responder_index) + assert get_current_epoch(state) <= get_randao_epoch_for_custody_period( + get_custody_period_for_validator(state, challenge.responder_index, epoch), + challenge.responder_index + ) + 2 * EPOCHS_PER_CUSTODY_PERIOD + responder.max_reveal_lateness # Verify the responder participated in the attestation attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) @@ -543,17 +613,18 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> assert record.challenger_index != challenge.challenger_index # Verify the responder custody key epoch_to_sign = get_randao_epoch_for_custody_period( - get_reveal_period(state, challenge.responder_index, epoch), + get_custody_period_for_validator(state, challenge.responder_index, epoch), challenge.responder_index, ) domain = get_domain(state, DOMAIN_RANDAO, epoch_to_sign) assert bls_verify(responder.pubkey, hash_tree_root(epoch_to_sign), challenge.responder_key, domain) # Verify the chunk count chunk_count = get_custody_chunk_count(attestation.data.crosslink) - # Verify the first bit of the hash of the chunk bits does not equal the custody bit + assert chunk_count == len(challenge.chunk_bits) + # Verify custody bit is incorrect committee = get_crosslink_committee(state, epoch, shard) custody_bit = attestation.custody_bits[committee.index(challenge.responder_index)] - assert custody_bit != get_bit(get_chunk_bits_root(challenge.chunk_bits), 0) + assert custody_bit != get_chunk_bits_root(challenge.chunk_bits) # Add new bit challenge record new_record = CustodyBitChallengeRecord( challenge_index=state.custody_challenge_index, @@ -636,16 +707,17 @@ def process_bit_challenge_response(state: BeaconState, root=challenge.data_root, ) # Verify the chunk bit leaf matches the challenge data - assert is_valid_merkle_branch( - leaf=response.chunk_bits_leaf, + assert is_valid_merkle_branch_with_mixin( + leaf=hash_tree_root(response.chunk_bits_leaf), branch=response.chunk_bits_branch, - depth=ceillog2(challenge.chunk_count) >> 8, + depth=ceillog2(MAX_CUSTODY_CHUNKS // 256), index=response.chunk_index // 256, - root=challenge.chunk_bits_merkle_root + root=challenge.chunk_bits_merkle_root, + mixin=challenge.chunk_count, ) # Verify the chunk bit does not match the challenge chunk bit assert (get_custody_chunk_bit(challenge.responder_key, response.chunk) - != get_bit(challenge.chunk_bits_leaf, response.chunk_index % 256)) + != response.chunk_bits_leaf[response.chunk_index % 256]) # Clear the challenge records = state.custody_bit_challenge_records records[records.index(challenge)] = CustodyBitChallengeRecord() @@ -665,8 +737,8 @@ Run `process_reveal_deadlines(state)` immediately after `process_registry_update # end insert @process_reveal_deadlines def process_reveal_deadlines(state: BeaconState) -> None: for index, validator in enumerate(state.validators): - deadline = validator.next_custody_reveal_period + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD) - if get_reveal_period(state, ValidatorIndex(index)) > deadline: + deadline = validator.next_custody_secret_to_reveal + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD) + if get_custody_period_for_validator(state, ValidatorIndex(index)) > deadline: slash_validator(state, ValidatorIndex(index)) ``` diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py index 400675888..ee1c04219 100644 --- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py @@ -1,4 +1,4 @@ -from eth2spec.test.context import with_all_phases, with_state, bls_switch +from eth2spec.test.context import with_all_phases, with_state, bls_switch, with_phases from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation @@ -103,7 +103,7 @@ def test_on_attestation_same_slot(spec, state): run_on_attestation(spec, state, store, attestation, False) -@with_all_phases +@with_phases(['phase0']) @with_state @bls_switch def test_on_attestation_invalid_attestation(spec, state): diff --git a/test_libs/pyspec/eth2spec/test/helpers/custody.py b/test_libs/pyspec/eth2spec/test/helpers/custody.py index 36f23ad1c..4b7c8c97b 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/custody.py +++ b/test_libs/pyspec/eth2spec/test/helpers/custody.py @@ -1,6 +1,11 @@ from eth2spec.test.helpers.keys import privkeys from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures from eth2spec.utils.hash_function import hash +from eth2spec.utils.ssz.ssz_typing import Bitlist, BytesN, Bitvector +from eth2spec.utils.ssz.ssz_impl import chunkify, pack, hash_tree_root +from eth2spec.utils.merkle_minimal import get_merkle_tree, get_merkle_proof + +BYTES_PER_CHUNK = 32 def get_valid_early_derived_secret_reveal(spec, state, epoch=None): @@ -13,7 +18,7 @@ def get_valid_early_derived_secret_reveal(spec, state, epoch=None): # Generate the secret that is being revealed reveal = bls_sign( - message_hash=spec.hash_tree_root(spec.Epoch(epoch)), + message_hash=hash_tree_root(spec.Epoch(epoch)), privkey=privkeys[revealed_index], domain=spec.get_domain( state=state, @@ -42,3 +47,128 @@ def get_valid_early_derived_secret_reveal(spec, state, epoch=None): masker_index=masker_index, mask=mask, ) + + +def get_valid_custody_key_reveal(spec, state, period=None): + current_epoch = spec.get_current_epoch(state) + revealer_index = spec.get_active_validator_indices(state, current_epoch)[0] + revealer = state.validators[revealer_index] + + if period is None: + period = revealer.next_custody_secret_to_reveal + + epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, revealer_index) + + # Generate the secret that is being revealed + reveal = bls_sign( + message_hash=hash_tree_root(spec.Epoch(epoch_to_sign)), + privkey=privkeys[revealer_index], + domain=spec.get_domain( + state=state, + domain_type=spec.DOMAIN_RANDAO, + message_epoch=epoch_to_sign, + ), + ) + return spec.CustodyKeyReveal( + revealer_index=revealer_index, + reveal=reveal, + ) + + +def bitlist_from_int(max_len, num_bits, n): + return Bitlist[max_len](*[(n >> i) & 0b1 for i in range(num_bits)]) + + +def get_valid_bit_challenge(spec, state, attestation, invalid_custody_bit=False): + crosslink_committee = spec.get_crosslink_committee( + state, + attestation.data.target.epoch, + attestation.data.crosslink.shard, + ) + responder_index = crosslink_committee[0] + challenger_index = crosslink_committee[-1] + + epoch = spec.get_randao_epoch_for_custody_period(attestation.data.target.epoch, + responder_index) + + # Generate the responder key + responder_key = bls_sign( + message_hash=hash_tree_root(spec.Epoch(epoch)), + privkey=privkeys[responder_index], + domain=spec.get_domain( + state=state, + domain_type=spec.DOMAIN_RANDAO, + message_epoch=epoch, + ), + ) + + chunk_count = spec.get_custody_chunk_count(attestation.data.crosslink) + + chunk_bits = bitlist_from_int(spec.MAX_CUSTODY_CHUNKS, chunk_count, 0) + + n = 0 + while spec.get_chunk_bits_root(chunk_bits) == attestation.custody_bits[0] ^ invalid_custody_bit: + chunk_bits = bitlist_from_int(spec.MAX_CUSTODY_CHUNKS, chunk_count, n) + n += 1 + + return spec.CustodyBitChallenge( + responder_index=responder_index, + attestation=attestation, + challenger_index=challenger_index, + responder_key=responder_key, + chunk_bits=chunk_bits, + ) + + +def custody_chunkify(spec, x): + chunks = [bytes(x[i:i + spec.BYTES_PER_CUSTODY_CHUNK]) for i in range(0, len(x), spec.BYTES_PER_CUSTODY_CHUNK)] + chunks[-1] = chunks[-1].ljust(spec.BYTES_PER_CUSTODY_CHUNK, b"\0") + return chunks + + +def get_valid_custody_response(spec, state, bit_challenge, custody_data, challenge_index, invalid_chunk_bit=False): + chunks = custody_chunkify(spec, custody_data) + + chunk_index = len(chunks) - 1 + chunk_bit = spec.get_custody_chunk_bit(bit_challenge.responder_key, chunks[chunk_index]) + + while chunk_bit == bit_challenge.chunk_bits[chunk_index] ^ invalid_chunk_bit: + chunk_index -= 1 + chunk_bit = spec.get_custody_chunk_bit(bit_challenge.responder_key, chunks[chunk_index]) + + chunks_hash_tree_roots = [hash_tree_root(BytesN[spec.BYTES_PER_CUSTODY_CHUNK](chunk)) for chunk in chunks] + chunks_hash_tree_roots += [ + hash_tree_root(BytesN[spec.BYTES_PER_CUSTODY_CHUNK](b"\0" * spec.BYTES_PER_CUSTODY_CHUNK)) + for i in range(2 ** spec.ceillog2(len(chunks)) - len(chunks))] + data_tree = get_merkle_tree(chunks_hash_tree_roots) + + data_branch = get_merkle_proof(data_tree, chunk_index) + + bitlist_chunk_index = chunk_index // BYTES_PER_CHUNK + bitlist_chunks = chunkify(pack(bit_challenge.chunk_bits)) + bitlist_tree = get_merkle_tree(bitlist_chunks, pad_to=spec.MAX_CUSTODY_CHUNKS // 256) + bitlist_chunk_branch = get_merkle_proof(bitlist_tree, chunk_index // 256) + \ + [len(bit_challenge.chunk_bits).to_bytes(32, "little")] + + bitlist_chunk_index = chunk_index // 256 + + chunk_bits_leaf = Bitvector[256](bit_challenge.chunk_bits[bitlist_chunk_index * 256: + (bitlist_chunk_index + 1) * 256]) + + return spec.CustodyResponse( + challenge_index=challenge_index, + chunk_index=chunk_index, + chunk=BytesN[spec.BYTES_PER_CUSTODY_CHUNK](chunks[chunk_index]), + data_branch=data_branch, + chunk_bits_branch=bitlist_chunk_branch, + chunk_bits_leaf=chunk_bits_leaf, + ) + + +def get_custody_test_vector(bytelength): + ints = bytelength // 4 + return b"".join(i.to_bytes(4, "little") for i in range(ints)) + + +def get_custody_merkle_root(data): + return get_merkle_tree(chunkify(data))[-1][0] diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index 8dc6b3b58..89574c977 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -47,7 +47,7 @@ def build_deposit(spec, deposit_data_list.append(deposit_data) root = hash_tree_root(List[spec.DepositData, 2**spec.DEPOSIT_CONTRACT_TREE_DEPTH](*deposit_data_list)) tree = calc_merkle_tree_from_leaves(tuple([d.hash_tree_root() for d in deposit_data_list])) - proof = list(get_merkle_proof(tree, item_index=index)) + [(index + 1).to_bytes(32, 'little')] + proof = list(get_merkle_proof(tree, item_index=index, tree_len=32)) + [(index + 1).to_bytes(32, 'little')] leaf = deposit_data.hash_tree_root() assert spec.is_valid_merkle_branch(leaf, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH + 1, index, root) deposit = spec.Deposit(proof=proof, data=deposit_data) 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 ab46a0d8c..84cb95ba0 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 @@ -363,7 +363,7 @@ def test_inconsistent_bits(spec, state): attestation = get_valid_attestation(spec, state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - custody_bits = attestation.aggregation_bits[:] + custody_bits = attestation.custody_bits[:] custody_bits.append(False) attestation.custody_bits = custody_bits diff --git a/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_bit_challenge.py b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_bit_challenge.py new file mode 100644 index 000000000..e4880555a --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_bit_challenge.py @@ -0,0 +1,347 @@ +from eth2spec.test.helpers.custody import ( + get_valid_bit_challenge, + get_valid_custody_response, + get_custody_test_vector, + get_custody_merkle_root +) +from eth2spec.test.helpers.attestations import ( + get_valid_attestation, +) +from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.test.helpers.state import next_epoch, get_balance +from eth2spec.test.helpers.block import apply_empty_block +from eth2spec.test.context import ( + with_all_phases_except, + spec_state_test, + expect_assertion_error, +) +from eth2spec.test.phase_0.block_processing.test_process_attestation import run_attestation_processing + + +def run_bit_challenge_processing(spec, state, custody_bit_challenge, valid=True): + """ + Run ``process_bit_challenge``, yielding: + - pre-state ('pre') + - CustodyBitChallenge ('custody_bit_challenge') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + yield 'pre', state + yield 'custody_bit_challenge', custody_bit_challenge + + if not valid: + expect_assertion_error(lambda: spec.process_bit_challenge(state, custody_bit_challenge)) + yield 'post', None + return + + spec.process_bit_challenge(state, custody_bit_challenge) + + assert state.custody_bit_challenge_records[state.custody_challenge_index - 1].chunk_bits_merkle_root == \ + hash_tree_root(custody_bit_challenge.chunk_bits) + assert state.custody_bit_challenge_records[state.custody_challenge_index - 1].challenger_index == \ + custody_bit_challenge.challenger_index + assert state.custody_bit_challenge_records[state.custody_challenge_index - 1].responder_index == \ + custody_bit_challenge.responder_index + + yield 'post', state + + +def run_custody_response_processing(spec, state, custody_response, valid=True): + """ + Run ``process_bit_challenge_response``, yielding: + - pre-state ('pre') + - CustodyResponse ('custody_response') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + yield 'pre', state + yield 'custody_response', custody_response + + if not valid: + expect_assertion_error(lambda: spec.process_custody_response(state, custody_response)) + yield 'post', None + return + + # TODO: Add capability to also process chunk challenges, not only bit challenges + challenge = state.custody_bit_challenge_records[custody_response.challenge_index] + pre_slashed_balance = get_balance(state, challenge.challenger_index) + + spec.process_custody_response(state, custody_response) + + slashed_validator = state.validators[challenge.challenger_index] + + assert slashed_validator.slashed + assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH + assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH + + assert get_balance(state, challenge.challenger_index) < pre_slashed_balance + yield 'post', state + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_challenge_appended(spec, state): + state.slot = spec.SLOTS_PER_EPOCH + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * spec.EPOCHS_PER_CUSTODY_PERIOD + + challenge = get_valid_bit_challenge(spec, state, attestation) + + yield from run_bit_challenge_processing(spec, state, challenge) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_multiple_epochs_custody(spec, state): + state.slot = spec.SLOTS_PER_EPOCH * 3 + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1) + + challenge = get_valid_bit_challenge(spec, state, attestation) + + yield from run_bit_challenge_processing(spec, state, challenge) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_many_epochs_custody(spec, state): + state.slot = spec.SLOTS_PER_EPOCH * 100 + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1) + + challenge = get_valid_bit_challenge(spec, state, attestation) + + yield from run_bit_challenge_processing(spec, state, challenge) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_off_chain_attestation(spec, state): + state.slot = spec.SLOTS_PER_EPOCH + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + state.slot += spec.SLOTS_PER_EPOCH * spec.EPOCHS_PER_CUSTODY_PERIOD + + challenge = get_valid_bit_challenge(spec, state, attestation) + + yield from run_bit_challenge_processing(spec, state, challenge) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_invalid_custody_bit_challenge(spec, state): + state.slot = spec.SLOTS_PER_EPOCH + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * spec.EPOCHS_PER_CUSTODY_PERIOD + + challenge = get_valid_bit_challenge(spec, state, attestation, invalid_custody_bit=True) + + yield from run_bit_challenge_processing(spec, state, challenge, valid=False) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_max_reveal_lateness_1(spec, state): + next_epoch(spec, state) + apply_empty_block(spec, state) + + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + next_epoch(spec, state) + apply_empty_block(spec, state) + + _, _, _ = run_attestation_processing(spec, state, attestation) + + challenge = get_valid_bit_challenge(spec, state, attestation) + + responder_index = challenge.responder_index + + state.validators[responder_index].max_reveal_lateness = 3 + + for i in range(spec.get_randao_epoch_for_custody_period( + spec.get_custody_period_for_validator(state, responder_index), + responder_index + ) + 2 * spec.EPOCHS_PER_CUSTODY_PERIOD + state.validators[responder_index].max_reveal_lateness - 2): + next_epoch(spec, state) + apply_empty_block(spec, state) + + yield from run_bit_challenge_processing(spec, state, challenge) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_max_reveal_lateness_2(spec, state): + next_epoch(spec, state) + apply_empty_block(spec, state) + + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + next_epoch(spec, state) + apply_empty_block(spec, state) + + _, _, _ = run_attestation_processing(spec, state, attestation) + + challenge = get_valid_bit_challenge(spec, state, attestation) + + responder_index = challenge.responder_index + + state.validators[responder_index].max_reveal_lateness = 3 + + for i in range(spec.get_randao_epoch_for_custody_period( + spec.get_custody_period_for_validator(state, responder_index), + responder_index + ) + 2 * spec.EPOCHS_PER_CUSTODY_PERIOD + state.validators[responder_index].max_reveal_lateness - 1): + next_epoch(spec, state) + apply_empty_block(spec, state) + + yield from run_bit_challenge_processing(spec, state, challenge, False) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_custody_response(spec, state): + state.slot = spec.SLOTS_PER_EPOCH + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * spec.EPOCHS_PER_CUSTODY_PERIOD + + challenge = get_valid_bit_challenge(spec, state, attestation) + + _, _, _ = run_bit_challenge_processing(spec, state, challenge) + + bit_challenge_index = state.custody_challenge_index - 1 + + custody_response = get_valid_custody_response(spec, state, challenge, test_vector, bit_challenge_index) + + yield from run_custody_response_processing(spec, state, custody_response) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_custody_response_multiple_epochs(spec, state): + state.slot = spec.SLOTS_PER_EPOCH * 3 + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * spec.EPOCHS_PER_CUSTODY_PERIOD + + challenge = get_valid_bit_challenge(spec, state, attestation) + + _, _, _ = run_bit_challenge_processing(spec, state, challenge) + + bit_challenge_index = state.custody_challenge_index - 1 + + custody_response = get_valid_custody_response(spec, state, challenge, test_vector, bit_challenge_index) + + yield from run_custody_response_processing(spec, state, custody_response) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_custody_response_many_epochs(spec, state): + state.slot = spec.SLOTS_PER_EPOCH * 100 + attestation = get_valid_attestation(spec, state, signed=True) + + test_vector = get_custody_test_vector( + spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) + shard_root = get_custody_merkle_root(test_vector) + attestation.data.crosslink.data_root = shard_root + attestation.custody_bits[0] = 0 + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + _, _, _ = run_attestation_processing(spec, state, attestation) + + state.slot += spec.SLOTS_PER_EPOCH * spec.EPOCHS_PER_CUSTODY_PERIOD + + challenge = get_valid_bit_challenge(spec, state, attestation) + + _, _, _ = run_bit_challenge_processing(spec, state, challenge) + + bit_challenge_index = state.custody_challenge_index - 1 + + custody_response = get_valid_custody_response(spec, state, challenge, test_vector, bit_challenge_index) + + yield from run_custody_response_processing(spec, state, custody_response) diff --git a/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py new file mode 100644 index 000000000..f8860cf87 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py @@ -0,0 +1,118 @@ +from eth2spec.test.helpers.custody import get_valid_custody_key_reveal +from eth2spec.test.context import ( + with_all_phases_except, + spec_state_test, + expect_assertion_error, + always_bls, +) + + +def run_custody_key_reveal_processing(spec, state, custody_key_reveal, valid=True): + """ + Run ``process_custody_key_reveal``, yielding: + - pre-state ('pre') + - custody_key_reveal ('custody_key_reveal') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + yield 'pre', state + yield 'custody_key_reveal', custody_key_reveal + + if not valid: + expect_assertion_error(lambda: spec.process_custody_key_reveal(state, custody_key_reveal)) + yield 'post', None + return + + revealer_index = custody_key_reveal.revealer_index + + pre_next_custody_secret_to_reveal = \ + state.validators[revealer_index].next_custody_secret_to_reveal + pre_reveal_lateness = state.validators[revealer_index].max_reveal_lateness + + spec.process_custody_key_reveal(state, custody_key_reveal) + + post_next_custody_secret_to_reveal = \ + state.validators[revealer_index].next_custody_secret_to_reveal + post_reveal_lateness = state.validators[revealer_index].max_reveal_lateness + + assert post_next_custody_secret_to_reveal == pre_next_custody_secret_to_reveal + 1 + + if spec.get_current_epoch(state) > spec.get_randao_epoch_for_custody_period( + pre_next_custody_secret_to_reveal, + revealer_index + ) + spec.EPOCHS_PER_CUSTODY_PERIOD: + assert post_reveal_lateness > 0 + if pre_reveal_lateness == 0: + assert post_reveal_lateness == spec.get_current_epoch(state) - spec.get_randao_epoch_for_custody_period( + pre_next_custody_secret_to_reveal, + revealer_index + ) - spec.EPOCHS_PER_CUSTODY_PERIOD + else: + if pre_reveal_lateness > 0: + assert post_reveal_lateness < pre_reveal_lateness + + yield 'post', state + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_success(spec, state): + state.slot += spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH + custody_key_reveal = get_valid_custody_key_reveal(spec, state) + + yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal) + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_reveal_too_early(spec, state): + custody_key_reveal = get_valid_custody_key_reveal(spec, state) + + yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal, False) + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_wrong_period(spec, state): + custody_key_reveal = get_valid_custody_key_reveal(spec, state, period=5) + + yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal, False) + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_late_reveal(spec, state): + state.slot += spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH * 3 + 150 + custody_key_reveal = get_valid_custody_key_reveal(spec, state) + + yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal) + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_double_reveal(spec, state): + state.slot += spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH * 2 + custody_key_reveal = get_valid_custody_key_reveal(spec, state) + + _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) + + yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal, False) + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_max_decrement(spec, state): + state.slot += spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH * 3 + 150 + custody_key_reveal = get_valid_custody_key_reveal(spec, state) + + _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) + + custody_key_reveal2 = get_valid_custody_key_reveal(spec, state) + + yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal2) diff --git a/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py index 831ad35a5..63f4721b9 100644 --- a/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py +++ b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py @@ -98,25 +98,21 @@ def test_reveal_with_custody_padding_minus_one(spec, state): @never_bls @spec_state_test def test_double_reveal(spec, state): + epoch = spec.get_current_epoch(state) + spec.RANDAO_PENALTY_EPOCHS randao_key_reveal1 = get_valid_early_derived_secret_reveal( spec, state, - spec.get_current_epoch(state) + spec.RANDAO_PENALTY_EPOCHS + 1, + epoch, ) - res = dict(run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal1)) - pre_state = res['pre'] - yield 'pre', pre_state - intermediate_state = res['post'] + _, _, _ = dict(run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal1)) randao_key_reveal2 = get_valid_early_derived_secret_reveal( spec, - intermediate_state, - spec.get_current_epoch(pre_state) + spec.RANDAO_PENALTY_EPOCHS + 1, + state, + epoch, ) - res = dict(run_early_derived_secret_reveal_processing(spec, intermediate_state, randao_key_reveal2, False)) - post_state = res['post'] - yield 'randao_key_reveal', [randao_key_reveal1, randao_key_reveal2] - yield 'post', post_state + + yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal2, False) @with_all_phases_except(['phase0']) diff --git a/test_libs/pyspec/eth2spec/utils/bls.py b/test_libs/pyspec/eth2spec/utils/bls.py index d8a9ab5be..f40e5ab04 100644 --- a/test_libs/pyspec/eth2spec/utils/bls.py +++ b/test_libs/pyspec/eth2spec/utils/bls.py @@ -5,6 +5,7 @@ bls_active = True STUB_SIGNATURE = b'\x11' * 96 STUB_PUBKEY = b'\x22' * 48 +STUB_COORDINATES = bls.api.signature_to_G2(bls.sign(b"", 0, b"\0" * 8)) def only_with_bls(alt_return=None): @@ -47,3 +48,8 @@ def bls_aggregate_signatures(signatures): def bls_sign(message_hash, privkey, domain): return bls.sign(message_hash=message_hash, privkey=privkey, domain=domain) + + +@only_with_bls(alt_return=STUB_COORDINATES) +def bls_signature_to_G2(signature): + return bls.api.signature_to_G2(signature) diff --git a/test_libs/pyspec/eth2spec/utils/merkle_minimal.py b/test_libs/pyspec/eth2spec/utils/merkle_minimal.py index 9d7138d7d..aae7ff5c0 100644 --- a/test_libs/pyspec/eth2spec/utils/merkle_minimal.py +++ b/test_libs/pyspec/eth2spec/utils/merkle_minimal.py @@ -20,6 +20,13 @@ def calc_merkle_tree_from_leaves(values, layer_count=32): return tree +def get_merkle_tree(values, pad_to=None): + layer_count = (len(values) - 1).bit_length() if pad_to is None else (pad_to - 1).bit_length() + if len(values) == 0: + return zerohashes[layer_count] + return calc_merkle_tree_from_leaves(values, layer_count) + + def get_merkle_root(values, pad_to=1): if pad_to == 0: return zerohashes[0] @@ -29,9 +36,9 @@ def get_merkle_root(values, pad_to=1): return calc_merkle_tree_from_leaves(values, layer_count)[-1][0] -def get_merkle_proof(tree, item_index): +def get_merkle_proof(tree, item_index, tree_len=None): proof = [] - for i in range(32): + for i in range(tree_len if tree_len is not None else len(tree)): subindex = (item_index // 2**i) ^ 1 proof.append(tree[i][subindex] if subindex < len(tree[i]) else zerohashes[i]) return proof