From 6e73df06929a8911a541dff952d389576189ba16 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 16 Jan 2020 11:09:58 -0700 Subject: [PATCH 01/20] fix broken bls generators --- tests/generators/bls/main.py | 86 ++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index 587b3adc04..e8503c2e5c 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -20,6 +20,7 @@ def hash(x): F2Q_COEFF_LEN = 48 G2_COMPRESSED_Z_LEN = 48 +DST = b'BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_' def int_to_hex(n: int, byte_length: int = None) -> str: @@ -29,6 +30,13 @@ def int_to_hex(n: int, byte_length: int = None) -> str: return encode_hex(byte_value) +def int_to_bytes(n: int, byte_length: int = None) -> bytes: + byte_value = int_to_big_endian(n) + if byte_length: + byte_value = byte_value.rjust(byte_length, b'\x00') + return byte_value + + def hex_to_int(x: str) -> int: return int(x, 16) @@ -57,13 +65,11 @@ def hex_to_int(x: str) -> int: ] -def hash_message(msg: bytes, - domain: bytes) -> Tuple[Tuple[str, str], Tuple[str, str], Tuple[str, str]]: +def hash_message(msg: bytes) -> Tuple[Tuple[str, str], Tuple[str, str], Tuple[str, str]]: """ Hash message Input: - Message as bytes32 - - domain as bytes8 Output: - Message hash as a G2 point """ @@ -72,49 +78,44 @@ def hash_message(msg: bytes, int_to_hex(fq2.coeffs[0], F2Q_COEFF_LEN), int_to_hex(fq2.coeffs[1], F2Q_COEFF_LEN), ] - for fq2 in bls.utils.hash_to_G2(msg, domain) + for fq2 in bls.hash_to_curve.hash_to_G2(msg, DST) ] -def hash_message_compressed(msg: bytes, domain: bytes) -> Tuple[str, str]: +def hash_message_compressed(msg: bytes) -> Tuple[str, str]: """ Hash message Input: - Message as bytes32 - - domain as bytes8 Output: - Message hash as a compressed G2 point """ - z1, z2 = bls.utils.compress_G2(bls.utils.hash_to_G2(msg, domain)) + z1, z2 = bls.point_compression.compress_G2(bls.hash_to_curve.hash_to_G2(msg, DST)) return [int_to_hex(z1, G2_COMPRESSED_Z_LEN), int_to_hex(z2, G2_COMPRESSED_Z_LEN)] def case01_message_hash_G2_uncompressed(): for msg in MESSAGES: - for domain in DOMAINS: - yield f'uncom_g2_hash_{encode_hex(msg)}_{encode_hex(domain)}', { - 'input': { - 'message': encode_hex(msg), - 'domain': encode_hex(domain), - }, - 'output': hash_message(msg, domain) - } + yield f'uncom_g2_hash_{encode_hex(msg)}', { + 'input': { + 'message': encode_hex(msg), + }, + 'output': hash_message(msg) + } def case02_message_hash_G2_compressed(): for msg in MESSAGES: - for domain in DOMAINS: - yield f'com_g2_hash_{encode_hex(msg)}_{encode_hex(domain)}', { - 'input': { - 'message': encode_hex(msg), - 'domain': encode_hex(domain), - }, - 'output': hash_message_compressed(msg, domain) - } + yield f'com_g2_hash_{encode_hex(msg)}', { + 'input': { + 'message': encode_hex(msg), + }, + 'output': hash_message_compressed(msg) + } def case03_private_to_public_key(): - pubkeys = [bls.privtopub(privkey) for privkey in PRIVKEYS] + pubkeys = [bls.G2Basic.PrivToPub(privkey) for privkey in PRIVKEYS] pubkeys_serial = ['0x' + pubkey.hex() for pubkey in pubkeys] for privkey, pubkey_serial in zip(PRIVKEYS, pubkeys_serial): yield f'priv_to_pub_{int_to_hex(privkey)}', { @@ -126,17 +127,15 @@ def case03_private_to_public_key(): def case04_sign_messages(): for privkey in PRIVKEYS: for message in MESSAGES: - for domain in DOMAINS: - sig = bls.sign(message, privkey, domain) - full_name = f'{int_to_hex(privkey)}_{encode_hex(message)}_{encode_hex(domain)}' - yield f'sign_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { - 'input': { - 'privkey': int_to_hex(privkey), - 'message': encode_hex(message), - 'domain': encode_hex(domain), - }, - 'output': encode_hex(sig) - } + sig = bls.G2ProofOfPossession.Sign(privkey, message) + full_name = f'{int_to_hex(privkey)}_{encode_hex(message)}' + yield f'sign_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'privkey': int_to_hex(privkey), + 'message': encode_hex(message), + }, + 'output': encode_hex(sig) + } # TODO: case05_verify_messages: Verify messages signed in case04 @@ -144,21 +143,20 @@ def case04_sign_messages(): def case06_aggregate_sigs(): - for domain in DOMAINS: - for message in MESSAGES: - sigs = [bls.sign(message, privkey, domain) for privkey in PRIVKEYS] - yield f'agg_sigs_{encode_hex(message)}_{encode_hex(domain)}', { - 'input': [encode_hex(sig) for sig in sigs], - 'output': encode_hex(bls.aggregate_signatures(sigs)), - } + for message in MESSAGES: + sigs = [bls.G2ProofOfPossession.Sign(privkey, message) for privkey in PRIVKEYS] + yield f'agg_sigs_{encode_hex(message)}', { + 'input': [encode_hex(sig) for sig in sigs], + 'output': encode_hex(bls.G2ProofOfPossession.Aggregate(sigs)), + } def case07_aggregate_pubkeys(): - pubkeys = [bls.privtopub(privkey) for privkey in PRIVKEYS] + pubkeys = [bls.G2Basic.PrivToPub(privkey) for privkey in PRIVKEYS] pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys] yield f'agg_pub_keys', { 'input': pubkeys_serial, - 'output': encode_hex(bls.aggregate_pubkeys(pubkeys)), + 'output': encode_hex(bls.G2ProofOfPossession._AggregatePKs(pubkeys)), } From 2a0f68076995fe9915f8df8b58bc5f6273bc9942 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 16 Jan 2020 11:15:44 -0700 Subject: [PATCH 02/20] fix ssz_generic test generator --- tests/generators/ssz_generic/ssz_container.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/ssz_generic/ssz_container.py b/tests/generators/ssz_generic/ssz_container.py index 7dbd5e1110..ecb2d8c34e 100644 --- a/tests/generators/ssz_generic/ssz_container.py +++ b/tests/generators/ssz_generic/ssz_container.py @@ -1,6 +1,6 @@ from ssz_test_case import invalid_test_case, valid_test_case from eth2spec.utils.ssz.ssz_typing import SSZType, Container, byte, uint8, uint16, \ - uint32, uint64, List, Bytes, Vector, Bitvector, Bitlist + uint32, uint64, List, ByteList, Vector, Bitvector, Bitlist from eth2spec.utils.ssz.ssz_impl import serialize from random import Random from typing import Dict, Tuple, Sequence, Callable @@ -32,7 +32,7 @@ class ComplexTestStruct(Container): A: uint16 B: List[uint16, 128] C: uint8 - D: Bytes[256] + D: ByteList[256] E: VarTestStruct F: Vector[FixedTestStruct, 4] G: Vector[VarTestStruct, 2] From f1697d03e73e6d3adf68ae4f27d5b27ea3892edd Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 20 Jan 2020 17:40:36 -0700 Subject: [PATCH 03/20] fix corner case to properly handle skipped slots in get_ancestor --- specs/phase0/fork-choice.md | 20 +++------ .../test/fork_choice/test_on_block.py | 42 +++++++++++++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 0d9823fcdb..64d48d4b55 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -136,7 +136,8 @@ def get_ancestor(store: Store, root: Root, slot: Slot) -> Root: elif block.slot == slot: return root else: - return Bytes32() # root is older than queried slot: no results. + # root is older than queried slot, thus a skip slot. Return earliest root prior to slot + return root ``` #### `get_latest_attesting_balance` @@ -239,13 +240,8 @@ def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: C if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: return True - new_justified_block = store.blocks[new_justified_checkpoint.root] - if new_justified_block.slot <= compute_start_slot_at_epoch(store.justified_checkpoint.epoch): - return False - if not ( - get_ancestor(store, new_justified_checkpoint.root, store.blocks[store.justified_checkpoint.root].slot) - == store.justified_checkpoint.root - ): + justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch) + if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root: return False return True @@ -284,12 +280,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add new block to the store store.blocks[hash_tree_root(block)] = block # Check block is a descendant of the finalized block - assert ( - get_ancestor(store, hash_tree_root(block), store.blocks[store.finalized_checkpoint.root].slot) == - store.finalized_checkpoint.root - ) - # Check that block is later than the finalized epoch slot - assert block.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + assert get_ancestor(store, hash_tree_root(block), finalized_slot) == store.finalized_checkpoint.root # Check the block is valid and compute the post-state state = state_transition(pre_state, signed_block, True) # Add new state for this block to the store diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py index 10d1c0011b..f9995ad13f 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py @@ -135,6 +135,48 @@ def test_on_block_before_finalized(spec, state): run_on_block(spec, store, signed_block, False) +@with_all_phases +@spec_state_test +def test_on_block_finalized_skip_slots(spec, state): + # Initialization + store = spec.get_genesis_store(state) + time = 100 + spec.on_tick(store, time) + + store.finalized_checkpoint = spec.Checkpoint( + epoch=store.finalized_checkpoint.epoch + 2, + root=store.finalized_checkpoint.root + ) + + # Build block that includes the skipped slots up to finality in chain + block = build_empty_block(spec, state, spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + 2) + signed_block = state_transition_and_sign_block(spec, state, block) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + run_on_block(spec, store, signed_block) + + +@with_all_phases +@spec_state_test +def test_on_block_finalized_skip_slots_not_in_skip_chain(spec, state): + # Initialization + store = spec.get_genesis_store(state) + + store.finalized_checkpoint = spec.Checkpoint( + epoch=store.finalized_checkpoint.epoch + 2, + root=store.finalized_checkpoint.root + ) + + # First transition through the epoch to ensure no skipped slots + state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store) + + # Now build a block at later slot than finalized epoch + # Includes finalized block in chain, but not at appropriate skip slot + block = build_empty_block(spec, state, spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + 2) + signed_block = state_transition_and_sign_block(spec, state, block) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + run_on_block(spec, store, signed_block, False) + + @with_all_phases @spec_state_test def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state): From e98c1b415445e239c98e3382ed4ba475ee5e3c84 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 20 Jan 2020 18:10:39 -0700 Subject: [PATCH 04/20] don't consider blocks with slots earlier than finalized in on_block fork choice (optimization) --- specs/phase0/fork-choice.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 64d48d4b55..39d9bd4026 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -279,9 +279,13 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: assert get_current_slot(store) >= block.slot # Add new block to the store store.blocks[hash_tree_root(block)] = block - # Check block is a descendant of the finalized block + + # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + assert block.slot > finalized_slot + # Check block is a descendant of the finalized block assert get_ancestor(store, hash_tree_root(block), finalized_slot) == store.finalized_checkpoint.root + # Check the block is valid and compute the post-state state = state_transition(pre_state, signed_block, True) # Add new state for this block to the store From 00d3fb4fe6316d9d0a5914b71e04d40fb8d2dab2 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 20 Jan 2020 18:50:50 -0700 Subject: [PATCH 05/20] fix issue 1574 by ensuring that justified checkpoint in store updates when finalized updates if needbe --- specs/phase0/fork-choice.md | 9 ++++ .../test/fork_choice/test_on_block.py | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 39d9bd4026..8e5a9f492e 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -301,6 +301,15 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Update finalized checkpoint if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: store.finalized_checkpoint = state.finalized_checkpoint + finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + + # Update justified if new justified is later than store justified + # or if store justified is not in chain with finalized checkpoint + if ( + state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch + or get_ancestor(store, store.justified_checkpoint.root, finalized_slot) != store.finalized_checkpoint.root + ): + store.justified_checkpoint = state.current_justified_checkpoint ``` #### `on_attestation` diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py index f9995ad13f..b2e774b26e 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py @@ -256,3 +256,51 @@ def test_on_block_outside_safe_slots_and_multiple_better_justified(spec, state): assert store.justified_checkpoint == previously_justified # ensure the best from the series was stored assert store.best_justified_checkpoint == best_justified_checkpoint + + +@with_all_phases +@spec_state_test +def test_on_block_outside_safe_slots_but_finality(spec, state): + # Initialization + store = spec.get_genesis_store(state) + time = 100 + spec.on_tick(store, time) + + next_epoch(spec, state) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store) + next_epoch(spec, state) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + last_block_root = hash_tree_root(last_signed_block.message) + + # Mock justified block in store + just_block = build_empty_block_for_next_slot(spec, state) + # Slot is same as justified checkpoint so does not trigger an override in the store + just_block.slot = spec.compute_start_slot_at_epoch(store.justified_checkpoint.epoch) + store.blocks[just_block.hash_tree_root()] = just_block + + # Step time past safe slots + spec.on_tick(store, store.time + spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED * spec.SECONDS_PER_SLOT) + assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED + + # Mock justified and finalized update in state + just_fin_state = store.block_states[last_block_root] + new_justified = spec.Checkpoint( + epoch=store.justified_checkpoint.epoch + 1, + root=just_block.hash_tree_root(), + ) + new_finalized = spec.Checkpoint( + epoch=store.finalized_checkpoint.epoch + 1, + root=just_block.parent_root, + ) + just_fin_state.current_justified_checkpoint = new_justified + just_fin_state.finalized_checkpoint = new_finalized + + # Build and add block that includes the new justified/finalized info + block = build_empty_block_for_next_slot(spec, just_fin_state) + signed_block = state_transition_and_sign_block(spec, deepcopy(just_fin_state), block) + + run_on_block(spec, store, signed_block) + + assert store.finalized_checkpoint == new_finalized + assert store.justified_checkpoint == new_justified From 2015433fa1c7c95867d796eacf1a14ee5ce14b49 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 20 Jan 2020 20:03:38 -0700 Subject: [PATCH 06/20] revert exit queue epoch issue introduced in v0.10.0. add test to catch subtlety --- specs/phase0/beacon-chain.md | 2 +- .../test_process_voluntary_exit.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 503286e48f..dcc03fc473 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1036,7 +1036,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] - exit_queue_epoch = max(exit_epochs, default=compute_activation_exit_epoch(get_current_epoch(state))) + exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): exit_queue_epoch += Epoch(1) diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py index 461c38b737..19915750f6 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py @@ -46,6 +46,8 @@ def test_success(spec, state): yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit) + assert state.validators[validator_index].exit_epoch == spec.compute_activation_exit_epoch(current_epoch) + @with_all_phases @spec_state_test @@ -110,6 +112,28 @@ def test_success_exit_queue(spec, state): ) +@with_all_phases +@spec_state_test +def test_default_exit_epoch_subsequent_exit(spec, state): + # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = spec.get_active_validator_indices(state, current_epoch)[0] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] + + signed_voluntary_exit = sign_voluntary_exit( + spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey) + + # Exit one validator prior to this new one + exited_index = spec.get_active_validator_indices(state, current_epoch)[-1] + state.validators[exited_index].exit_epoch = current_epoch - 1 + + yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit) + + assert state.validators[validator_index].exit_epoch == spec.compute_activation_exit_epoch(current_epoch) + + @with_all_phases @spec_state_test def test_validator_exit_in_future(spec, state): From f012844493a83f21d8f9c0dbf7b73761fd1b1d19 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 21 Jan 2020 10:28:31 -0700 Subject: [PATCH 07/20] Add additional bls tests for IETF standards to bls gens --- tests/generators/bls/main.py | 127 +++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 7 deletions(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index e8503c2e5c..e6513e89f0 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -20,7 +20,7 @@ def hash(x): F2Q_COEFF_LEN = 48 G2_COMPRESSED_Z_LEN = 48 -DST = b'BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_' +DST = bls.G2ProofOfPossession.DST def int_to_hex(n: int, byte_length: int = None) -> str: @@ -124,7 +124,7 @@ def case03_private_to_public_key(): } -def case04_sign_messages(): +def case04_sign_message(): for privkey in PRIVKEYS: for message in MESSAGES: sig = bls.G2ProofOfPossession.Sign(privkey, message) @@ -138,8 +138,45 @@ def case04_sign_messages(): } -# TODO: case05_verify_messages: Verify messages signed in case04 -# It takes too long, empty for now +def case05_verify_message(): + for i, privkey in enumerate(PRIVKEYS): + for message in MESSAGES: + # Valid signature + signature = bls.G2ProofOfPossession.Sign(privkey, message) + pubkey = bls.G2Basic.PrivToPub(privkey) + full_name = f'{encode_hex(pubkey)}_{encode_hex(message)}_valid' + yield f'verify_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'pubkey': encode_hex(pubkey), + 'message': encode_hex(message), + 'signature': encode_hex(signature), + }, + 'output': True, + } + + # Invalid signatures -- wrong pubkey + wrong_pubkey = bls.G2Basic.PrivToPub(PRIVKEYS[(i + 1) % len(PRIVKEYS)]) + full_name = f'{encode_hex(wrong_pubkey)}_{encode_hex(message)}_wrong_pubkey' + yield f'verify_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'pubkey': encode_hex(wrong_pubkey), + 'message': encode_hex(message), + 'signature': encode_hex(signature), + }, + 'output': False, + } + + # Invalid signature -- tampered with signature + tampered_signature = signature[:-4] + b'\xFF\xFF\xFF\xFF' + full_name = f'{encode_hex(pubkey)}_{encode_hex(message)}_tampered_signature' + yield f'verify_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'pubkey': encode_hex(pubkey), + 'message': encode_hex(message), + 'signature': encode_hex(tampered_signature), + }, + 'output': False, + } def case06_aggregate_sigs(): @@ -160,8 +197,81 @@ def case07_aggregate_pubkeys(): } -# TODO -# Aggregate verify +def case08_fast_aggregate_verify(): + for i, message in enumerate(MESSAGES): + privkeys = PRIVKEYS[:i + 1] + sigs = [bls.G2ProofOfPossession.Sign(privkey, message) for privkey in privkeys] + aggregate_signature = bls.G2ProofOfPossession.Aggregate(sigs) + pubkeys = [bls.G2Basic.PrivToPub(privkey) for privkey in privkeys] + pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys] + + # Valid signature + full_name = f'{pubkeys_serial}_{encode_hex(message)}_valid' + yield f'fast_aggregate_verify_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'pubkeys': pubkeys_serial, + 'message': encode_hex(message), + 'signature': encode_hex(aggregate_signature), + }, + 'output': True, + } + + # Invalid signature -- extra pubkey + pubkeys_extra = pubkeys + [bls.G2Basic.PrivToPub(PRIVKEYS[-1])] + pubkeys_extra_serial = [encode_hex(pubkey) for pubkey in pubkeys] + full_name = f'{pubkeys_extra_serial}_{encode_hex(message)}_extra_pubkey' + yield f'fast_aggregate_verify_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'pubkeys': pubkeys_extra_serial, + 'message': encode_hex(message), + 'signature': encode_hex(aggregate_signature), + }, + 'output': False, + } + + # Invalid signature -- tampered with signature + tampered_signature = aggregate_signature[:-4] + b'\xff\xff\xff\xff' + full_name = f'{pubkeys_serial}_{encode_hex(message)}_tampered_signature' + yield f'fast_aggregate_verify_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + 'input': { + 'pubkeys': pubkeys_serial, + 'message': encode_hex(message), + 'signature': encode_hex(tampered_signature), + }, + 'output': False, + } + + +def case09_aggregate_verify(): + pairs = [] + sigs = [] + for privkey, message in zip(PRIVKEYS, MESSAGES): + sig = bls.G2ProofOfPossession.Sign(privkey, message) + pubkey = bls.G2Basic.PrivToPub(privkey) + pairs.append({ + 'pubkey': encode_hex(pubkey), + 'message': encode_hex(message), + }) + sigs.append(sig) + + aggregate_signature = bls.G2ProofOfPossession.Aggregate(sigs) + yield f'fast_aggregate_verify_valid', { + 'input': { + 'pairs': pairs, + 'signature': encode_hex(aggregate_signature), + }, + 'output': True, + } + + tampered_signature = aggregate_signature[:4] + b'\xff\xff\xff\xff' + yield f'fast_aggregate_verify_tampered_signature', { + 'input': { + 'pairs': pairs, + 'signature': encode_hex(tampered_signature), + }, + 'output': False, + } + # TODO # Proof-of-possession @@ -196,7 +306,10 @@ def cases_fn() -> Iterable[gen_typing.TestCase]: create_provider('msg_hash_uncompressed', case01_message_hash_G2_uncompressed), create_provider('msg_hash_compressed', case02_message_hash_G2_compressed), create_provider('priv_to_pub', case03_private_to_public_key), - create_provider('sign_msg', case04_sign_messages), + create_provider('sign_msg', case04_sign_message), + create_provider('verify_msg', case05_verify_message), create_provider('aggregate_sigs', case06_aggregate_sigs), create_provider('aggregate_pubkeys', case07_aggregate_pubkeys), + create_provider('fast_aggregate_verify', case08_fast_aggregate_verify), + create_provider('aggregate_verify', case09_aggregate_verify), ]) From 601701241c6c1e26143a9295161dfa0d7836f1f4 Mon Sep 17 00:00:00 2001 From: Kirk Baird Date: Wed, 22 Jan 2020 10:57:13 +1100 Subject: [PATCH 08/20] Update link Signed-off-by: Kirk Baird --- specs/phase0/validator.md | 2 +- tests/generators/bls/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 75b9f9311b..da68466893 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -97,7 +97,7 @@ A validator must initialize many parameters locally before submitting a deposit #### BLS public key -Validator public keys are [G1 points](../bls_signature.md#g1-points) on the [BLS12-381 curve](https://z.cash/blog/new-snark-curve). A private key, `privkey`, must be securely generated along with the resultant `pubkey`. This `privkey` must be "hot", that is, constantly available to sign data throughout the lifetime of the validator. +Validator public keys are [G1 points](beacon-chain.md#bls-signatures) on the [BLS12-381 curve](https://z.cash/blog/new-snark-curve). A private key, `privkey`, must be securely generated along with the resultant `pubkey`. This `privkey` must be "hot", that is, constantly available to sign data throughout the lifetime of the validator. #### BLS withdrawal key diff --git a/tests/generators/bls/README.md b/tests/generators/bls/README.md index 39261771b4..878bb156ba 100644 --- a/tests/generators/bls/README.md +++ b/tests/generators/bls/README.md @@ -9,7 +9,7 @@ The base unit is bytes48 of which only 381 bits are used ## Resources -- [Eth2 spec](../../specs/bls_signature.md) +- [Eth2 spec](../../../specs/phase0/beacon-chain.md#bls-signatures) - [Finite Field Arithmetic](http://www.springeronline.com/sgw/cda/pageitems/document/cda_downloaddocument/0,11996,0-0-45-110359-0,00.pdf) - Chapter 2 of [Elliptic Curve Cryptography](http://cacr.uwaterloo.ca/ecc/). Darrel Hankerson, Alfred Menezes, and Scott Vanstone - [Zcash BLS parameters](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381) From cf18b040b4fbac21b469a1000deb44f237287c57 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 22 Jan 2020 07:24:15 -0700 Subject: [PATCH 09/20] fix default value in compute_domain --- specs/phase0/beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 503286e48f..478a2daa12 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -788,10 +788,12 @@ def compute_activation_exit_epoch(epoch: Epoch) -> Epoch: #### `compute_domain` ```python -def compute_domain(domain_type: DomainType, fork_version: Version=GENESIS_FORK_VERSION) -> Domain: +def compute_domain(domain_type: DomainType, fork_version: Version=None) -> Domain: """ Return the domain for the ``domain_type`` and ``fork_version``. """ + if fork_version is None: + fork_version = GENESIS_FORK_VERSION return Domain(domain_type + fork_version) ``` From dd9325d9fa3750d50d9a52c3da8657799f09ff09 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 22 Jan 2020 11:32:25 -0700 Subject: [PATCH 10/20] G2Basic to G2ProofOfPossession for private_to_public_key bls test generator Co-Authored-By: Carl Beekhuizen --- tests/generators/bls/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index e6513e89f0..6959fa746d 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -115,7 +115,7 @@ def case02_message_hash_G2_compressed(): def case03_private_to_public_key(): - pubkeys = [bls.G2Basic.PrivToPub(privkey) for privkey in PRIVKEYS] + pubkeys = [bls. G2ProofOfPossession.PrivToPub(privkey) for privkey in PRIVKEYS] pubkeys_serial = ['0x' + pubkey.hex() for pubkey in pubkeys] for privkey, pubkey_serial in zip(PRIVKEYS, pubkeys_serial): yield f'priv_to_pub_{int_to_hex(privkey)}', { From e821476c07560c8810acc768bb30a7abd2d00e2b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 22 Jan 2020 11:27:45 -0700 Subject: [PATCH 11/20] explicitly use Optiona type for fork_version in compute_domain Co-Authored-By: Carl Beekhuizen --- scripts/build_spec.py | 2 +- specs/phase0/beacon-chain.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 114832bc2a..16dd0d21d1 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -31,7 +31,7 @@ SSZObject = TypeVar('SSZObject', bound=SSZType) ''' PHASE1_IMPORTS = '''from typing import ( - Any, Dict, Set, Sequence, MutableSequence, NewType, Tuple, Union, TypeVar + Any, Dict, Set, Sequence, MutableSequence, NewType, Optional, Tuple, Union, TypeVar ) from math import ( log2, diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 478a2daa12..7f2e873bfb 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -788,7 +788,7 @@ def compute_activation_exit_epoch(epoch: Epoch) -> Epoch: #### `compute_domain` ```python -def compute_domain(domain_type: DomainType, fork_version: Version=None) -> Domain: +def compute_domain(domain_type: DomainType, fork_version: Optional[Version]=None) -> Domain: """ Return the domain for the ``domain_type`` and ``fork_version``. """ From b357e43aab037e8a7f3fd5dced3046f2be765848 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 22 Jan 2020 14:31:23 -0700 Subject: [PATCH 12/20] clarifying comment on call to get_ancestor in on_block --- specs/phase0/fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 39d9bd4026..a69252a378 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -283,7 +283,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) assert block.slot > finalized_slot - # Check block is a descendant of the finalized block + # Check block is a descendant of the finalized block at the checkpoint finalized slot assert get_ancestor(store, hash_tree_root(block), finalized_slot) == store.finalized_checkpoint.root # Check the block is valid and compute the post-state From 3b80e12fc3ab3fbc22d00166bb803a9a40af530c Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 22 Jan 2020 17:56:50 -0700 Subject: [PATCH 13/20] only expose BLS tests for the used APIs --- tests/generators/bls/main.py | 77 +++++++----------------------------- 1 file changed, 15 insertions(+), 62 deletions(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index 6959fa746d..14a9f474dc 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -94,42 +94,12 @@ def hash_message_compressed(msg: bytes) -> Tuple[str, str]: return [int_to_hex(z1, G2_COMPRESSED_Z_LEN), int_to_hex(z2, G2_COMPRESSED_Z_LEN)] -def case01_message_hash_G2_uncompressed(): - for msg in MESSAGES: - yield f'uncom_g2_hash_{encode_hex(msg)}', { - 'input': { - 'message': encode_hex(msg), - }, - 'output': hash_message(msg) - } - - -def case02_message_hash_G2_compressed(): - for msg in MESSAGES: - yield f'com_g2_hash_{encode_hex(msg)}', { - 'input': { - 'message': encode_hex(msg), - }, - 'output': hash_message_compressed(msg) - } - - -def case03_private_to_public_key(): - pubkeys = [bls. G2ProofOfPossession.PrivToPub(privkey) for privkey in PRIVKEYS] - pubkeys_serial = ['0x' + pubkey.hex() for pubkey in pubkeys] - for privkey, pubkey_serial in zip(PRIVKEYS, pubkeys_serial): - yield f'priv_to_pub_{int_to_hex(privkey)}', { - 'input': int_to_hex(privkey), - 'output': pubkey_serial, - } - - -def case04_sign_message(): +def case01_sign(): for privkey in PRIVKEYS: for message in MESSAGES: sig = bls.G2ProofOfPossession.Sign(privkey, message) full_name = f'{int_to_hex(privkey)}_{encode_hex(message)}' - yield f'sign_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + yield f'sign_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { 'privkey': int_to_hex(privkey), 'message': encode_hex(message), @@ -138,14 +108,14 @@ def case04_sign_message(): } -def case05_verify_message(): +def case02_verify(): for i, privkey in enumerate(PRIVKEYS): for message in MESSAGES: # Valid signature signature = bls.G2ProofOfPossession.Sign(privkey, message) pubkey = bls.G2Basic.PrivToPub(privkey) full_name = f'{encode_hex(pubkey)}_{encode_hex(message)}_valid' - yield f'verify_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + yield f'verify_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { 'pubkey': encode_hex(pubkey), 'message': encode_hex(message), @@ -157,7 +127,7 @@ def case05_verify_message(): # Invalid signatures -- wrong pubkey wrong_pubkey = bls.G2Basic.PrivToPub(PRIVKEYS[(i + 1) % len(PRIVKEYS)]) full_name = f'{encode_hex(wrong_pubkey)}_{encode_hex(message)}_wrong_pubkey' - yield f'verify_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + yield f'verify_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { 'pubkey': encode_hex(wrong_pubkey), 'message': encode_hex(message), @@ -169,7 +139,7 @@ def case05_verify_message(): # Invalid signature -- tampered with signature tampered_signature = signature[:-4] + b'\xFF\xFF\xFF\xFF' full_name = f'{encode_hex(pubkey)}_{encode_hex(message)}_tampered_signature' - yield f'verify_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { + yield f'verify_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { 'pubkey': encode_hex(pubkey), 'message': encode_hex(message), @@ -179,25 +149,16 @@ def case05_verify_message(): } -def case06_aggregate_sigs(): +def case03_aggregate(): for message in MESSAGES: sigs = [bls.G2ProofOfPossession.Sign(privkey, message) for privkey in PRIVKEYS] - yield f'agg_sigs_{encode_hex(message)}', { + yield f'aggregate_{encode_hex(message)}', { 'input': [encode_hex(sig) for sig in sigs], 'output': encode_hex(bls.G2ProofOfPossession.Aggregate(sigs)), } -def case07_aggregate_pubkeys(): - pubkeys = [bls.G2Basic.PrivToPub(privkey) for privkey in PRIVKEYS] - pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys] - yield f'agg_pub_keys', { - 'input': pubkeys_serial, - 'output': encode_hex(bls.G2ProofOfPossession._AggregatePKs(pubkeys)), - } - - -def case08_fast_aggregate_verify(): +def case04_fast_aggregate_verify(): for i, message in enumerate(MESSAGES): privkeys = PRIVKEYS[:i + 1] sigs = [bls.G2ProofOfPossession.Sign(privkey, message) for privkey in privkeys] @@ -242,7 +203,7 @@ def case08_fast_aggregate_verify(): } -def case09_aggregate_verify(): +def case05_aggregate_verify(): pairs = [] sigs = [] for privkey, message in zip(PRIVKEYS, MESSAGES): @@ -273,10 +234,6 @@ def case09_aggregate_verify(): } -# TODO -# Proof-of-possession - - def create_provider(handler_name: str, test_case_fn: Callable[[], Iterable[Tuple[str, Dict[str, Any]]]]) -> gen_typing.TestProvider: @@ -303,13 +260,9 @@ def cases_fn() -> Iterable[gen_typing.TestCase]: if __name__ == "__main__": gen_runner.run_generator("bls", [ - create_provider('msg_hash_uncompressed', case01_message_hash_G2_uncompressed), - create_provider('msg_hash_compressed', case02_message_hash_G2_compressed), - create_provider('priv_to_pub', case03_private_to_public_key), - create_provider('sign_msg', case04_sign_message), - create_provider('verify_msg', case05_verify_message), - create_provider('aggregate_sigs', case06_aggregate_sigs), - create_provider('aggregate_pubkeys', case07_aggregate_pubkeys), - create_provider('fast_aggregate_verify', case08_fast_aggregate_verify), - create_provider('aggregate_verify', case09_aggregate_verify), + create_provider('sign', case01_sign), + create_provider('verify', case02_verify), + create_provider('aggregate', case03_aggregate), + create_provider('fast_aggregate_verify', case04_fast_aggregate_verify), + create_provider('aggregate_verify', case05_aggregate_verify), ]) From 14cfc66c33ce42208cf9c61565974d6f5ab85842 Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Mon, 13 Jan 2020 20:42:23 +0800 Subject: [PATCH 14/20] Fix p2p interface indentation --- specs/phase0/p2p-interface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index c04d2ae4c6..674f2e2b85 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -80,7 +80,7 @@ It consists of four main sections: - [How do we upgrade gossip channels (e.g. changes in encoding, compression)?](#how-do-we-upgrade-gossip-channels-eg-changes-in-encoding-compression) - [Why must all clients use the same gossip topic instead of one negotiated between each peer pair?](#why-must-all-clients-use-the-same-gossip-topic-instead-of-one-negotiated-between-each-peer-pair) - [Why are the topics strings and not hashes?](#why-are-the-topics-strings-and-not-hashes) - - [Why are we overriding the default libp2p pubsub `message-id`?](#why-are-we-overriding-the-default-libp2p-pubsub-message-id) + - [Why are we overriding the default libp2p pubsub `message-id`?](#why-are-we-overriding-the-default-libp2p-pubsub-message-id) - [Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets?](#why-is-there-maximum_gossip_clock_disparity-when-validating-slot-ranges-of-messages-in-gossip-subnets) - [Why are there `ATTESTATION_SUBNET_COUNT` attestation subnets?](#why-are-there-attestation_subnet_count-attestation-subnets) - [Why are attestations limited to be broadcast on gossip channels within `SLOTS_PER_EPOCH` slots?](#why-are-attestations-limited-to-be-broadcast-on-gossip-channels-within-slots_per_epoch-slots) @@ -759,7 +759,7 @@ No security or privacy guarantees are lost as a result of choosing plaintext top Furthermore, the Eth2 topic names are shorter than their digest equivalents (assuming SHA-256 hash), so hashing topics would bloat messages unnecessarily. -## Why are we overriding the default libp2p pubsub `message-id`? +### Why are we overriding the default libp2p pubsub `message-id`? For our current purposes, there is no need to address messages based on source peer, and it seems likely we might even override the message `from` to obfuscate the peer. By overriding the default `message-id` to use content-addressing we can filter unnecessary duplicates before hitting the application layer. From f8250b6a1abe8bf422fa66c5ef3a6f3275c6dbbb Mon Sep 17 00:00:00 2001 From: Ben Edgington Date: Mon, 13 Jan 2020 09:32:34 +0000 Subject: [PATCH 15/20] Correct typo in heading level --- specs/phase0/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 8a33461f19..ceee257a4c 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -76,7 +76,7 @@ - [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch) - [`compute_activation_exit_epoch`](#compute_activation_exit_epoch) - [`compute_domain`](#compute_domain) - - [`compute_signing_root`](#compute_signing_root) + - [`compute_signing_root`](#compute_signing_root) - [Beacon state accessors](#beacon-state-accessors) - [`get_current_epoch`](#get_current_epoch) - [`get_previous_epoch`](#get_previous_epoch) @@ -797,7 +797,7 @@ def compute_domain(domain_type: DomainType, fork_version: Optional[Version]=None return Domain(domain_type + fork_version) ``` -### `compute_signing_root` +#### `compute_signing_root` ```python def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root: From a13271af41f7cfcbc253cc81a9ba2b7e7d389b74 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 13 Jan 2020 12:49:03 +0800 Subject: [PATCH 16/20] Fix BLS API description --- specs/phase0/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index ceee257a4c..33dd1cbcf6 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -588,8 +588,8 @@ Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specificati - `def Sign(SK: int, message: Bytes) -> BLSSignature` - `def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool` - `def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature` -- `def FastAggregateVerify(PKs: Sequence[BLSSignature], message: Bytes, signature: BLSSignature) -> bool` -- `def AggregateVerify(pairs: Sequence[PK: BLSSignature, message: Bytes], signature: BLSSignature) -> bool` +- `def FastAggregateVerify(PKs: Sequence[BLSPubkey], message: Bytes, signature: BLSSignature) -> bool` +- `def AggregateVerify(pairs: Sequence[PK: BLSPubkey, message: Bytes], signature: BLSSignature) -> bool` Within these specifications, BLS signatures are treated as a module for notational clarity, thus to verify a signature `bls.Verify(...)` is used. From de1bb7bde149c641819d8f023093691fb2c8dc51 Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Wed, 22 Jan 2020 21:15:23 +0000 Subject: [PATCH 17/20] Update for renamed constant --- specs/phase0/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index da68466893..5e1e6b6100 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -128,7 +128,7 @@ To submit a deposit: ### Process deposit -Deposits cannot be processed into the beacon chain until the Eth1 block in which they were deposited or any of its descendants is added to the beacon chain `state.eth1_data`. This takes _a minimum_ of `ETH1_FOLLOW_DISTANCE` Eth1 blocks (~4 hours) plus `ETH1_DATA_VOTING_PERIOD` epochs (~1.7 hours). Once the requisite Eth1 data is added, the deposit will normally be added to a beacon chain block and processed into the `state.validators` within an epoch or two. The validator is then in a queue to be activated. +Deposits cannot be processed into the beacon chain until the Eth1 block in which they were deposited or any of its descendants is added to the beacon chain `state.eth1_data`. This takes _a minimum_ of `ETH1_FOLLOW_DISTANCE` Eth1 blocks (~4 hours) plus `SLOTS_PER_ETH1_VOTING_PERIOD` slots (~1.7 hours). Once the requisite Eth1 data is added, the deposit will normally be added to a beacon chain block and processed into the `state.validators` within an epoch or two. The validator is then in a queue to be activated. ### Validator index From 67763ae0e08803f95a337b7a9edcfb0376d94a3b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jan 2020 10:32:12 -0700 Subject: [PATCH 18/20] PR feedback --- tests/generators/bls/main.py | 39 +++++------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index 14a9f474dc..a1f5835beb 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -65,35 +65,6 @@ def hex_to_int(x: str) -> int: ] -def hash_message(msg: bytes) -> Tuple[Tuple[str, str], Tuple[str, str], Tuple[str, str]]: - """ - Hash message - Input: - - Message as bytes32 - Output: - - Message hash as a G2 point - """ - return [ - [ - int_to_hex(fq2.coeffs[0], F2Q_COEFF_LEN), - int_to_hex(fq2.coeffs[1], F2Q_COEFF_LEN), - ] - for fq2 in bls.hash_to_curve.hash_to_G2(msg, DST) - ] - - -def hash_message_compressed(msg: bytes) -> Tuple[str, str]: - """ - Hash message - Input: - - Message as bytes32 - Output: - - Message hash as a compressed G2 point - """ - z1, z2 = bls.point_compression.compress_G2(bls.hash_to_curve.hash_to_G2(msg, DST)) - return [int_to_hex(z1, G2_COMPRESSED_Z_LEN), int_to_hex(z2, G2_COMPRESSED_Z_LEN)] - - def case01_sign(): for privkey in PRIVKEYS: for message in MESSAGES: @@ -113,7 +84,7 @@ def case02_verify(): for message in MESSAGES: # Valid signature signature = bls.G2ProofOfPossession.Sign(privkey, message) - pubkey = bls.G2Basic.PrivToPub(privkey) + pubkey = bls.G2ProofOfPossession.PrivToPub(privkey) full_name = f'{encode_hex(pubkey)}_{encode_hex(message)}_valid' yield f'verify_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { @@ -125,7 +96,7 @@ def case02_verify(): } # Invalid signatures -- wrong pubkey - wrong_pubkey = bls.G2Basic.PrivToPub(PRIVKEYS[(i + 1) % len(PRIVKEYS)]) + wrong_pubkey = bls.G2ProofOfPossession.PrivToPub(PRIVKEYS[(i + 1) % len(PRIVKEYS)]) full_name = f'{encode_hex(wrong_pubkey)}_{encode_hex(message)}_wrong_pubkey' yield f'verify_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { @@ -163,7 +134,7 @@ def case04_fast_aggregate_verify(): privkeys = PRIVKEYS[:i + 1] sigs = [bls.G2ProofOfPossession.Sign(privkey, message) for privkey in privkeys] aggregate_signature = bls.G2ProofOfPossession.Aggregate(sigs) - pubkeys = [bls.G2Basic.PrivToPub(privkey) for privkey in privkeys] + pubkeys = [bls.G2ProofOfPossession.PrivToPub(privkey) for privkey in privkeys] pubkeys_serial = [encode_hex(pubkey) for pubkey in pubkeys] # Valid signature @@ -178,7 +149,7 @@ def case04_fast_aggregate_verify(): } # Invalid signature -- extra pubkey - pubkeys_extra = pubkeys + [bls.G2Basic.PrivToPub(PRIVKEYS[-1])] + pubkeys_extra = pubkeys + [bls.G2ProofOfPossession.PrivToPub(PRIVKEYS[-1])] pubkeys_extra_serial = [encode_hex(pubkey) for pubkey in pubkeys] full_name = f'{pubkeys_extra_serial}_{encode_hex(message)}_extra_pubkey' yield f'fast_aggregate_verify_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { @@ -208,7 +179,7 @@ def case05_aggregate_verify(): sigs = [] for privkey, message in zip(PRIVKEYS, MESSAGES): sig = bls.G2ProofOfPossession.Sign(privkey, message) - pubkey = bls.G2Basic.PrivToPub(privkey) + pubkey = bls.G2ProofOfPossession.PrivToPub(privkey) pairs.append({ 'pubkey': encode_hex(pubkey), 'message': encode_hex(message), From 161c0a8bc14f779d5df8c0c84ac1c9203249cb46 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 23 Jan 2020 11:35:59 -0700 Subject: [PATCH 19/20] remove unused code from bls generator file --- tests/generators/bls/main.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py index a1f5835beb..cb14ec9ec8 100644 --- a/tests/generators/bls/main.py +++ b/tests/generators/bls/main.py @@ -30,26 +30,10 @@ def int_to_hex(n: int, byte_length: int = None) -> str: return encode_hex(byte_value) -def int_to_bytes(n: int, byte_length: int = None) -> bytes: - byte_value = int_to_big_endian(n) - if byte_length: - byte_value = byte_value.rjust(byte_length, b'\x00') - return byte_value - - def hex_to_int(x: str) -> int: return int(x, 16) -DOMAINS = [ - b'\x00\x00\x00\x00\x00\x00\x00\x00', - b'\x00\x00\x00\x00\x00\x00\x00\x01', - b'\x01\x00\x00\x00\x00\x00\x00\x00', - b'\x80\x00\x00\x00\x00\x00\x00\x00', - b'\x01\x23\x45\x67\x89\xab\xcd\xef', - b'\xff\xff\xff\xff\xff\xff\xff\xff' -] - MESSAGES = [ bytes(b'\x00' * 32), bytes(b'\x56' * 32), From d4ae00819a6253f11d7564629e5ed1b516ede51e Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Thu, 23 Jan 2020 19:53:48 +0000 Subject: [PATCH 20/20] Fix for increased seconds per slot --- specs/phase0/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 5e1e6b6100..494035bdad 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -128,7 +128,7 @@ To submit a deposit: ### Process deposit -Deposits cannot be processed into the beacon chain until the Eth1 block in which they were deposited or any of its descendants is added to the beacon chain `state.eth1_data`. This takes _a minimum_ of `ETH1_FOLLOW_DISTANCE` Eth1 blocks (~4 hours) plus `SLOTS_PER_ETH1_VOTING_PERIOD` slots (~1.7 hours). Once the requisite Eth1 data is added, the deposit will normally be added to a beacon chain block and processed into the `state.validators` within an epoch or two. The validator is then in a queue to be activated. +Deposits cannot be processed into the beacon chain until the Eth1 block in which they were deposited or any of its descendants is added to the beacon chain `state.eth1_data`. This takes _a minimum_ of `ETH1_FOLLOW_DISTANCE` Eth1 blocks (~4 hours) plus `SLOTS_PER_ETH1_VOTING_PERIOD` slots (~3.4 hours). Once the requisite Eth1 data is added, the deposit will normally be added to a beacon chain block and processed into the `state.validators` within an epoch or two. The validator is then in a queue to be activated. ### Validator index