Merge pull request #1532 from ethereum/carl_new_new_bls

Yet another attempt at adopting IETF BLS Standards
This commit is contained in:
Danny Ryan 2020-01-08 13:06:12 -07:00 committed by GitHub
commit 38f947b0c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 167 additions and 396 deletions

View File

@ -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:

View File

@ -11,7 +11,7 @@ from typing import (
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 dataclasses import (
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 @@ from eth2spec.utils.ssz.ssz_typing import (
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:

View File

@ -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
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## 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)`.

View File

@ -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)
```

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -1,3 +1,3 @@
py_ecc==1.7.1
py_ecc==2.0.0
eth-utils==1.6.0
../../test_libs/gen_helpers

View File

@ -3,7 +3,7 @@ from typing import List
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):

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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)}

View File

@ -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)

View File

@ -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,

View File

@ -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)
)

View File

@ -6,7 +6,7 @@ from eth2spec.test.helpers.deposits import (
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)

View File

@ -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

View File

@ -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 only_with_bls(alt_return=None):
@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)

View File

@ -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

View File

@ -8,7 +8,7 @@ setup(
"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",
]