diff --git a/.circleci/config.yml b/.circleci/config.yml index 26b2597382..f3870d9227 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,26 +35,26 @@ commands: description: "Restore the cache with pyspec keys" steps: - restore_cached_venv: - venv_name: v4-pyspec + venv_name: v5-pyspec reqs_checksum: cache-{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "test_libs/pyspec/requirements-testing.txt" }} save_pyspec_cached_venv: description: Save a venv into a cache with pyspec keys" steps: - save_cached_venv: - venv_name: v4-pyspec + venv_name: v5-pyspec reqs_checksum: cache-{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "test_libs/pyspec/requirements-testing.txt" }} venv_path: ./test_libs/pyspec/venv restore_deposit_contract_cached_venv: description: "Restore the cache with deposit_contract keys" steps: - restore_cached_venv: - venv_name: v7-deposit-contract + venv_name: v8-deposit-contract reqs_checksum: cache-{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "deposit_contract/requirements-testing.txt" }} save_deposit_contract_cached_venv: description: Save a venv into a cache with deposit_contract keys" steps: - save_cached_venv: - venv_name: v7-deposit-contract + venv_name: v8-deposit-contract reqs_checksum: cache-{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "deposit_contract/requirements-testing.txt" }} venv_path: ./deposit_contract/venv jobs: diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 1a719cb29d..50f57960fb 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -11,7 +11,7 @@ PHASE0_IMPORTS = '''from typing import ( - Any, Dict, Set, Sequence, Tuple, Optional + Any, Dict, Set, Sequence, Tuple, Optional, TypeVar ) from dataclasses import ( @@ -21,20 +21,17 @@ from eth2spec.utils.ssz.ssz_impl import hash_tree_root from eth2spec.utils.ssz.ssz_typing import ( - boolean, Container, List, Vector, uint64, + boolean, Container, List, Vector, uint64, SSZType, Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) -from eth2spec.utils.bls import ( - bls_aggregate_signatures, - bls_aggregate_pubkeys, - bls_verify, - bls_sign, -) +from eth2spec.utils import bls from eth2spec.utils.hash_function import hash + +SSZObject = TypeVar('SSZObject', bound=SSZType) ''' PHASE1_IMPORTS = '''from typing import ( - Any, Dict, Set, Sequence, MutableSequence, NewType, Tuple, Union, + Any, Dict, Set, Sequence, MutableSequence, NewType, Tuple, Union, TypeVar ) from math import ( log2, @@ -55,18 +52,14 @@ Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, uint64, bit, boolean, byte, ) -from eth2spec.utils.bls import ( - bls_aggregate_pubkeys, - bls_verify, - bls_verify_multiple, - bls_signature_to_G2, -) +from eth2spec.utils import bls from eth2spec.utils.hash_function import hash SSZVariableName = str GeneralizedIndex = NewType('GeneralizedIndex', int) +SSZObject = TypeVar('SSZObject', bound=SSZType) ''' SUNDRY_CONSTANTS_FUNCTIONS = ''' def ceillog2(x: uint64) -> int: diff --git a/specs/bls_signature.md b/specs/bls_signature.md deleted file mode 100644 index aafeeb54d4..0000000000 --- a/specs/bls_signature.md +++ /dev/null @@ -1,148 +0,0 @@ -# BLS signature verification - -**Notice**: This document is a placeholder to facilitate the emergence of cross-client testnets. Substantive changes are postponed until [BLS standardisation](https://github.com/cfrg/draft-irtf-cfrg-bls-signature) is finalized. - -**Warning**: The constructions in this document should not be considered secure. In particular, the `hash_to_G2` function is known to be unsecure. - -## Table of contents - - - - - -- [Curve parameters](#curve-parameters) -- [Point representations](#point-representations) - - [G1 points](#g1-points) - - [G2 points](#g2-points) -- [Helpers](#helpers) - - [`hash_to_G2`](#hash_to_g2) - - [`modular_squareroot`](#modular_squareroot) -- [Aggregation operations](#aggregation-operations) - - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - - [`bls_aggregate_signatures`](#bls_aggregate_signatures) -- [Signature verification](#signature-verification) - - [`bls_verify`](#bls_verify) - - [`bls_verify_multiple`](#bls_verify_multiple) - - - - -## Curve parameters - -The BLS12-381 curve parameters are defined [here](https://z.cash/blog/new-snark-curve). - -## Point representations - -We represent points in the groups G1 and G2 following [zkcrypto/pairing](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381). We denote by `q` the field modulus and by `i` the imaginary unit. - -### G1 points - -A point in G1 is represented as a 384-bit integer `z` decomposed as a 381-bit integer `x` and three 1-bit flags in the top bits: - -* `x = z % 2**381` -* `a_flag = (z % 2**382) // 2**381` -* `b_flag = (z % 2**383) // 2**382` -* `c_flag = (z % 2**384) // 2**383` - -Respecting bit ordering, `z` is decomposed as `(c_flag, b_flag, a_flag, x)`. - -We require: - -* `x < q` -* `c_flag == 1` -* if `b_flag == 1` then `a_flag == x == 0` and `z` represents the point at infinity -* if `b_flag == 0` then `z` represents the point `(x, y)` where `y` is the valid coordinate such that `(y * 2) // q == a_flag` - -### G2 points - -A point in G2 is represented as a pair of 384-bit integers `(z1, z2)`. We decompose `z1` as above into `x1`, `a_flag1`, `b_flag1`, `c_flag1` and `z2` into `x2`, `a_flag2`, `b_flag2`, `c_flag2`. - -We require: - -* `x1 < q` and `x2 < q` -* `a_flag2 == b_flag2 == c_flag2 == 0` -* `c_flag1 == 1` -* if `b_flag1 == 1` then `a_flag1 == x1 == x2 == 0` and `(z1, z2)` represents the point at infinity -* if `b_flag1 == 0` then `(z1, z2)` represents the point `(x1 * i + x2, y)` where `y` is the valid coordinate such that the imaginary part `y_im` of `y` satisfies `(y_im * 2) // q == a_flag1` - -## Helpers - -### `hash_to_G2` - -```python -G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 -q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 - -def hash_to_G2(message_hash: Bytes32, domain: Bytes8) -> Tuple[uint384, uint384]: - # Initial candidate x coordinate - x_re = int.from_bytes(hash(message_hash + domain + b'\x01'), 'big') - x_im = int.from_bytes(hash(message_hash + domain + b'\x02'), 'big') - x_coordinate = Fq2([x_re, x_im]) # x = x_re + i * x_im - - # Test candidate y coordinates until a one is found - while 1: - y_coordinate_squared = x_coordinate ** 3 + Fq2([4, 4]) # The curve is y^2 = x^3 + 4(i + 1) - y_coordinate = modular_squareroot(y_coordinate_squared) - if y_coordinate is not None: # Check if quadratic residue found - return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor) - x_coordinate += Fq2([1, 0]) # Add 1 and try again -``` - -### `modular_squareroot` - -`modular_squareroot(x)` returns a solution `y` to `y**2 % q == x`, and `None` if none exists. If there are two solutions, the one with higher imaginary component is favored; if both solutions have equal imaginary component, the one with higher real component is favored (note that this is equivalent to saying that the single solution with either imaginary component > p/2 or imaginary component zero and real component > p/2 is favored). - -The following is a sample implementation; implementers are free to implement modular square roots as they wish. Note that `x2 = -x1` is an _additive modular inverse_ so real and imaginary coefficients remain in `[0 .. q-1]`. `coerce_to_int(element: Fq) -> int` is a function that takes Fq element `element` (i.e. integers `mod q`) and converts it to a regular integer. - -```python -Fq2_order = q ** 2 - 1 -eighth_roots_of_unity = [Fq2([1,1]) ** ((Fq2_order * k) // 8) for k in range(8)] - -def modular_squareroot(value: Fq2) -> Fq2: - candidate_squareroot = value ** ((Fq2_order + 8) // 16) - check = candidate_squareroot ** 2 / value - if check in eighth_roots_of_unity[::2]: - x1 = candidate_squareroot / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2] - x2 = -x1 - x1_re, x1_im = coerce_to_int(x1.coeffs[0]), coerce_to_int(x1.coeffs[1]) - x2_re, x2_im = coerce_to_int(x2.coeffs[0]), coerce_to_int(x2.coeffs[1]) - return x1 if (x1_im > x2_im or (x1_im == x2_im and x1_re > x2_re)) else x2 - return None -``` - -## Aggregation operations - -### `bls_aggregate_pubkeys` - -Let `bls_aggregate_pubkeys(pubkeys: List[Bytes48]) -> Bytes48` return `pubkeys[0] + .... + pubkeys[len(pubkeys)-1]`, where `+` is the elliptic curve addition operation over the G1 curve. (When `len(pubkeys) == 0` the empty sum is the G1 point at infinity.) - -### `bls_aggregate_signatures` - -Let `bls_aggregate_signatures(signatures: List[Bytes96]) -> Bytes96` return `signatures[0] + .... + signatures[len(signatures)-1]`, where `+` is the elliptic curve addition operation over the G2 curve. (When `len(signatures) == 0` the empty sum is the G2 point at infinity.) - -## Signature verification - -In the following, `e` is the pairing function and `g` is the G1 generator with the following coordinates (see [here](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381#g1)): - -```python -g_x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 -g_y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 -g = Fq2([g_x, g_y]) -``` - -### `bls_verify` - -Let `bls_verify(pubkey: Bytes48, message_hash: Bytes32, signature: Bytes96, domain: Bytes8) -> bool`: - -* Verify that `pubkey` is a valid G1 point. -* Verify that `signature` is a valid G2 point. -* Verify that `e(pubkey, hash_to_G2(message_hash, domain)) == e(g, signature)`. - -### `bls_verify_multiple` - -Let `bls_verify_multiple(pubkeys: List[Bytes48], message_hashes: List[Bytes32], signature: Bytes96, domain: Bytes8) -> bool`: - -* Verify that each `pubkey` in `pubkeys` is a valid G1 point. -* Verify that `signature` is a valid G2 point. -* Verify that `len(pubkeys)` equals `len(message_hashes)` and denote the length `L`. -* Verify that `e(pubkeys[0], hash_to_G2(message_hashes[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(message_hashes[L-1], domain)) == e(g, signature)`. diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c0d43601e7..36a65b2430 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -34,6 +34,7 @@ - [`DepositMessage`](#depositmessage) - [`DepositData`](#depositdata) - [`BeaconBlockHeader`](#beaconblockheader) + - [`SigningRoot`](#signingroot) - [Beacon operations](#beacon-operations) - [`ProposerSlashing`](#proposerslashing) - [`AttesterSlashing`](#attesterslashing) @@ -58,8 +59,7 @@ - [Crypto](#crypto) - [`hash`](#hash) - [`hash_tree_root`](#hash_tree_root) - - [`bls_verify`](#bls_verify) - - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) + - [BLS Signatures](#bls-signatures) - [Predicates](#predicates) - [`is_active_validator`](#is_active_validator) - [`is_eligible_for_activation_queue`](#is_eligible_for_activation_queue) @@ -76,6 +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) - [Beacon state accessors](#beacon-state-accessors) - [`get_current_epoch`](#get_current_epoch) - [`get_previous_epoch`](#get_previous_epoch) @@ -378,6 +379,14 @@ class BeaconBlockHeader(Container): body_root: Root ``` +#### `SigningRoot` + +```python +class SigningRoot(Container): + object_root: Root + domain: Domain +``` + ### Beacon operations #### `ProposerSlashing` @@ -574,13 +583,17 @@ def bytes_to_int(data: bytes) -> uint64: `def hash_tree_root(object: SSZSerializable) -> Root` is a function for hashing objects into a single root by utilizing a hash tree structure, as defined in the [SSZ spec](../simple-serialize.md#merkleization). -#### `bls_verify` +#### BLS Signatures -`bls_verify` is a function for verifying a BLS signature, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify). +Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specification](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00). Specifically, eth2 uses the `BLS_SIG_BLS12381G2-SHA256-SSWU-RO-_POP_` ciphersuite which implements the following interfaces: -#### `bls_aggregate_pubkeys` +- `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` -`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, as defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys). +Within these specifications, BLS signatures are treated as a module for notational clarity, thus to verify a signature `bls.Verify(...)` is used. ### Predicates @@ -663,14 +676,10 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe if not indices == sorted(set(indices)): return False # Verify aggregate signature - if not bls_verify( - pubkey=bls_aggregate_pubkeys([state.validators[i].pubkey for i in indices]), - message_hash=hash_tree_root(indexed_attestation.data), - signature=indexed_attestation.signature, - domain=get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch), - ): - return False - return True + pubkeys = [state.validators[i].pubkey for i in indices] + domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch) + signing_root = compute_signing_root(indexed_attestation.data, domain) + return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature) ``` #### `is_valid_merkle_branch` @@ -788,6 +797,20 @@ def compute_domain(domain_type: DomainType, fork_version: Version=GENESIS_FORK_V return Domain(domain_type + fork_version) ``` +### `compute_signing_root` + +```python +def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root: + """ + Return the signing root of an object by calculating the root of the object-domain tree. + """ + domain_wrapped_object = SigningRoot( + object_root=hash_tree_root(ssz_object), + domain=domain, + ) + return hash_tree_root(domain_wrapped_object) +``` + ### Beacon state accessors #### `get_current_epoch` @@ -941,11 +964,11 @@ def get_total_active_balance(state: BeaconState) -> Gwei: #### `get_domain` ```python -def get_domain(state: BeaconState, domain_type: DomainType, message_epoch: Epoch=None) -> Domain: +def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: """ Return the signature domain (fork version concatenated with domain type) of a message. """ - epoch = get_current_epoch(state) if message_epoch is None else message_epoch + epoch = get_current_epoch(state) if epoch is None else epoch fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version return compute_domain(domain_type, fork_version) ``` @@ -1136,8 +1159,8 @@ def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, valida ```python def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool: proposer = state.validators[get_beacon_proposer_index(state)] - domain = get_domain(state, DOMAIN_BEACON_PROPOSER) - return bls_verify(proposer.pubkey, hash_tree_root(signed_block.message), signed_block.signature, domain) + signing_root = compute_signing_root(signed_block.message, get_domain(state, DOMAIN_BEACON_PROPOSER)) + return bls.Verify(proposer.pubkey, signing_root, signed_block.signature) ``` ```python @@ -1436,7 +1459,8 @@ def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: epoch = get_current_epoch(state) # Verify RANDAO reveal proposer = state.validators[get_beacon_proposer_index(state)] - assert bls_verify(proposer.pubkey, hash_tree_root(epoch), body.randao_reveal, get_domain(state, DOMAIN_RANDAO)) + signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO)) + assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal) # Mix in RANDAO reveal mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal)) state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix @@ -1484,7 +1508,8 @@ def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSla # Signatures are valid for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2): domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot)) - assert bls_verify(proposer.pubkey, hash_tree_root(signed_header.message), signed_header.signature, domain) + signing_root = compute_signing_root(signed_header.message, domain) + assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature) slash_validator(state, proposer_slashing.proposer_index) ``` @@ -1562,12 +1587,12 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: # Verify the deposit signature (proof of possession) for new validators. # Note: The deposit contract does not check signatures. # Note: Deposits are valid across forks, thus the deposit domain is retrieved directly from `compute_domain`. - domain = compute_domain(DOMAIN_DEPOSIT) deposit_message = DepositMessage( pubkey=deposit.data.pubkey, withdrawal_credentials=deposit.data.withdrawal_credentials, amount=deposit.data.amount) - if not bls_verify(pubkey, hash_tree_root(deposit_message), deposit.data.signature, domain): + signing_root = compute_signing_root(deposit_message, compute_domain(DOMAIN_DEPOSIT)) + if not bls.Verify(pubkey, signing_root, deposit.data.signature): return # Add validator and balance entries @@ -1603,7 +1628,8 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu assert get_current_epoch(state) >= validator.activation_epoch + PERSISTENT_COMMITTEE_PERIOD # Verify signature domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) - assert bls_verify(validator.pubkey, hash_tree_root(voluntary_exit), signed_voluntary_exit.signature, domain) + signing_root = compute_signing_root(voluntary_exit, domain) + assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature) # Initiate exit initiate_validator_exit(state, voluntary_exit.validator_index) ``` diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index b1f61de2fb..fdaf9af423 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -353,7 +353,7 @@ def custody_subchunkify(bytez: bytes) -> Sequence[bytes]: ```python def get_custody_chunk_bit(key: BLSSignature, chunk: bytes) -> bool: - full_G2_element = bls_signature_to_G2(key) + 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))] @@ -429,16 +429,9 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> assert is_slashable_validator(revealer, get_current_epoch(state)) # Verify signature - assert bls_verify( - pubkey=revealer.pubkey, - message_hash=hash_tree_root(epoch_to_sign), - signature=reveal.reveal, - domain=get_domain( - state=state, - domain_type=DOMAIN_RANDAO, - message_epoch=epoch_to_sign, - ), - ) + domain = get_domain(state, DOMAIN_RANDAO, epoch_to_sign) + signing_root = compute_signing_root(epoch_to_sign, domain) + assert bls.Verify(revealer.pubkey, signing_root, reveal.reveal) # Decrement max reveal lateness if response is timely if epoch_to_sign + EPOCHS_PER_CUSTODY_PERIOD >= get_current_epoch(state): @@ -487,21 +480,10 @@ def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerived # Verify signature correctness masker = state.validators[reveal.masker_index] pubkeys = [revealed_validator.pubkey, masker.pubkey] - message_hashes = [ - hash_tree_root(reveal.epoch), - reveal.mask, - ] - - assert bls_verify_multiple( - pubkeys=pubkeys, - message_hashes=message_hashes, - signature=reveal.reveal, - domain=get_domain( - state=state, - domain_type=DOMAIN_RANDAO, - message_epoch=reveal.epoch, - ), - ) + + domain = get_domain(state, DOMAIN_RANDAO, reveal.epoch) + signing_roots = [compute_signing_root(root, domain) for root in [hash_tree_root(reveal.epoch), reveal.mask]] + assert bls.AggregateVerify(zip(pubkeys, signing_roots), reveal.reveal) if reveal.epoch >= get_current_epoch(state) + CUSTODY_PERIOD_TO_RANDAO_PADDING: # Full slashing when the secret was revealed so early it may be a valid custody @@ -598,7 +580,7 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> challenger = state.validators[challenge.challenger_index] domain = get_domain(state, DOMAIN_CUSTODY_BIT_CHALLENGE, get_current_epoch(state)) # TODO incorrect hash-tree-root, but this changes with phase 1 PR #1483 - assert bls_verify(challenger.pubkey, hash_tree_root(challenge), challenge.signature, domain) + assert bls.Verify(challenger.pubkey, compute_signing_root(challenge, domain), challenge.signature) # Verify challenger is slashable assert is_slashable_validator(challenger, get_current_epoch(state)) # Verify attestation @@ -622,7 +604,7 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> 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) + assert bls.Verify(responder.pubkey, compute_signing_root(epoch_to_sign, domain), challenge.responder_key) # Verify the chunk count chunk_count = get_custody_chunk_count(attestation.data.crosslink) assert chunk_count == len(challenge.chunk_bits) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 93570dbeea..e04e3ba5e4 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -386,7 +386,7 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat assert not proposer.slashed # Verify proposer signature domain = get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(block.slot)) - assert bls_verify(proposer.pubkey, hash_tree_root(block), block.signature, domain) + assert bls.Verify(proposer.pubkey, compute_signing_root(block, domain), block.signature) ``` #### Attestations @@ -406,8 +406,9 @@ def process_shard_attestations(beacon_state: BeaconState, shard_state: ShardStat assert block.aggregation_bits[i] == 0b0 # Verify attester aggregate signature domain = get_domain(beacon_state, DOMAIN_SHARD_ATTESTER, compute_epoch_of_shard_slot(block.slot)) - message = hash_tree_root(ShardAttestationData(slot=shard_state.slot, parent_root=block.parent_root)) - assert bls_verify(bls_aggregate_pubkeys(pubkeys), message, block.attestations, domain) + shard_attestation_data = ShardAttestationData(slot=shard_state.slot, parent_root=block.parent_root) + signing_root = compute_signing_root(shard_attestation_data, domain) + assert bls.FastAggregateVerify(pubkeys, signing_root, block.attestations) # Proposer micro-reward proposer_index = get_shard_proposer_index(beacon_state, shard_state.shard, block.slot) reward = attestation_count * get_base_reward(beacon_state, proposer_index) // PROPOSER_REWARD_QUOTIENT diff --git a/specs/light_client/sync_protocol.md b/specs/light_client/sync_protocol.md index 05180516bf..5a74101800 100644 --- a/specs/light_client/sync_protocol.md +++ b/specs/light_client/sync_protocol.md @@ -135,9 +135,10 @@ def update_memory(memory: LightClientMemory, update: LightClientUpdate) -> None: assert 3 * sum(filter(lambda i: update.aggregation_bits[i], balances)) > 2 * sum(balances) # Verify shard attestations - pubkey = bls_aggregate_pubkeys(filter(lambda i: update.aggregation_bits[i], pubkeys)) + pubkeys = filter(lambda i: update.aggregation_bits[i], pubkeys) domain = compute_domain(DOMAIN_SHARD_ATTESTER, update.fork_version) - assert bls_verify(pubkey, update.shard_block_root, update.signature, domain) + signing_root = compute_signing_root(update.shard_block_root, domain) + assert bls.FastAggregateVerify(pubkeys, signing_root, update.signature) # Update period committees if entering a new period if next_period == current_period + 1: diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 5876cbb7eb..55a061446a 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -120,7 +120,7 @@ To submit a deposit: - Set `deposit_data.withdrawal_credentials` to `withdrawal_credentials`. - Set `deposit_data.amount` to `amount`. - Let `deposit_message` be a `DepositMessage` with all the `DepositData` contents except the `signature`. -- Let `signature` be the result of `bls_sign` of the `hash_tree_root(deposit_message)` with `domain=compute_domain(DOMAIN_DEPOSIT)`. (_Warning_: Deposits _must_ be signed with `GENESIS_FORK_VERSION`, calling `compute_domain` without a second argument defaults to the correct version). +- Let `signature` be the result of `bls.Sign` of the `compute_signing_root(deposit_message, domain)` with `domain=compute_domain(DOMAIN_DEPOSIT)`. (_Warning_: Deposits _must_ be signed with `GENESIS_FORK_VERSION`, calling `compute_domain` without a second argument defaults to the correct version). - Let `deposit_data_root` be `hash_tree_root(deposit_data)`. - Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96], deposit_data_root: bytes32)` along with a deposit of `amount` Gwei. @@ -237,7 +237,8 @@ Set `block.body.randao_reveal = epoch_signature` where `epoch_signature` is obta ```python def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_RANDAO, compute_epoch_at_slot(block.slot)) - return bls_sign(privkey, hash_tree_root(compute_epoch_at_slot(block.slot)), domain) + signing_root = compute_signing_root(compute_epoch_at_slot(block.slot), domain) + return bls.Sign(privkey, signing_root) ``` ##### Eth1 Data @@ -341,7 +342,8 @@ def compute_new_state_root(state: BeaconState, block: BeaconBlock) -> Root: ```python def get_block_signature(state: BeaconState, header: BeaconBlockHeader, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot)) - return bls_sign(privkey, hash_tree_root(header), domain) + signing_root = compute_signing_root(header, domain) + return bls.Sign(privkey, signing_root) ``` ### Attesting @@ -399,7 +401,8 @@ Set `attestation.signature = signed_attestation_data` where `signed_attestation_ ```python def get_signed_attestation_data(state: BeaconState, attestation: IndexedAttestation, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch) - return bls_sign(privkey, hash_tree_root(attestation.data), domain) + signing_root = compute_signing_root(attestation.data, domain) + return bls.Sign(privkey, signing_root) ``` #### Broadcast attestation @@ -417,7 +420,8 @@ A validator is selected to aggregate based upon the return value of `is_aggregat ```python def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_BEACON_ATTESTER, compute_epoch_at_slot(slot)) - return bls_sign(privkey, hash_tree_root(slot), domain) + signing_root = compute_signing_root(slot, domain) + return bls.Sign(privkey, signing_root) ``` ```python @@ -448,7 +452,7 @@ Set `aggregate_attestation.signature = aggregate_signature` where `aggregate_sig ```python def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature: signatures = [attestation.signature for attestation in attestations] - return bls_aggregate_signatures(signatures) + return bls.Aggregate(signatures) ``` #### Broadcast aggregate diff --git a/test_generators/bls/requirements.txt b/test_generators/bls/requirements.txt index 84a28d357f..8a72affe01 100644 --- a/test_generators/bls/requirements.txt +++ b/test_generators/bls/requirements.txt @@ -1,3 +1,3 @@ -py_ecc==1.7.1 +py_ecc==2.0.0 eth-utils==1.6.0 ../../test_libs/gen_helpers diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index 3ed54888f4..cb3e863203 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -3,7 +3,7 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot, transition_unsigned_block, \ build_empty_block from eth2spec.test.helpers.keys import privkeys -from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures +from eth2spec.utils import bls from eth2spec.utils.ssz.ssz_typing import Bitlist @@ -77,8 +77,7 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List privkey ) ) - - return bls_aggregate_signatures(signatures) + return bls.Aggregate(signatures) def sign_indexed_attestation(spec, state, indexed_attestation): @@ -97,15 +96,9 @@ def sign_attestation(spec, state, attestation): def get_attestation_signature(spec, state, attestation_data, privkey): - return bls_sign( - message_hash=attestation_data.hash_tree_root(), - privkey=privkey, - domain=spec.get_domain( - state=state, - domain_type=spec.DOMAIN_BEACON_ATTESTER, - message_epoch=attestation_data.target.epoch, - ) - ) + domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch) + signing_root = spec.compute_signing_root(attestation_data, domain) + return bls.Sign(privkey, signing_root) def fill_aggregate_attestation(spec, state, attestation, signed=False): diff --git a/test_libs/pyspec/eth2spec/test/helpers/block.py b/test_libs/pyspec/eth2spec/test/helpers/block.py index b8c514eb4f..dda03cbf13 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/block.py @@ -1,7 +1,8 @@ from copy import deepcopy from eth2spec.test.helpers.keys import privkeys -from eth2spec.utils.bls import bls_sign, only_with_bls +from eth2spec.utils import bls +from eth2spec.utils.bls import only_with_bls from eth2spec.utils.ssz.ssz_impl import hash_tree_root @@ -28,15 +29,9 @@ def apply_randao_reveal(spec, state, block, proposer_index=None): proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index) privkey = privkeys[proposer_index] - block.body.randao_reveal = bls_sign( - privkey=privkey, - message_hash=hash_tree_root(spec.compute_epoch_at_slot(block.slot)), - domain=spec.get_domain( - state, - message_epoch=spec.compute_epoch_at_slot(block.slot), - domain_type=spec.DOMAIN_RANDAO, - ) - ) + domain = spec.get_domain(state, spec.DOMAIN_RANDAO, spec.compute_epoch_at_slot(block.slot)) + signing_root = spec.compute_signing_root(spec.compute_epoch_at_slot(block.slot), domain) + block.body.randao_reveal = bls.Sign(privkey, signing_root) # Fully ignore the function if BLS is off, beacon-proposer index calculation is slow. @@ -46,14 +41,10 @@ def apply_sig(spec, state, signed_block, proposer_index=None): proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index) privkey = privkeys[proposer_index] + domain = spec.get_domain(state, spec.DOMAIN_BEACON_PROPOSER, spec.compute_epoch_at_slot(block.slot)) + signing_root = spec.compute_signing_root(block, domain) - signed_block.signature = bls_sign( - message_hash=hash_tree_root(block), - privkey=privkey, - domain=spec.get_domain( - state, - spec.DOMAIN_BEACON_PROPOSER, - spec.compute_epoch_at_slot(block.slot))) + signed_block.signature = bls.Sign(privkey, signing_root) def sign_block(spec, state, block, proposer_index=None): diff --git a/test_libs/pyspec/eth2spec/test/helpers/block_header.py b/test_libs/pyspec/eth2spec/test/helpers/block_header.py index a9c8145ae0..c1bc746ccc 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/block_header.py +++ b/test_libs/pyspec/eth2spec/test/helpers/block_header.py @@ -1,5 +1,4 @@ -from eth2spec.utils.bls import bls_sign -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils import bls def sign_block_header(spec, state, header, privkey): @@ -7,8 +6,6 @@ def sign_block_header(spec, state, header, privkey): state=state, domain_type=spec.DOMAIN_BEACON_PROPOSER, ) - return spec.SignedBeaconBlockHeader(message=header, signature=bls_sign( - message_hash=hash_tree_root(header), - privkey=privkey, - domain=domain, - )) + signing_root = spec.compute_signing_root(header, domain) + signature = bls.Sign(privkey, signing_root) + return spec.SignedBeaconBlockHeader(message=header, signature=signature) diff --git a/test_libs/pyspec/eth2spec/test/helpers/custody.py b/test_libs/pyspec/eth2spec/test/helpers/custody.py index f6ca8ecd9a..e00d64a172 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/custody.py +++ b/test_libs/pyspec/eth2spec/test/helpers/custody.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.keys import privkeys -from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures +from eth2spec.utils import bls from eth2spec.utils.hash_function import hash from eth2spec.utils.ssz.ssz_typing import Bitlist, ByteVector, Bitvector from eth2spec.utils.ssz.ssz_impl import chunkify, pack, hash_tree_root @@ -17,28 +17,15 @@ def get_valid_early_derived_secret_reveal(spec, state, epoch=None): epoch = current_epoch + spec.CUSTODY_PERIOD_TO_RANDAO_PADDING # Generate the secret that is being revealed - reveal = bls_sign( - message_hash=hash_tree_root(spec.Epoch(epoch)), - privkey=privkeys[revealed_index], - domain=spec.get_domain( - state=state, - domain_type=spec.DOMAIN_RANDAO, - message_epoch=epoch, - ), - ) + domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch) + signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain) + reveal = bls.Sign(privkeys[revealed_index], signing_root) # Generate the mask (any random 32 bytes that don't reveal the masker's secret will do) mask = hash(reveal) # Generate masker's signature on the mask - masker_signature = bls_sign( - message_hash=mask, - privkey=privkeys[masker_index], - domain=spec.get_domain( - state=state, - domain_type=spec.DOMAIN_RANDAO, - message_epoch=epoch, - ), - ) - masked_reveal = bls_aggregate_signatures([reveal, masker_signature]) + signing_root = spec.compute_signing_root(mask, domain) + masker_signature = bls.Sign(privkeys[masker_index], signing_root) + masked_reveal = bls.Aggregate([reveal, masker_signature]) return spec.EarlyDerivedSecretReveal( revealed_index=revealed_index, @@ -60,15 +47,9 @@ def get_valid_custody_key_reveal(spec, state, period=None): 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, - ), - ) + domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign) + signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain) + reveal = bls.Sign(privkeys[revealer_index], signing_root) return spec.CustodyKeyReveal( revealer_index=revealer_index, reveal=reveal, @@ -92,15 +73,9 @@ def get_valid_bit_challenge(spec, state, attestation, invalid_custody_bit=False) 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, - ), - ) + domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch) + signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain) + responder_key = bls.Sign(privkeys[responder_index], signing_root) chunk_count = spec.get_custody_chunk_count(attestation.data.crosslink) diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index 071e177fd6..a16f7a7bf8 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.keys import pubkeys, privkeys -from eth2spec.utils.bls import bls_sign +from eth2spec.utils import bls from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof from eth2spec.utils.ssz.ssz_impl import hash_tree_root from eth2spec.utils.ssz.ssz_typing import List @@ -21,12 +21,9 @@ def sign_deposit_data(spec, deposit_data, privkey): pubkey=deposit_data.pubkey, withdrawal_credentials=deposit_data.withdrawal_credentials, amount=deposit_data.amount) - signature = bls_sign( - message_hash=hash_tree_root(deposit_message), - privkey=privkey, - domain=spec.compute_domain(spec.DOMAIN_DEPOSIT), - ) - deposit_data.signature = signature + domain = spec.compute_domain(spec.DOMAIN_DEPOSIT) + signing_root = spec.compute_signing_root(deposit_message, domain) + deposit_data.signature = bls.Sign(privkey, signing_root) def build_deposit(spec, diff --git a/test_libs/pyspec/eth2spec/test/helpers/keys.py b/test_libs/pyspec/eth2spec/test/helpers/keys.py index f47cd7c10b..23bb95131f 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/keys.py +++ b/test_libs/pyspec/eth2spec/test/helpers/keys.py @@ -1,6 +1,6 @@ -from py_ecc import bls +from py_ecc.bls import G2ProofOfPossession as bls from eth2spec.phase0 import spec privkeys = [i + 1 for i in range(spec.SLOTS_PER_EPOCH * 16)] -pubkeys = [bls.privtopub(privkey) for privkey in privkeys] +pubkeys = [bls.PrivToPub(privkey) for privkey in privkeys] pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)} diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py index 4f0a9fb0aa..622183fe92 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py @@ -1,8 +1,5 @@ from eth2spec.test.helpers.keys import privkeys -from eth2spec.utils.bls import ( - bls_aggregate_signatures, - bls_sign, -) +from eth2spec.utils import bls def sign_shard_attestation(spec, beacon_state, shard_state, block, participants): @@ -24,17 +21,10 @@ def sign_shard_attestation(spec, beacon_state, shard_state, block, participants) privkey, ) ) - - return bls_aggregate_signatures(signatures) + return bls.Aggregate(signatures) def get_attestation_signature(spec, beacon_state, shard_state, message_hash, block_epoch, privkey): - return bls_sign( - message_hash=message_hash, - privkey=privkey, - domain=spec.get_domain( - state=beacon_state, - domain_type=spec.DOMAIN_SHARD_ATTESTER, - message_epoch=block_epoch, - ) - ) + domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_ATTESTER, block_epoch) + signing_root = spec.compute_signing_root(message_hash, domain) + return bls.Sign(privkey, signing_root) diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py index 8d12110e7b..6e1fba8dc2 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -1,10 +1,8 @@ from copy import deepcopy from eth2spec.test.helpers.keys import privkeys -from eth2spec.utils.bls import ( - bls_sign, - only_with_bls, -) +from eth2spec.utils import bls +from eth2spec.utils.bls import only_with_bls from eth2spec.utils.ssz.ssz_impl import ( hash_tree_root, ) @@ -20,16 +18,9 @@ def sign_shard_block(spec, beacon_state, shard_state, block, proposer_index=None proposer_index = spec.get_shard_proposer_index(beacon_state, shard_state.shard, block.slot) privkey = privkeys[proposer_index] - - block.signature = bls_sign( - message_hash=hash_tree_root(block), - privkey=privkey, - domain=spec.get_domain( - beacon_state, - spec.DOMAIN_SHARD_PROPOSER, - spec.compute_epoch_of_shard_slot(block.slot), - ) - ) + domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_PROPOSER, spec.compute_epoch_of_shard_slot(block.slot)) + signing_root = spec.compute_signing_root(block, domain) + block.signature = bls.Sign(privkey, signing_root) def build_empty_shard_block(spec, diff --git a/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py b/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py index 62d8f13baa..55310ef7d5 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py @@ -1,17 +1,10 @@ -from eth2spec.utils.bls import bls_sign -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils import bls def sign_voluntary_exit(spec, state, voluntary_exit, privkey): + domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) + signing_root = spec.compute_signing_root(voluntary_exit, domain) return spec.SignedVoluntaryExit( message=voluntary_exit, - signature=bls_sign( - message_hash=hash_tree_root(voluntary_exit), - privkey=privkey, - domain=spec.get_domain( - state=state, - domain_type=spec.DOMAIN_VOLUNTARY_EXIT, - message_epoch=voluntary_exit.epoch, - ) - ) + signature=bls.Sign(privkey, signing_root) ) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py index 05a40407b3..b7a0de6c87 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py @@ -6,7 +6,7 @@ deposit_from_context) from eth2spec.test.helpers.state import get_balance from eth2spec.test.helpers.keys import privkeys, pubkeys -from eth2spec.utils.bls import bls_sign +from eth2spec.utils import bls def run_deposit_processing(spec, state, deposit, validator_index, valid=True, effective=True): @@ -106,14 +106,11 @@ def test_invalid_sig_other_version(spec, state): withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] # Go through the effort of manually signing, not something normally done. This sig domain will be invalid. + deposit_message = spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) + domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=spec.Version('0xaabbccdd')) deposit_data = spec.DepositData( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, - signature=bls_sign( - message_hash=spec.hash_tree_root( - spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)), - privkey=privkey, - domain=spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=spec.Version('0xaabbccdd')), - ) + signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain)) ) deposit, root, _ = deposit_from_context(spec, [deposit_data], 0) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index c32f4c5834..b386d36b40 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -1,7 +1,6 @@ from copy import deepcopy -from eth2spec.utils.ssz.ssz_impl import hash_tree_root -from eth2spec.utils.bls import bls_sign +from eth2spec.utils import bls from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block, \ @@ -108,15 +107,11 @@ def test_invalid_block_sig(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) + domain = spec.get_domain(state, spec.DOMAIN_BEACON_PROPOSER, spec.compute_epoch_at_slot(block.slot)) + signing_root = spec.compute_signing_root(block, domain) invalid_signed_block = spec.SignedBeaconBlock( message=block, - signature=bls_sign( - message_hash=hash_tree_root(block), - privkey=123456, - domain=spec.get_domain( - state, - spec.DOMAIN_BEACON_PROPOSER, - spec.compute_epoch_at_slot(block.slot))) + signature=bls.Sign(123456, signing_root) ) expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block)) @@ -421,16 +416,11 @@ def test_voluntary_exit(spec, state): epoch=spec.get_current_epoch(state), validator_index=validator_index, ) + domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT) + signing_root = spec.compute_signing_root(voluntary_exit, domain) signed_voluntary_exit = spec.SignedVoluntaryExit( message=voluntary_exit, - signature=bls_sign( - message_hash=hash_tree_root(voluntary_exit), - privkey=privkeys[validator_index], - domain=spec.get_domain( - state=state, - domain_type=spec.DOMAIN_VOLUNTARY_EXIT, - ) - ) + signature=bls.Sign(privkeys[validator_index], signing_root) ) # Add to state via block transition diff --git a/test_libs/pyspec/eth2spec/utils/bls.py b/test_libs/pyspec/eth2spec/utils/bls.py index f40e5ab048..83371ac628 100644 --- a/test_libs/pyspec/eth2spec/utils/bls.py +++ b/test_libs/pyspec/eth2spec/utils/bls.py @@ -1,11 +1,12 @@ -from py_ecc import bls +from py_ecc.bls import G2ProofOfPossession as bls +from py_ecc.bls.g2_primatives import signature_to_G2 as _signature_to_G2 # Flag to make BLS active or not. Used for testing, do not ignore BLS in production unless you know what you are doing. 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)) +STUB_COORDINATES = _signature_to_G2(bls.Sign(0, b"")) def only_with_bls(alt_return=None): @@ -23,33 +24,30 @@ def entry(*args, **kw): @only_with_bls(alt_return=True) -def bls_verify(pubkey, message_hash, signature, domain): - return bls.verify(message_hash=message_hash, pubkey=pubkey, - signature=signature, domain=domain) +def Verify(PK, message, signature): + return bls.Verify(PK, message, signature) @only_with_bls(alt_return=True) -def bls_verify_multiple(pubkeys, message_hashes, signature, domain): - return bls.verify_multiple(pubkeys=pubkeys, message_hashes=message_hashes, - signature=signature, domain=domain) +def AggregateVerify(pairs, signature): + return bls.AggregateVerify(pairs, signature) -@only_with_bls(alt_return=STUB_PUBKEY) -def bls_aggregate_pubkeys(pubkeys): - return bls.aggregate_pubkeys(pubkeys) +@only_with_bls(alt_return=True) +def FastAggregateVerify(PKs, message, signature): + return bls.FastAggregateVerify(PKs, message, signature) @only_with_bls(alt_return=STUB_SIGNATURE) -def bls_aggregate_signatures(signatures): - return bls.aggregate_signatures(signatures) +def Aggregate(signatures): + return bls.Aggregate(signatures) @only_with_bls(alt_return=STUB_SIGNATURE) -def bls_sign(message_hash, privkey, domain): - return bls.sign(message_hash=message_hash, privkey=privkey, - domain=domain) +def Sign(SK, message): + return bls.Sign(SK, message) @only_with_bls(alt_return=STUB_COORDINATES) -def bls_signature_to_G2(signature): - return bls.api.signature_to_G2(signature) +def signature_to_G2(signature): + return _signature_to_G2(signature) diff --git a/test_libs/pyspec/requirements.txt b/test_libs/pyspec/requirements.txt index 8dfbccf660..60acb5d354 100644 --- a/test_libs/pyspec/requirements.txt +++ b/test_libs/pyspec/requirements.txt @@ -1,6 +1,6 @@ eth-utils>=1.3.0,<2 eth-typing>=2.1.0,<3.0.0 pycryptodome==3.9.4 -py_ecc==1.7.1 +py_ecc==2.0.0 dataclasses==0.6 ssz==0.1.3 diff --git a/test_libs/pyspec/setup.py b/test_libs/pyspec/setup.py index d41412eb8b..5be0db7f82 100644 --- a/test_libs/pyspec/setup.py +++ b/test_libs/pyspec/setup.py @@ -8,7 +8,7 @@ "eth-utils>=1.3.0,<2", "eth-typing>=2.1.0,<3.0.0", "pycryptodome==3.9.4", - "py_ecc==1.7.1", + "py_ecc==2.0.0", "ssz==0.1.3", "dataclasses==0.6", ]