Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yet another attempt at adopting IETF BLS Standards #1532

Merged
merged 19 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions scripts/build_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -21,20 +21,22 @@

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,
Sign,
Verify,
Aggregate,
FastAggregateVerify,
)

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,
Expand All @@ -56,9 +58,9 @@
uint64, bit, boolean, byte,
)
from eth2spec.utils.bls import (
bls_aggregate_pubkeys,
bls_verify,
bls_verify_multiple,
Verify,
AggregateVerify,
FastAggregateVerify,
bls_signature_to_G2,
)

Expand All @@ -67,6 +69,7 @@

SSZVariableName = str
GeneralizedIndex = NewType('GeneralizedIndex', int)
SSZObject = TypeVar('SSZObject', bound=SSZType)
'''
SUNDRY_CONSTANTS_FUNCTIONS = '''
def ceillog2(x: uint64) -> int:
Expand Down Expand Up @@ -164,7 +167,7 @@ def objects_to_spec(functions: Dict[str, str],
functions_spec = '\n\n'.join(functions.values())
for k in list(constants.keys()):
if k.startswith('DOMAIN_'):
constants[k] = f"DomainType(({constants[k]}).to_bytes(length=4, byteorder='little'))"
constants[k] = f"DomainType(bytes.fromhex('{constants[k]}'[2:]))"
if k == "BLS12_381_Q":
constants[k] += " # noqa: E501"
constants_spec = '\n'.join(map(lambda x: '%s = %s' % (x, constants[x]), constants))
Expand Down
148 changes: 0 additions & 148 deletions specs/bls_signature.md

This file was deleted.

81 changes: 52 additions & 29 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- [`DepositMessage`](#depositmessage)
- [`DepositData`](#depositdata)
- [`BeaconBlockHeader`](#beaconblockheader)
- [`DomainWrapper`](#domainwrapper)
- [Beacon operations](#beacon-operations)
- [`ProposerSlashing`](#proposerslashing)
- [`AttesterSlashing`](#attesterslashing)
Expand All @@ -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)
Expand All @@ -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_domain_wrapper_root`](#compute_domain_wrapper_root)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_current_epoch`](#get_current_epoch)
- [`get_previous_epoch`](#get_previous_epoch)
Expand Down Expand Up @@ -249,15 +250,13 @@ The following values are (non-configurable) constants used throughout the specif

### Domain types

The following types are defined, mapping into `DomainType` (little endian):

| Name | Value |
| - | - |
| `DOMAIN_BEACON_PROPOSER` | `0` |
| `DOMAIN_BEACON_ATTESTER` | `1` |
| `DOMAIN_RANDAO` | `2` |
| `DOMAIN_DEPOSIT` | `3` |
| `DOMAIN_VOLUNTARY_EXIT` | `4` |
| `DOMAIN_BEACON_PROPOSER` | `0x00000000` |
| `DOMAIN_BEACON_ATTESTER` | `0x01000000` |
| `DOMAIN_RANDAO` | `0x02000000` |
| `DOMAIN_DEPOSIT` | `0x03000000` |
| `DOMAIN_VOLUNTARY_EXIT` | `0x04000000` |

## Containers

Expand Down Expand Up @@ -379,6 +378,14 @@ class BeaconBlockHeader(Container):
body_root: Root
```

#### `DomainWrapper`

```python
class DomainWrapper(Container):
root: Root
domain: Domain
```

### Beacon operations

#### `ProposerSlashing`
Expand Down Expand Up @@ -575,13 +582,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).

#### `bls_aggregate_pubkeys`
Specifically, eth2 uses the `BLS_SIG_BLS12381G2-SHA256-SSWU-RO-_POP_` ciphersuite where it makes use of the following functions:

`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).
* `def Sign(SK: int, message: Bytes) -> BLSSignature`
JustinDrake marked this conversation as resolved.
Show resolved Hide resolved
* `def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool`
* `def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature`
CarlBeek marked this conversation as resolved.
Show resolved Hide resolved
* `def bls_aggregate_pubkeys(PKs: Sequence[BLSPubkey]) -> BLSPubkey`
CarlBeek marked this conversation as resolved.
Show resolved Hide resolved
* `def FastAggregateVerify(PKs: Sequence[BLSSignature], message: Bytes, signature: BLSSignature) -> bool`

### Predicates

Expand Down Expand Up @@ -664,14 +675,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)
message = compute_domain_wrapper_root(indexed_attestation.data, domain)
return FastAggregateVerify(pubkeys, message, indexed_attestation.signature)
```

#### `is_valid_merkle_branch`
Expand Down Expand Up @@ -789,6 +796,17 @@ def compute_domain(domain_type: DomainType, fork_version: Version=Version()) ->
return Domain(domain_type + fork_version)
```

### `compute_domain_wrapper_root`
CarlBeek marked this conversation as resolved.
Show resolved Hide resolved

```python
def compute_domain_wrapper_root(object: SSZObject, domain: Domain) -> Root:
CarlBeek marked this conversation as resolved.
Show resolved Hide resolved
domain_wrapped_object = DomainWrapper(
root=hash_tree_root(object),
domain=domain,
)
return hash_tree_root(domain_wrapped_object)
```

### Beacon state accessors

#### `get_current_epoch`
Expand Down Expand Up @@ -1131,8 +1149,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)
message = compute_domain_wrapper_root(signed_block.message, get_domain(state, DOMAIN_BEACON_PROPOSER))
CarlBeek marked this conversation as resolved.
Show resolved Hide resolved
return Verify(proposer.pubkey, message, signed_block.signature)
```

```python
Expand Down Expand Up @@ -1431,7 +1449,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))
message = compute_domain_wrapper_root(epoch, get_domain(state, DOMAIN_RANDAO))
CarlBeek marked this conversation as resolved.
Show resolved Hide resolved
assert Verify(proposer.pubkey, message, 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
Expand Down Expand Up @@ -1478,8 +1497,11 @@ def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSla
assert is_slashable_validator(proposer, get_current_epoch(state))
# 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)
message = compute_domain_wrapper_root(
object=signed_header.message,
domain=get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot)),
)
assert Verify(proposer.pubkey, message, signed_header.signature)

slash_validator(state, proposer_slashing.proposer_index)
```
Expand Down Expand Up @@ -1557,12 +1579,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):
message = compute_domain_wrapper_root(deposit_message, compute_domain(DOMAIN_DEPOSIT))
if not Verify(pubkey, message, deposit.data.signature):
return

# Add validator and balance entries
Expand Down Expand Up @@ -1598,7 +1620,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)
message = compute_domain_wrapper_root(voluntary_exit, domain)
assert Verify(validator.pubkey, message, signed_voluntary_exit.signature)
# Initiate exit
initiate_validator_exit(state, voluntary_exit.validator_index)
```
Loading