Add whisk tests
This commit is contained in:
parent
f1aabcd718
commit
4ce2b02b44
|
@ -168,6 +168,19 @@ jobs:
|
||||||
command: make citest fork=eip6110
|
command: make citest fork=eip6110
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: tests/core/pyspec/test-reports
|
path: tests/core/pyspec/test-reports
|
||||||
|
test-whisk:
|
||||||
|
docker:
|
||||||
|
- image: circleci/python:3.9
|
||||||
|
working_directory: ~/specs-repo
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||||
|
- restore_pyspec_cached_venv
|
||||||
|
- run:
|
||||||
|
name: Run py-tests
|
||||||
|
command: make citest fork=whisk
|
||||||
|
- store_test_results:
|
||||||
|
path: tests/core/pyspec/test-reports
|
||||||
table_of_contents:
|
table_of_contents:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:10.16.3
|
- image: circleci/node:10.16.3
|
||||||
|
@ -291,6 +304,9 @@ workflows:
|
||||||
- test-eip6110:
|
- test-eip6110:
|
||||||
requires:
|
requires:
|
||||||
- install_pyspec_test
|
- install_pyspec_test
|
||||||
|
- test-whisk:
|
||||||
|
requires:
|
||||||
|
- install_pyspec_test
|
||||||
- table_of_contents
|
- table_of_contents
|
||||||
- codespell
|
- codespell
|
||||||
- lint:
|
- lint:
|
||||||
|
|
|
@ -71,7 +71,7 @@ jobs:
|
||||||
needs: [preclear,lint,codespell,table_of_contents]
|
needs: [preclear,lint,codespell,table_of_contents]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "eip6110"]
|
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "eip6110", "whisk"]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout this repo
|
- name: Checkout this repo
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.2.0
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -257,6 +257,11 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr
|
||||||
if any('KZG_SETUP' in name for name in constant_vars):
|
if any('KZG_SETUP' in name for name in constant_vars):
|
||||||
_update_constant_vars_with_kzg_setups(constant_vars, preset_name)
|
_update_constant_vars_with_kzg_setups(constant_vars, preset_name)
|
||||||
|
|
||||||
|
if any('CURDLEPROOFS_CRS' in name for name in constant_vars):
|
||||||
|
# TODO: Use actual CRS derived from a fixed string like 'nankokita_no_kakurenbo'
|
||||||
|
crs_len = int(preset_vars['WHISK_VALIDATORS_PER_SHUFFLE'].value) + int(preset_vars['CURDLEPROOFS_N_BLINDERS'].value) + 3
|
||||||
|
constant_vars['CURDLEPROOFS_CRS_G1'] = VariableDefinition(constant_vars['CURDLEPROOFS_CRS_G1'].value, str(ALL_KZG_SETUPS['mainnet'][0][0:crs_len]), "noqa: E501", None)
|
||||||
|
|
||||||
return SpecObject(
|
return SpecObject(
|
||||||
functions=functions,
|
functions=functions,
|
||||||
protocols=protocols,
|
protocols=protocols,
|
||||||
|
@ -519,6 +524,6 @@ setup(
|
||||||
"lru-dict==1.2.0",
|
"lru-dict==1.2.0",
|
||||||
MARKO_VERSION,
|
MARKO_VERSION,
|
||||||
"py_arkworks_bls12381==0.3.4",
|
"py_arkworks_bls12381==0.3.4",
|
||||||
"curdleproofs @ git+https://github.com/nalinbhardwaj/curdleproofs.pie@805d06785b6ff35fde7148762277dd1ae678beeb#egg=curdleproofs&subdirectory=curdleproofs",
|
"curdleproofs @ git+https://github.com/nalinbhardwaj/curdleproofs.pie@5fe661b7183454655ff1e47690bb28e01e66ea66#egg=curdleproofs&subdirectory=curdleproofs",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
This document details the beacon chain additions and changes of to support the Whisk SSLE,
|
This document details the beacon chain additions and changes of to support the Whisk SSLE,
|
||||||
|
|
||||||
*Note:* This specification is built upon [Capella](../../capella/beacon-chain.md) and is under active development.
|
*Note:* This specification is built upon [capella](../../capella/beacon-chain.md) and is under active development.
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
|
@ -83,14 +83,15 @@ def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
|
||||||
TODO: Deneb will introduces this helper too. Should delete it once it's rebased to post-Deneb.
|
TODO: Deneb will introduces this helper too. Should delete it once it's rebased to post-Deneb.
|
||||||
"""
|
"""
|
||||||
field_element = int.from_bytes(b, ENDIANNESS)
|
field_element = int.from_bytes(b, ENDIANNESS)
|
||||||
assert field_element < BLS_MODULUS
|
return BLSFieldElement(field_element % BLS_MODULUS)
|
||||||
return BLSFieldElement(field_element)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
| Name | Value |
|
| Name | Value |
|
||||||
| ------------------ | ------------------------------------------------------------------------------- |
|
| ------------------ | ------------------------------------------------------------------------------- |
|
||||||
| `BLS_G1_GENERATOR` | `bls.G1_to_bytes48(bls.G1)` |
|
| `BLS_G1_GENERATOR` | `Bytes48('0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb')` |
|
||||||
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` |
|
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` |
|
||||||
|
| `CURDLEPROOFS_CRS_G1` | `Vector[G1Point, WHISK_VALIDATORS_PER_SHUFFLE + CURDLEPROOFS_N_BLINDERS + 3]`, contents TBD
|
||||||
|
| `CURDLEPROOFS_CRS` | `curdleproofs.CurdleproofsCrs.from_points_compressed(WHISK_VALIDATORS_PER_SHUFFLE, CURDLEPROOFS_N_BLINDERS, CURDLEPROOFS_CRS_G1)` |
|
||||||
|
|
||||||
### Curdleproofs and opening proofs
|
### Curdleproofs and opening proofs
|
||||||
|
|
||||||
|
@ -105,8 +106,16 @@ def IsValidWhiskShuffleProof(pre_shuffle_trackers: Sequence[WhiskTracker],
|
||||||
Verify `post_shuffle_trackers` is a permutation of `pre_shuffle_trackers`.
|
Verify `post_shuffle_trackers` is a permutation of `pre_shuffle_trackers`.
|
||||||
Defined in https://github.com/nalinbhardwaj/curdleproofs.pie/tree/verifier-only.
|
Defined in https://github.com/nalinbhardwaj/curdleproofs.pie/tree/verifier-only.
|
||||||
"""
|
"""
|
||||||
# pylint: disable=unused-argument
|
try:
|
||||||
return True
|
return curdleproofs.IsValidWhiskShuffleProof(
|
||||||
|
CURDLEPROOFS_CRS,
|
||||||
|
pre_shuffle_trackers,
|
||||||
|
post_shuffle_trackers,
|
||||||
|
M,
|
||||||
|
shuffle_proof,
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -117,8 +126,11 @@ def IsValidWhiskOpeningProof(tracker: WhiskTracker,
|
||||||
Verify knowledge of `k` such that `tracker.k_r_G == k * tracker.r_G` and `k_commitment == k * BLS_G1_GENERATOR`.
|
Verify knowledge of `k` such that `tracker.k_r_G == k * tracker.r_G` and `k_commitment == k * BLS_G1_GENERATOR`.
|
||||||
Defined in https://github.com/nalinbhardwaj/curdleproofs.pie/tree/verifier-only.
|
Defined in https://github.com/nalinbhardwaj/curdleproofs.pie/tree/verifier-only.
|
||||||
"""
|
"""
|
||||||
# pylint: disable=unused-argument
|
try:
|
||||||
return True
|
return curdleproofs.IsValidWhiskOpeningProof(tracker, k_commitment, tracker_proof)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Epoch processing
|
## Epoch processing
|
||||||
|
@ -276,7 +288,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
||||||
#### `BeaconBlockBody`
|
#### `BeaconBlockBody`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class BeaconBlockBody(capella.BeaconBlockBody):
|
class BeaconBlockBody(Container):
|
||||||
randao_reveal: BLSSignature
|
randao_reveal: BLSSignature
|
||||||
eth1_data: Eth1Data # Eth1 data vote
|
eth1_data: Eth1Data # Eth1 data vote
|
||||||
graffiti: Bytes32 # Arbitrary data
|
graffiti: Bytes32 # Arbitrary data
|
||||||
|
@ -289,7 +301,6 @@ class BeaconBlockBody(capella.BeaconBlockBody):
|
||||||
sync_aggregate: SyncAggregate
|
sync_aggregate: SyncAggregate
|
||||||
# Execution
|
# Execution
|
||||||
execution_payload: ExecutionPayload
|
execution_payload: ExecutionPayload
|
||||||
# Capella operations
|
|
||||||
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
|
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
|
||||||
# Whisk
|
# Whisk
|
||||||
whisk_opening_proof: WhiskTrackerProof # [New in Whisk]
|
whisk_opening_proof: WhiskTrackerProof # [New in Whisk]
|
||||||
|
@ -350,10 +361,7 @@ def is_k_commitment_unique(state: BeaconState, k_commitment: BLSG1Point) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_whisk(state: BeaconState, body: BeaconBlockBody) -> None:
|
def process_whisk_registration(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||||
process_shuffled_trackers(state, body)
|
|
||||||
|
|
||||||
# Overwrite all validator Whisk fields (first Whisk proposal) or just the permutation commitment (next proposals)
|
|
||||||
proposer = state.validators[get_beacon_proposer_index(state)]
|
proposer = state.validators[get_beacon_proposer_index(state)]
|
||||||
if proposer.whisk_tracker.r_G == BLS_G1_GENERATOR: # first Whisk proposal
|
if proposer.whisk_tracker.r_G == BLS_G1_GENERATOR: # first Whisk proposal
|
||||||
assert body.whisk_tracker.r_G != BLS_G1_GENERATOR
|
assert body.whisk_tracker.r_G != BLS_G1_GENERATOR
|
||||||
|
@ -369,20 +377,19 @@ def process_whisk(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||||
assert body.whisk_registration_proof == WhiskTrackerProof()
|
assert body.whisk_registration_proof == WhiskTrackerProof()
|
||||||
assert body.whisk_tracker == WhiskTracker()
|
assert body.whisk_tracker == WhiskTracker()
|
||||||
assert body.whisk_k_commitment == BLSG1Point()
|
assert body.whisk_k_commitment == BLSG1Point()
|
||||||
assert body.whisk_shuffle_proof_M_commitment == BLSG1Point()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||||
process_block_header(state, block)
|
process_block_header(state, block)
|
||||||
if is_execution_enabled(state, block.body):
|
process_withdrawals(state, block.body.execution_payload)
|
||||||
process_withdrawals(state, block.body.execution_payload)
|
process_execution_payload(state, block.body, EXECUTION_ENGINE)
|
||||||
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE)
|
|
||||||
process_randao(state, block.body)
|
process_randao(state, block.body)
|
||||||
process_eth1_data(state, block.body)
|
process_eth1_data(state, block.body)
|
||||||
process_operations(state, block.body)
|
process_operations(state, block.body)
|
||||||
process_sync_aggregate(state, block.body.sync_aggregate)
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
||||||
process_whisk(state, block.body) # [New in Whisk]
|
process_shuffled_trackers(state, block.body) # [New in Whisk]
|
||||||
|
process_whisk_registration(state, block.body) # [New in Whisk]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Deposits
|
### Deposits
|
||||||
|
|
|
@ -9,12 +9,14 @@ from eth2spec.bellatrix import mainnet as spec_bellatrix_mainnet, minimal as spe
|
||||||
from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_capella_minimal
|
from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_capella_minimal
|
||||||
from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal
|
from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal
|
||||||
from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal
|
from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal
|
||||||
|
from eth2spec.whisk import mainnet as spec_whisk_mainnet, minimal as spec_whisk_minimal
|
||||||
from eth2spec.utils import bls
|
from eth2spec.utils import bls
|
||||||
|
|
||||||
from .exceptions import SkippedTest
|
from .exceptions import SkippedTest
|
||||||
from .helpers.constants import (
|
from .helpers.constants import (
|
||||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
|
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
|
||||||
EIP6110,
|
EIP6110,
|
||||||
|
WHISK,
|
||||||
MINIMAL, MAINNET,
|
MINIMAL, MAINNET,
|
||||||
ALL_PHASES,
|
ALL_PHASES,
|
||||||
ALL_FORK_UPGRADES,
|
ALL_FORK_UPGRADES,
|
||||||
|
@ -83,6 +85,7 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
|
||||||
CAPELLA: spec_capella_minimal,
|
CAPELLA: spec_capella_minimal,
|
||||||
DENEB: spec_deneb_minimal,
|
DENEB: spec_deneb_minimal,
|
||||||
EIP6110: spec_eip6110_minimal,
|
EIP6110: spec_eip6110_minimal,
|
||||||
|
WHISK: spec_whisk_minimal,
|
||||||
},
|
},
|
||||||
MAINNET: {
|
MAINNET: {
|
||||||
PHASE0: spec_phase0_mainnet,
|
PHASE0: spec_phase0_mainnet,
|
||||||
|
@ -91,6 +94,7 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
|
||||||
CAPELLA: spec_capella_mainnet,
|
CAPELLA: spec_capella_mainnet,
|
||||||
DENEB: spec_deneb_mainnet,
|
DENEB: spec_deneb_mainnet,
|
||||||
EIP6110: spec_eip6110_mainnet,
|
EIP6110: spec_eip6110_mainnet,
|
||||||
|
WHISK: spec_whisk_mainnet,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,6 +545,7 @@ with_bellatrix_and_later = with_all_phases_from(BELLATRIX)
|
||||||
with_capella_and_later = with_all_phases_from(CAPELLA)
|
with_capella_and_later = with_all_phases_from(CAPELLA)
|
||||||
with_deneb_and_later = with_all_phases_from(DENEB)
|
with_deneb_and_later = with_all_phases_from(DENEB)
|
||||||
with_eip6110_and_later = with_all_phases_from(EIP6110)
|
with_eip6110_and_later = with_all_phases_from(EIP6110)
|
||||||
|
with_whisk_and_later = with_all_phases_from(WHISK)
|
||||||
|
|
||||||
|
|
||||||
class quoted_str(str):
|
class quoted_str(str):
|
||||||
|
|
|
@ -1,10 +1,25 @@
|
||||||
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
|
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
|
||||||
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
|
from eth2spec.test.helpers.forks import is_post_whisk, is_post_altair, is_post_bellatrix
|
||||||
from eth2spec.test.helpers.keys import privkeys
|
from eth2spec.test.helpers.keys import privkeys, whisk_ks_initial, whisk_ks_final
|
||||||
from eth2spec.utils import bls
|
from eth2spec.utils import bls
|
||||||
from eth2spec.utils.bls import only_with_bls
|
from eth2spec.utils.bls import only_with_bls
|
||||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||||
|
from curdleproofs import (
|
||||||
|
GenerateWhiskTrackerProof,
|
||||||
|
WhiskTracker,
|
||||||
|
IsValidWhiskOpeningProof,
|
||||||
|
GenerateWhiskShuffleProof,
|
||||||
|
IsValidWhiskShuffleProof
|
||||||
|
)
|
||||||
|
from py_ecc.optimized_bls12_381.optimized_curve import G1, multiply
|
||||||
|
from py_ecc.typing import Optimized_Field, Optimized_Point3D
|
||||||
|
from py_ecc.bls.g2_primitives import (
|
||||||
|
G1_to_pubkey as py_ecc_G1_to_bytes48,
|
||||||
|
pubkey_to_G1 as py_ecc_bytes48_to_G1,
|
||||||
|
)
|
||||||
|
from eth2spec.test.helpers.whisk import get_whisk_tracker_and_commitment
|
||||||
|
|
||||||
|
PointProjective = Optimized_Point3D[Optimized_Field]
|
||||||
|
|
||||||
def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
|
def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
|
||||||
if proposer_index is None:
|
if proposer_index is None:
|
||||||
|
@ -24,10 +39,9 @@ def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
|
||||||
|
|
||||||
|
|
||||||
@only_with_bls()
|
@only_with_bls()
|
||||||
def apply_randao_reveal(spec, state, block, proposer_index=None):
|
def apply_randao_reveal(spec, state, block, proposer_index):
|
||||||
assert state.slot <= block.slot
|
assert state.slot <= block.slot
|
||||||
|
|
||||||
proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
|
|
||||||
privkey = privkeys[proposer_index]
|
privkey = privkeys[proposer_index]
|
||||||
|
|
||||||
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, spec.compute_epoch_at_slot(block.slot))
|
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, spec.compute_epoch_at_slot(block.slot))
|
||||||
|
@ -72,7 +86,7 @@ def apply_empty_block(spec, state, slot=None):
|
||||||
return transition_unsigned_block(spec, state, block)
|
return transition_unsigned_block(spec, state, block)
|
||||||
|
|
||||||
|
|
||||||
def build_empty_block(spec, state, slot=None):
|
def build_empty_block(spec, state, slot=None, proposer_index=None):
|
||||||
"""
|
"""
|
||||||
Build empty block for ``slot``, built upon the latest block header seen by ``state``.
|
Build empty block for ``slot``, built upon the latest block header seen by ``state``.
|
||||||
Slot must be greater than or equal to the current slot in ``state``.
|
Slot must be greater than or equal to the current slot in ``state``.
|
||||||
|
@ -87,13 +101,14 @@ def build_empty_block(spec, state, slot=None):
|
||||||
spec.process_slots(state, slot)
|
spec.process_slots(state, slot)
|
||||||
|
|
||||||
state, parent_block_root = get_state_and_beacon_parent_root_at_slot(spec, state, slot)
|
state, parent_block_root = get_state_and_beacon_parent_root_at_slot(spec, state, slot)
|
||||||
|
proposer_index = get_beacon_proposer_to_build(spec, state, proposer_index)
|
||||||
empty_block = spec.BeaconBlock()
|
empty_block = spec.BeaconBlock()
|
||||||
empty_block.slot = slot
|
empty_block.slot = slot
|
||||||
empty_block.proposer_index = spec.get_beacon_proposer_index(state)
|
empty_block.proposer_index = proposer_index
|
||||||
empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index
|
empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index
|
||||||
empty_block.parent_root = parent_block_root
|
empty_block.parent_root = parent_block_root
|
||||||
|
|
||||||
apply_randao_reveal(spec, state, empty_block)
|
apply_randao_reveal(spec, state, empty_block, proposer_index)
|
||||||
|
|
||||||
if is_post_altair(spec):
|
if is_post_altair(spec):
|
||||||
empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY
|
empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY
|
||||||
|
@ -101,11 +116,112 @@ def build_empty_block(spec, state, slot=None):
|
||||||
if is_post_bellatrix(spec):
|
if is_post_bellatrix(spec):
|
||||||
empty_block.body.execution_payload = build_empty_execution_payload(spec, state)
|
empty_block.body.execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
|
||||||
|
if is_post_whisk(spec):
|
||||||
|
# Whisk opening proof
|
||||||
|
#######
|
||||||
|
|
||||||
|
# Create valid whisk opening proof
|
||||||
|
# TODO: Use k_initial or k_final to handle first and subsequent proposals
|
||||||
|
k_initial = whisk_ks_initial[proposer_index]
|
||||||
|
|
||||||
|
# Sanity check proposer is correct
|
||||||
|
proposer_k_commitment = state.validators[proposer_index].whisk_k_commitment
|
||||||
|
k_commitment = py_ecc_G1_to_bytes48(multiply(G1, int(k_initial)))
|
||||||
|
if proposer_k_commitment != k_commitment:
|
||||||
|
raise Exception("k proposer_index does not match proposer_k_commitment", proposer_k_commitment, k_commitment)
|
||||||
|
|
||||||
|
proposer_tracker = state.whisk_proposer_trackers[state.slot % spec.WHISK_PROPOSER_TRACKERS_COUNT]
|
||||||
|
if not is_whisk_proposer(proposer_tracker, k_initial):
|
||||||
|
raise Exception("k proposer_index does not match proposer_tracker")
|
||||||
|
|
||||||
|
empty_block.body.whisk_opening_proof = GenerateWhiskTrackerProof(proposer_tracker, k_initial)
|
||||||
|
if not IsValidWhiskOpeningProof(proposer_tracker, proposer_k_commitment, empty_block.body.whisk_opening_proof):
|
||||||
|
raise Exception(
|
||||||
|
"produced opening proof is not valid",
|
||||||
|
proposer_tracker,
|
||||||
|
proposer_k_commitment,
|
||||||
|
empty_block.body.whisk_opening_proof
|
||||||
|
)
|
||||||
|
|
||||||
|
# Whisk shuffle proof
|
||||||
|
#######
|
||||||
|
|
||||||
|
shuffle_indices = spec.get_shuffle_indices(empty_block.body.randao_reveal)
|
||||||
|
pre_shuffle_trackers = [state.whisk_candidate_trackers[i] for i in shuffle_indices]
|
||||||
|
|
||||||
|
post_trackers, m, shuffle_proof = GenerateWhiskShuffleProof(spec.CURDLEPROOFS_CRS, pre_shuffle_trackers)
|
||||||
|
empty_block.body.whisk_post_shuffle_trackers = post_trackers
|
||||||
|
empty_block.body.whisk_shuffle_proof = shuffle_proof
|
||||||
|
empty_block.body.whisk_shuffle_proof_M_commitment = m
|
||||||
|
|
||||||
|
if not IsValidWhiskShuffleProof(
|
||||||
|
spec.CURDLEPROOFS_CRS,
|
||||||
|
pre_shuffle_trackers,
|
||||||
|
empty_block.body.whisk_post_shuffle_trackers,
|
||||||
|
empty_block.body.whisk_shuffle_proof_M_commitment,
|
||||||
|
empty_block.body.whisk_shuffle_proof,
|
||||||
|
):
|
||||||
|
raise Exception(
|
||||||
|
"produced shuffle proof is not valid",
|
||||||
|
pre_shuffle_trackers,
|
||||||
|
empty_block.body.whisk_post_shuffle_trackers,
|
||||||
|
empty_block.body.whisk_shuffle_proof_M_commitment,
|
||||||
|
empty_block.body.whisk_shuffle_proof,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Whisk registration proof
|
||||||
|
#######
|
||||||
|
|
||||||
|
# Branching logic depending if first proposal or not
|
||||||
|
is_first_proposal = state.validators[proposer_index].whisk_tracker.r_G == spec.BLS_G1_GENERATOR
|
||||||
|
if is_first_proposal:
|
||||||
|
# Register new tracker
|
||||||
|
k_final = whisk_ks_final[proposer_index]
|
||||||
|
# TODO: Actual logic should pick a random r, but we may want to do something fancy to locate trackers quickly
|
||||||
|
r = 2
|
||||||
|
tracker, k_commitment = get_whisk_tracker_and_commitment(k_final, r)
|
||||||
|
empty_block.body.whisk_registration_proof = GenerateWhiskTrackerProof(tracker, k_final)
|
||||||
|
empty_block.body.whisk_tracker = tracker
|
||||||
|
empty_block.body.whisk_k_commitment = k_commitment
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Subsequent proposals, just leave empty
|
||||||
|
empty_block.body.whisk_registration_proof = spec.WhiskTrackerProof()
|
||||||
|
empty_block.body.whisk_tracker = spec.WhiskTracker()
|
||||||
|
empty_block.body.whisk_k_commitment = spec.BLSG1Point()
|
||||||
|
|
||||||
return empty_block
|
return empty_block
|
||||||
|
|
||||||
|
|
||||||
def build_empty_block_for_next_slot(spec, state):
|
def is_whisk_proposer(tracker: WhiskTracker, k: int) -> bool:
|
||||||
return build_empty_block(spec, state, state.slot + 1)
|
return py_ecc_G1_to_bytes48(multiply(py_ecc_bytes48_to_G1(tracker.r_G), k)) == tracker.k_r_G
|
||||||
|
|
||||||
|
|
||||||
|
def get_beacon_proposer_to_build(spec, state, proposer_index=None):
|
||||||
|
if is_post_whisk(spec):
|
||||||
|
if proposer_index is None:
|
||||||
|
return find_whisk_proposer(spec, state)
|
||||||
|
else:
|
||||||
|
return proposer_index
|
||||||
|
else:
|
||||||
|
return spec.get_beacon_proposer_index(state)
|
||||||
|
|
||||||
|
|
||||||
|
def find_whisk_proposer(spec, state):
|
||||||
|
raise Exception("proposer not known without heavy math")
|
||||||
|
proposer_tracker = state.whisk_proposer_trackers[state.slot % spec.WHISK_PROPOSER_TRACKERS_COUNT]
|
||||||
|
# First attempt direct equality with trackers
|
||||||
|
for i, validator in enumerate(state.validators):
|
||||||
|
# # This is insanely slow
|
||||||
|
# if validator.whisk_tracker == proposer_tracker:
|
||||||
|
if True:
|
||||||
|
return i
|
||||||
|
# In Whisk where to get proposer from?
|
||||||
|
raise Exception("proposer_tracker not matched")
|
||||||
|
|
||||||
|
|
||||||
|
def build_empty_block_for_next_slot(spec, state, proposer_index=None):
|
||||||
|
return build_empty_block(spec, state, state.slot + 1, proposer_index)
|
||||||
|
|
||||||
|
|
||||||
def get_state_and_beacon_parent_root_at_slot(spec, state, slot):
|
def get_state_and_beacon_parent_root_at_slot(spec, state, slot):
|
||||||
|
|
|
@ -17,6 +17,7 @@ SHARDING = SpecForkName('sharding')
|
||||||
CUSTODY_GAME = SpecForkName('custody_game')
|
CUSTODY_GAME = SpecForkName('custody_game')
|
||||||
DAS = SpecForkName('das')
|
DAS = SpecForkName('das')
|
||||||
EIP6110 = SpecForkName('eip6110')
|
EIP6110 = SpecForkName('eip6110')
|
||||||
|
WHISK = SpecForkName('whisk')
|
||||||
|
|
||||||
#
|
#
|
||||||
# SpecFork settings
|
# SpecFork settings
|
||||||
|
@ -32,11 +33,12 @@ ALL_PHASES = (
|
||||||
DENEB,
|
DENEB,
|
||||||
# Experimental patches
|
# Experimental patches
|
||||||
EIP6110,
|
EIP6110,
|
||||||
|
WHISK,
|
||||||
)
|
)
|
||||||
# The forks that have light client specs
|
# The forks that have light client specs
|
||||||
LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB)
|
LIGHT_CLIENT_TESTING_FORKS = (*[item for item in MAINNET_FORKS if item != PHASE0], DENEB)
|
||||||
# The forks that output to the test vectors.
|
# The forks that output to the test vectors.
|
||||||
TESTGEN_FORKS = (*MAINNET_FORKS, DENEB, EIP6110)
|
TESTGEN_FORKS = (*MAINNET_FORKS, DENEB, EIP6110, WHISK)
|
||||||
|
|
||||||
ALL_FORK_UPGRADES = {
|
ALL_FORK_UPGRADES = {
|
||||||
# pre_fork_name: post_fork_name
|
# pre_fork_name: post_fork_name
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
from .constants import (
|
from .constants import (
|
||||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
|
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
|
||||||
EIP6110,
|
EIP6110,
|
||||||
|
WHISK,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_post_fork(a, b):
|
def is_post_fork(a, b):
|
||||||
|
if a == WHISK:
|
||||||
|
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, WHISK]
|
||||||
if a == EIP6110:
|
if a == EIP6110:
|
||||||
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110]
|
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110]
|
||||||
if a == DENEB:
|
if a == DENEB:
|
||||||
|
@ -38,3 +41,7 @@ def is_post_deneb(spec):
|
||||||
|
|
||||||
def is_post_eip6110(spec):
|
def is_post_eip6110(spec):
|
||||||
return is_post_fork(spec.fork, EIP6110)
|
return is_post_fork(spec.fork, EIP6110)
|
||||||
|
|
||||||
|
|
||||||
|
def is_post_whisk(spec):
|
||||||
|
return is_post_fork(spec.fork, WHISK)
|
|
@ -1,13 +1,13 @@
|
||||||
from eth2spec.test.helpers.constants import (
|
from eth2spec.test.helpers.constants import (
|
||||||
ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110,
|
ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, WHISK,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.execution_payload import (
|
from eth2spec.test.helpers.execution_payload import (
|
||||||
compute_el_header_block_hash,
|
compute_el_header_block_hash,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.forks import (
|
from eth2spec.test.helpers.forks import (
|
||||||
is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110,
|
is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110, is_post_whisk,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.keys import pubkeys
|
from eth2spec.test.helpers.keys import pubkeys, whisk_ks_initial
|
||||||
|
|
||||||
|
|
||||||
def build_mock_validator(spec, i: int, balance: int):
|
def build_mock_validator(spec, i: int, balance: int):
|
||||||
|
@ -86,6 +86,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
|
||||||
elif spec.fork == EIP6110:
|
elif spec.fork == EIP6110:
|
||||||
previous_version = spec.config.DENEB_FORK_VERSION
|
previous_version = spec.config.DENEB_FORK_VERSION
|
||||||
current_version = spec.config.EIP6110_FORK_VERSION
|
current_version = spec.config.EIP6110_FORK_VERSION
|
||||||
|
elif spec.fork == WHISK:
|
||||||
|
previous_version = spec.config.CAPELLA_FORK_VERSION
|
||||||
|
current_version = spec.config.WHISK_FORK_VERSION
|
||||||
|
|
||||||
state = spec.BeaconState(
|
state = spec.BeaconState(
|
||||||
genesis_time=0,
|
genesis_time=0,
|
||||||
|
|
|
@ -4,3 +4,13 @@ from py_ecc.bls import G2ProofOfPossession as bls
|
||||||
privkeys = [i + 1 for i in range(32 * 256)]
|
privkeys = [i + 1 for i in range(32 * 256)]
|
||||||
pubkeys = [bls.SkToPk(privkey) for privkey in privkeys]
|
pubkeys = [bls.SkToPk(privkey) for privkey in privkeys]
|
||||||
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
|
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
|
||||||
|
|
||||||
|
MAX_KEYS = 32 * 256
|
||||||
|
whisk_ks_initial = [i for i in range(MAX_KEYS)]
|
||||||
|
# Must be unique among the set `whisk_ks_initial + whisk_ks_final`
|
||||||
|
whisk_ks_final = [MAX_KEYS + i for i in range(MAX_KEYS)]
|
||||||
|
|
||||||
|
known_whisk_trackers = {}
|
||||||
|
|
||||||
|
def register_known_whisk_tracker(k_r_G: bytes, index: int):
|
||||||
|
known_whisk_trackers[k_r_G] = index
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
from typing import Tuple
|
||||||
|
from eth_typing import BLSPubkey
|
||||||
|
from py_ecc.optimized_bls12_381.optimized_curve import G1, multiply
|
||||||
|
from py_ecc.bls.g2_primitives import (
|
||||||
|
G1_to_pubkey as py_ecc_G1_to_bytes48,
|
||||||
|
pubkey_to_G1 as py_ecc_bytes48_to_G1,
|
||||||
|
)
|
||||||
|
from curdleproofs import (
|
||||||
|
GenerateWhiskTrackerProof,
|
||||||
|
WhiskTracker,
|
||||||
|
IsValidWhiskOpeningProof,
|
||||||
|
GenerateWhiskShuffleProof,
|
||||||
|
IsValidWhiskShuffleProof
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_whisk_k_commitment(k: int) -> BLSPubkey:
|
||||||
|
return py_ecc_G1_to_bytes48(multiply(G1, int(k)))
|
||||||
|
|
||||||
|
def get_whisk_tracker(k: int, r: int) -> WhiskTracker:
|
||||||
|
r_G = multiply(G1, int(r))
|
||||||
|
k_r_G = multiply(r_G, int(k))
|
||||||
|
return WhiskTracker(py_ecc_G1_to_bytes48(r_G), py_ecc_G1_to_bytes48(k_r_G))
|
||||||
|
|
||||||
|
def get_whisk_tracker_and_commitment(k: int, r: int) -> Tuple[WhiskTracker, BLSPubkey]:
|
||||||
|
k_G = multiply(G1, int(k))
|
||||||
|
r_G = multiply(G1, int(r))
|
||||||
|
k_r_G = multiply(r_G, int(k))
|
||||||
|
tracker = WhiskTracker(py_ecc_G1_to_bytes48(r_G), py_ecc_G1_to_bytes48(k_r_G))
|
||||||
|
commitment = py_ecc_G1_to_bytes48(k_G)
|
||||||
|
return tracker, commitment
|
||||||
|
|
||||||
|
# Trigger condition for first proposal
|
||||||
|
def set_as_first_proposal(spec, state, proposer_index: int):
|
||||||
|
# Ensure tracker is empty to prevent breaking it
|
||||||
|
assert state.validators[proposer_index].whisk_tracker.r_G == spec.BLSG1Point()
|
||||||
|
state.validators[proposer_index].whisk_tracker.r_G = spec.BLS_G1_GENERATOR
|
||||||
|
|
||||||
|
|
||||||
|
def is_first_proposal(spec, state, proposer_index: int) -> bool:
|
||||||
|
return state.validators[proposer_index].whisk_tracker.r_G == spec.BLS_G1_GENERATOR
|
||||||
|
|
||||||
|
def set_registration(body, k: int, r: int):
|
||||||
|
tracker, k_commitment = get_whisk_tracker_and_commitment(k, r)
|
||||||
|
body.whisk_registration_proof = GenerateWhiskTrackerProof(tracker, k)
|
||||||
|
body.whisk_tracker = tracker
|
||||||
|
body.whisk_k_commitment = k_commitment
|
||||||
|
|
||||||
|
def set_opening_proof(spec, state, block, proposer_index: int, k: int, r: int):
|
||||||
|
tracker, k_commitment = get_whisk_tracker_and_commitment(k, r)
|
||||||
|
state.whisk_proposer_trackers[state.slot % spec.WHISK_PROPOSER_TRACKERS_COUNT] = tracker
|
||||||
|
state.validators[proposer_index].whisk_k_commitment = k_commitment
|
||||||
|
block.proposer_index = proposer_index
|
||||||
|
block.body.whisk_opening_proof = GenerateWhiskTrackerProof(tracker, k)
|
|
@ -0,0 +1,148 @@
|
||||||
|
from eth2spec.test.context import spec_state_test, with_whisk_and_later, expect_assertion_error
|
||||||
|
from eth2spec.test.helpers.keys import whisk_ks_initial
|
||||||
|
from eth2spec.test.helpers.whisk import get_whisk_tracker
|
||||||
|
from curdleproofs import GenerateWhiskShuffleProof
|
||||||
|
|
||||||
|
def set_correct_shuffle_proofs(spec, state, body):
|
||||||
|
pre_shuffle_trackers = get_and_populate_pre_shuffle_trackers(spec, state, body)
|
||||||
|
|
||||||
|
post_trackers, m, shuffle_proof = GenerateWhiskShuffleProof(spec.CURDLEPROOFS_CRS, pre_shuffle_trackers)
|
||||||
|
body.whisk_post_shuffle_trackers = post_trackers
|
||||||
|
body.whisk_shuffle_proof = shuffle_proof
|
||||||
|
body.whisk_shuffle_proof_M_commitment = m
|
||||||
|
|
||||||
|
def get_and_populate_pre_shuffle_trackers(spec, state, body):
|
||||||
|
shuffle_indices = spec.get_shuffle_indices(body.randao_reveal)
|
||||||
|
pre_shuffle_trackers = []
|
||||||
|
for i in shuffle_indices:
|
||||||
|
# Set r to some value > 1 ( = 2+i)
|
||||||
|
tracker = get_whisk_tracker(whisk_ks_initial[i], 2 + i)
|
||||||
|
state.whisk_candidate_trackers[i] = tracker
|
||||||
|
pre_shuffle_trackers.append(tracker)
|
||||||
|
return pre_shuffle_trackers
|
||||||
|
|
||||||
|
def get_pre_shuffle_trackers(spec, state, body):
|
||||||
|
return [state.whisk_candidate_trackers[i] for i in spec.get_shuffle_indices(body.randao_reveal)]
|
||||||
|
|
||||||
|
def set_state_epoch(spec, state, epoch):
|
||||||
|
state.slot = epoch * spec.SLOTS_PER_EPOCH
|
||||||
|
|
||||||
|
def set_state_epoch_selection_gap(spec, state):
|
||||||
|
set_state_epoch(spec, state, spec.WHISK_EPOCHS_PER_SHUFFLING_PHASE - 1)
|
||||||
|
|
||||||
|
def empty_block_body(spec):
|
||||||
|
return spec.BeaconBlockBody()
|
||||||
|
|
||||||
|
def run_process_shuffled_trackers(spec, state, body, valid=True):
|
||||||
|
yield 'pre', state
|
||||||
|
yield 'body', body
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
expect_assertion_error(lambda: spec.process_shuffled_trackers(state, body))
|
||||||
|
yield 'post', None
|
||||||
|
return
|
||||||
|
|
||||||
|
spec.process_shuffled_trackers(state, body)
|
||||||
|
|
||||||
|
yield 'post', state
|
||||||
|
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_shuffle_trackers(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_correct_shuffle_proofs(spec, state, body)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_no_shuffle_minus_selection_gap(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_state_epoch_selection_gap(spec, state)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_no_shuffle_minus_one_and_selection_gap(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_state_epoch(spec, state, spec.WHISK_EPOCHS_PER_SHUFFLING_PHASE - spec.WHISK_PROPOSER_SELECTION_GAP - 1)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_shuffle_during_selection_gap(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_correct_shuffle_proofs(spec, state, body)
|
||||||
|
set_state_epoch_selection_gap(spec, state)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
# Invalid cases on shuffle
|
||||||
|
# - wrong m
|
||||||
|
# - wrong proof
|
||||||
|
# - wrong post shuffle
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_invalid_shuffle_bad_m(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_correct_shuffle_proofs(spec, state, body)
|
||||||
|
body.whisk_shuffle_proof_M_commitment = spec.BLSG1Point()
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_invalid_shuffle_bad_proof(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_correct_shuffle_proofs(spec, state, body)
|
||||||
|
body.whisk_shuffle_proof = spec.WhiskShuffleProof()
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_invalid_shuffle_bad_trackers_zero(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_correct_shuffle_proofs(spec, state, body)
|
||||||
|
body.whisk_post_shuffle_trackers[0] = spec.WhiskTracker()
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
# TODO: This test does not fail
|
||||||
|
# @with_whisk_and_later
|
||||||
|
# @spec_state_test
|
||||||
|
# def test_invalid_shuffle_bad_trackers_swap(spec, state):
|
||||||
|
# body = empty_block_body(spec)
|
||||||
|
# set_correct_shuffle_proofs(spec, state, body)
|
||||||
|
# assert body.whisk_post_shuffle_trackers[0] != body.whisk_post_shuffle_trackers[1]
|
||||||
|
# tracker_0 = body.whisk_post_shuffle_trackers[0]
|
||||||
|
# body.whisk_post_shuffle_trackers[0] = body.whisk_post_shuffle_trackers[1]
|
||||||
|
# body.whisk_post_shuffle_trackers[1] = tracker_0
|
||||||
|
# yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
# Invalid things on gap
|
||||||
|
# - not empty shuffle trackers
|
||||||
|
# - not empty m
|
||||||
|
# - not empty proof
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_invalid_gap_non_zero_m(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
body.whisk_shuffle_proof_M_commitment = spec.BLSG1Point('0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
|
||||||
|
set_state_epoch_selection_gap(spec, state)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_invalid_gap_non_zero_proof(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
body.whisk_shuffle_proof = spec.WhiskShuffleProof('0xff')
|
||||||
|
set_state_epoch_selection_gap(spec, state)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_invalid_gap_non_zero_trackers(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
body.whisk_post_shuffle_trackers = get_and_populate_pre_shuffle_trackers(spec, state, body)
|
||||||
|
set_state_epoch_selection_gap(spec, state)
|
||||||
|
yield from run_process_shuffled_trackers(spec, state, body, valid=False)
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
from eth2spec.test.context import spec_state_test, with_whisk_and_later, expect_assertion_error
|
||||||
|
from eth2spec.test.helpers.whisk import (
|
||||||
|
get_whisk_k_commitment,
|
||||||
|
get_whisk_tracker,
|
||||||
|
set_opening_proof
|
||||||
|
)
|
||||||
|
|
||||||
|
def empty_block(spec):
|
||||||
|
return spec.BeaconBlock()
|
||||||
|
|
||||||
|
def run_process_whisk_opening_proof(spec, state, block, valid=True):
|
||||||
|
yield 'pre', state
|
||||||
|
yield 'block', block
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
expect_assertion_error(lambda: spec.process_whisk_opening_proof(state, block))
|
||||||
|
yield 'post', None
|
||||||
|
return
|
||||||
|
|
||||||
|
spec.process_whisk_opening_proof(state, block)
|
||||||
|
|
||||||
|
yield 'post', state
|
||||||
|
|
||||||
|
PROPOSER_INDEX = 0
|
||||||
|
K_OK = 2
|
||||||
|
K_WRONG = 3
|
||||||
|
R_OK = 2
|
||||||
|
R_WRONG = 3
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_valid_proof(spec, state):
|
||||||
|
block = empty_block(spec)
|
||||||
|
set_opening_proof(spec, state, block, PROPOSER_INDEX, K_OK, R_OK)
|
||||||
|
run_process_whisk_opening_proof(spec, state, block)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_wrong_commitment(spec, state):
|
||||||
|
block = empty_block(spec)
|
||||||
|
set_opening_proof(spec, state, block, PROPOSER_INDEX, K_OK, R_OK)
|
||||||
|
state.validators[PROPOSER_INDEX].whisk_k_commitment = get_whisk_k_commitment(K_WRONG)
|
||||||
|
run_process_whisk_opening_proof(spec, state, block, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_wrong_tracker_r(spec, state):
|
||||||
|
block = empty_block(spec)
|
||||||
|
set_opening_proof(spec, state, block, PROPOSER_INDEX, K_OK, R_OK)
|
||||||
|
state.whisk_proposer_trackers[state.slot % spec.WHISK_PROPOSER_TRACKERS_COUNT] = get_whisk_tracker(K_OK, R_WRONG)
|
||||||
|
run_process_whisk_opening_proof(spec, state, block, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_wrong_proof(spec, state):
|
||||||
|
block = empty_block(spec)
|
||||||
|
set_opening_proof(spec, state, block, PROPOSER_INDEX, K_OK, R_OK)
|
||||||
|
block.body.whisk_opening_proof = spec.WhiskTrackerProof()
|
||||||
|
run_process_whisk_opening_proof(spec, state, block, valid=False)
|
|
@ -0,0 +1,90 @@
|
||||||
|
from eth2spec.test.context import spec_state_test, with_whisk_and_later, expect_assertion_error
|
||||||
|
from eth2spec.test.helpers.keys import whisk_ks_initial, whisk_ks_final
|
||||||
|
from eth2spec.test.helpers.whisk import get_whisk_tracker, set_as_first_proposal, get_whisk_k_commitment, set_registration
|
||||||
|
from curdleproofs import GenerateWhiskShuffleProof
|
||||||
|
|
||||||
|
def empty_block_body(spec):
|
||||||
|
return spec.BeaconBlockBody()
|
||||||
|
|
||||||
|
def set_as_first_proposal_and_proposer(spec, state, proposer_index):
|
||||||
|
state.latest_block_header.proposer_index = proposer_index
|
||||||
|
set_as_first_proposal(spec, state, proposer_index)
|
||||||
|
|
||||||
|
def run_process_whisk_registration(spec, state, body, valid=True):
|
||||||
|
yield 'pre', state
|
||||||
|
yield 'body', body
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
expect_assertion_error(lambda: spec.process_whisk_registration(state, body))
|
||||||
|
yield 'post', None
|
||||||
|
return
|
||||||
|
|
||||||
|
spec.process_whisk_registration(state, body)
|
||||||
|
|
||||||
|
yield 'post', state
|
||||||
|
|
||||||
|
IDENTITY_R = 1
|
||||||
|
OTHER_R = 2
|
||||||
|
PROPOSER_INDEX = 0
|
||||||
|
OTHER_INDEX = 1
|
||||||
|
OTHER_K = 2
|
||||||
|
|
||||||
|
# First proposal
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_first_proposal_ok(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_as_first_proposal_and_proposer(spec, state, PROPOSER_INDEX)
|
||||||
|
set_registration(body, OTHER_K, OTHER_R)
|
||||||
|
yield from run_process_whisk_registration(spec, state, body)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_first_proposal_indentity_tracker(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_as_first_proposal_and_proposer(spec, state, PROPOSER_INDEX)
|
||||||
|
set_registration(body, OTHER_K, IDENTITY_R)
|
||||||
|
yield from run_process_whisk_registration(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_first_proposal_non_unique_k_other(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_as_first_proposal_and_proposer(spec, state, PROPOSER_INDEX)
|
||||||
|
state.validators[OTHER_INDEX].whisk_k_commitment = get_whisk_k_commitment(OTHER_K)
|
||||||
|
set_registration(body, OTHER_K, OTHER_R)
|
||||||
|
yield from run_process_whisk_registration(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_first_proposal_non_unique_k_self(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_as_first_proposal_and_proposer(spec, state, PROPOSER_INDEX)
|
||||||
|
state.validators[PROPOSER_INDEX].whisk_k_commitment = get_whisk_k_commitment(OTHER_K)
|
||||||
|
set_registration(body, OTHER_K, OTHER_R)
|
||||||
|
yield from run_process_whisk_registration(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_first_proposal_invalid_proof(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_as_first_proposal_and_proposer(spec, state, PROPOSER_INDEX)
|
||||||
|
set_registration(body, OTHER_K, OTHER_R)
|
||||||
|
body.whisk_tracker.k_r_G = spec.BLSG1Point()
|
||||||
|
yield from run_process_whisk_registration(spec, state, body, valid=False)
|
||||||
|
|
||||||
|
# Second proposal
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_second_proposal_ok(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
yield from run_process_whisk_registration(spec, state, body)
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_second_proposal_not_zero(spec, state):
|
||||||
|
body = empty_block_body(spec)
|
||||||
|
set_registration(body, OTHER_K, OTHER_R)
|
||||||
|
yield from run_process_whisk_registration(spec, state, body, valid=False)
|
|
@ -0,0 +1 @@
|
||||||
|
from .test_whisk import * # noqa: F401 F403
|
|
@ -0,0 +1,58 @@
|
||||||
|
from eth2spec.test.helpers.block import (
|
||||||
|
build_empty_block_for_next_slot, build_empty_block
|
||||||
|
)
|
||||||
|
from eth2spec.test.context import (
|
||||||
|
spec_state_test,
|
||||||
|
with_whisk_and_later,
|
||||||
|
WHISK,
|
||||||
|
)
|
||||||
|
from eth2spec.test.helpers.keys import privkeys, pubkeys, whisk_ks_initial
|
||||||
|
from eth2spec.test.helpers.state import (
|
||||||
|
state_transition_and_sign_block
|
||||||
|
)
|
||||||
|
from curdleproofs import IsValidWhiskShuffleProof
|
||||||
|
from eth2spec.test.helpers.whisk import is_first_proposal, get_whisk_tracker_and_commitment, set_as_first_proposal
|
||||||
|
from curdleproofs import WhiskTracker
|
||||||
|
|
||||||
|
known_whisk_trackers = {}
|
||||||
|
|
||||||
|
|
||||||
|
def assign_proposer_at_slot(state, slot: int):
|
||||||
|
state
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_whisk_full(spec, state):
|
||||||
|
# TODO: De-duplicate code from whisk/fork.md
|
||||||
|
for index, validator in enumerate(state.validators):
|
||||||
|
whisk_k_commitment, whisk_tracker = spec.get_initial_commitments(whisk_ks_initial[index])
|
||||||
|
validator.whisk_k_commitment = whisk_k_commitment
|
||||||
|
validator.whisk_tracker = whisk_tracker
|
||||||
|
|
||||||
|
# Do a candidate selection followed by a proposer selection so that we have proposers for the upcoming day
|
||||||
|
# Use an old epoch when selecting candidates so that we don't get the same seed as in the next candidate selection
|
||||||
|
spec.select_whisk_candidate_trackers(state, spec.Epoch(0))
|
||||||
|
spec.select_whisk_proposer_trackers(state, spec.Epoch(0))
|
||||||
|
|
||||||
|
# Fill candidate trackers with the same tracker so shuffling does not break
|
||||||
|
def fill_candidate_trackers(spec, state, tracker: WhiskTracker):
|
||||||
|
for i in range(spec.WHISK_CANDIDATE_TRACKERS_COUNT):
|
||||||
|
state.whisk_candidate_trackers[i] = tracker
|
||||||
|
|
||||||
|
@with_whisk_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_whisk__process_block_single_initial(spec, state):
|
||||||
|
assert state.slot == 0
|
||||||
|
proposer_slot_1 = 0
|
||||||
|
tracker_slot_1, k_commitment = get_whisk_tracker_and_commitment(whisk_ks_initial[proposer_slot_1], 1)
|
||||||
|
state.validators[proposer_slot_1].whisk_k_commitment = k_commitment
|
||||||
|
state.whisk_proposer_trackers[1] = tracker_slot_1
|
||||||
|
fill_candidate_trackers(spec, state, tracker_slot_1)
|
||||||
|
|
||||||
|
# Produce and process a whisk block
|
||||||
|
yield 'pre', state
|
||||||
|
|
||||||
|
block = build_empty_block(spec, state, 1, proposer_slot_1)
|
||||||
|
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||||
|
|
||||||
|
yield 'blocks', [signed_block]
|
||||||
|
yield 'post', state
|
Loading…
Reference in New Issue