Update curdleproofs usage

This commit is contained in:
dapplion 2023-07-24 18:41:38 +02:00
parent c0c453df2a
commit 211376e35c
7 changed files with 48 additions and 55 deletions

View File

@ -11,6 +11,7 @@ class WhiskSpecBuilder(BaseSpecBuilder):
return f'''
from eth2spec.capella import {preset_name} as capella
import curdleproofs
from py_arkworks_bls12381 import G1Point
'''
@classmethod

View File

@ -263,7 +263,7 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr
constant_vars['CURDLEPROOFS_CRS_G1'] = VariableDefinition(constant_vars['CURDLEPROOFS_CRS_G1'].value, str(ALL_KZG_SETUPS['mainnet'][0][0:crs_len]), "noqa: E501", None)
constant_vars['CURDLEPROOFS_CRS'] = VariableDefinition(
None,
"curdleproofs.CurdleproofsCrs.from_points_compressed(WHISK_VALIDATORS_PER_SHUFFLE, CURDLEPROOFS_N_BLINDERS, CURDLEPROOFS_CRS_G1)",
"curdleproofs.CurdleproofsCrs.from_random_points(WHISK_VALIDATORS_PER_SHUFFLE, CURDLEPROOFS_N_BLINDERS, [G1Point.from_compressed_bytes_unchecked(p) for p in CURDLEPROOFS_CRS_G1])",
"noqa: E501", None
)
constant_vars['BLS_G1_GENERATOR'] = VariableDefinition(

View File

@ -100,7 +100,6 @@ Note that Curdleproofs (Whisk Shuffle Proofs), the tracker opening proofs and al
```python
def IsValidWhiskShuffleProof(pre_shuffle_trackers: Sequence[WhiskTracker],
post_shuffle_trackers: Sequence[WhiskTracker],
M: BLSG1Point,
shuffle_proof: WhiskShuffleProof) -> bool:
"""
Verify `post_shuffle_trackers` is a permutation of `pre_shuffle_trackers`.
@ -110,7 +109,6 @@ def IsValidWhiskShuffleProof(pre_shuffle_trackers: Sequence[WhiskTracker],
CURDLEPROOFS_CRS,
pre_shuffle_trackers,
post_shuffle_trackers,
M,
shuffle_proof,
)
```
@ -299,7 +297,6 @@ class BeaconBlockBody(Container):
whisk_opening_proof: WhiskTrackerProof # [New in Whisk]
whisk_post_shuffle_trackers: Vector[WhiskTracker, WHISK_VALIDATORS_PER_SHUFFLE] # [New in Whisk]
whisk_shuffle_proof: WhiskShuffleProof # [New in Whisk]
whisk_shuffle_proof_M_commitment: BLSG1Point # [New in Whisk]
whisk_registration_proof: WhiskTrackerProof # [New in Whisk]
whisk_tracker: WhiskTracker # [New in Whisk]
whisk_k_commitment: BLSG1Point # k * BLS_G1_GENERATOR [New in Whisk]
@ -330,7 +327,6 @@ def process_shuffled_trackers(state: BeaconState, body: BeaconBlockBody) -> None
if shuffle_epoch + WHISK_PROPOSER_SELECTION_GAP + 1 >= WHISK_EPOCHS_PER_SHUFFLING_PHASE:
# Require trackers set to zero during cooldown
assert body.whisk_post_shuffle_trackers == Vector[WhiskTracker, WHISK_VALIDATORS_PER_SHUFFLE]()
assert body.whisk_shuffle_proof_M_commitment == BLSG1Point()
assert body.whisk_shuffle_proof == WhiskShuffleProof()
post_shuffle_trackers = pre_shuffle_trackers
else:
@ -338,7 +334,6 @@ def process_shuffled_trackers(state: BeaconState, body: BeaconBlockBody) -> None
assert IsValidWhiskShuffleProof(
pre_shuffle_trackers,
body.whisk_post_shuffle_trackers,
body.whisk_shuffle_proof_M_commitment,
body.whisk_shuffle_proof,
)
post_shuffle_trackers = body.whisk_post_shuffle_trackers

View File

@ -22,6 +22,7 @@ from eth2spec.test.helpers.whisk import (
is_first_proposal,
resolve_known_tracker
)
from py_arkworks_bls12381 import Scalar
PointProjective = Optimized_Point3D[Optimized_Field]
@ -139,7 +140,7 @@ def build_empty_block(spec, state, slot=None, proposer_index=None):
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)
empty_block.body.whisk_opening_proof = GenerateWhiskTrackerProof(proposer_tracker, Scalar(k_initial))
if not IsValidWhiskOpeningProof(proposer_tracker, proposer_k_commitment, empty_block.body.whisk_opening_proof):
raise Exception(
"produced opening proof is not valid",
@ -154,23 +155,20 @@ def build_empty_block(spec, state, slot=None, proposer_index=None):
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)
post_trackers, 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,
)
@ -184,7 +182,7 @@ def build_empty_block(spec, state, slot=None, proposer_index=None):
# TODO: Actual logic should pick a random r, but may need to do something fancy to locate trackers quickly
r = 2
tracker, k_commitment = compute_whisk_tracker_and_commitment(k_final, r)
empty_block.body.whisk_registration_proof = GenerateWhiskTrackerProof(tracker, k_final)
empty_block.body.whisk_registration_proof = GenerateWhiskTrackerProof(tracker, Scalar(k_final))
empty_block.body.whisk_tracker = tracker
empty_block.body.whisk_k_commitment = k_commitment

View File

@ -1,9 +1,8 @@
from typing import Tuple, Optional
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
from curdleproofs import GenerateWhiskTrackerProof, WhiskTracker
from eth2spec.test.helpers.keys import whisk_ks_initial
from py_arkworks_bls12381 import G1Point, Scalar
# Map of validator index to initial WhiskTracker (r = 1, k = index)
@ -14,6 +13,9 @@ whisk_initial_k_commitment_cache_by_index = {}
whisk_initial_tracker_cache_by_k_r_G = {}
INITIAL_R = 1
# Generator
G1 = G1Point()
def compute_whisk_initial_tracker_cached(i: int) -> WhiskTracker:
if i in whisk_initial_tracker_cache_by_index:
@ -41,39 +43,50 @@ def resolve_known_tracker(tracker: WhiskTracker) -> Optional[int]:
return None
def g1point_to_bytes(point: G1Point) -> bytes:
return bytes(point.to_compressed_bytes())
def compute_whisk_k_commitment(k: int) -> BLSPubkey:
return py_ecc_G1_to_bytes48(multiply(G1, int(k)))
return g1point_to_bytes(G1 * Scalar(k))
def compute_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))
r_G = G1 * Scalar(r)
k_r_G = r_G * Scalar(k)
return WhiskTracker(g1point_to_bytes(r_G), g1point_to_bytes(k_r_G))
def compute_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)
k_G = G1 * Scalar(k)
r_G = G1 * Scalar(r)
k_r_G = r_G * Scalar(k)
tracker = WhiskTracker(g1point_to_bytes(r_G), g1point_to_bytes(k_r_G))
commitment = g1point_to_bytes(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.whisk_trackers[proposer_index].r_G == spec.BLSG1Point()
state.whisk_trackers[proposer_index].r_G = spec.BLS_G1_GENERATOR
if state.whisk_trackers[proposer_index].r_G != spec.BLS_G1_GENERATOR:
# Ensure tracker is empty to prevent breaking it
assert state.whisk_trackers[proposer_index].r_G == spec.BLSG1Point()
state.whisk_trackers[proposer_index].r_G = spec.BLS_G1_GENERATOR
def is_first_proposal(spec, state, proposer_index: int) -> bool:
return state.whisk_trackers[proposer_index].r_G == spec.BLS_G1_GENERATOR
def register_tracker(state, proposer_index: int, k: int, r: int):
tracker, k_commitment = compute_whisk_tracker_and_commitment(k, r)
state.whisk_trackers[proposer_index] = tracker
state.whisk_k_commitments[proposer_index] = k_commitment
def set_registration(body, k: int, r: int):
tracker, k_commitment = compute_whisk_tracker_and_commitment(k, r)
body.whisk_registration_proof = GenerateWhiskTrackerProof(tracker, k)
body.whisk_registration_proof = GenerateWhiskTrackerProof(tracker, Scalar(k))
body.whisk_tracker = tracker
body.whisk_k_commitment = k_commitment
@ -83,4 +96,4 @@ def set_opening_proof(spec, state, block, proposer_index: int, k: int, r: int):
state.whisk_proposer_trackers[state.slot % spec.WHISK_PROPOSER_TRACKERS_COUNT] = tracker
state.whisk_k_commitments[proposer_index] = k_commitment
block.proposer_index = proposer_index
block.body.whisk_opening_proof = GenerateWhiskTrackerProof(tracker, k)
block.body.whisk_opening_proof = GenerateWhiskTrackerProof(tracker, Scalar(k))

View File

@ -7,10 +7,9 @@ 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)
post_trackers, 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):
@ -87,20 +86,10 @@ def test_shuffle_during_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):
@ -132,21 +121,9 @@ def test_invalid_shuffle_bad_trackers_zero(spec, state):
# 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):

View File

@ -1,5 +1,10 @@
from eth2spec.test.context import spec_state_test, with_whisk_and_later, expect_assertion_error
from eth2spec.test.helpers.whisk import set_as_first_proposal, compute_whisk_k_commitment, set_registration
from eth2spec.test.helpers.whisk import (
set_as_first_proposal,
compute_whisk_k_commitment,
set_registration,
register_tracker
)
def empty_block_body(spec):
@ -26,10 +31,10 @@ def run_process_whisk_registration(spec, state, body, valid=True):
IDENTITY_R = 1
OTHER_R = 2
OTHER_R = 100_000_2 # Large enough values to not collide with initial k values
OTHER_K = 100_000_2
PROPOSER_INDEX = 0
OTHER_INDEX = 1
OTHER_K = 2
# First proposal
@ -88,6 +93,9 @@ def test_first_proposal_invalid_proof(spec, state):
@spec_state_test
def test_second_proposal_ok(spec, state):
body = empty_block_body(spec)
# An empty body has the correct values for a second proposal
# Set tracker to != G1 generator for second proposal condition
register_tracker(state, PROPOSER_INDEX, OTHER_K, OTHER_R)
yield from run_process_whisk_registration(spec, state, body)
@ -96,4 +104,5 @@ def test_second_proposal_ok(spec, state):
def test_second_proposal_not_zero(spec, state):
body = empty_block_body(spec)
set_registration(body, OTHER_K, OTHER_R)
register_tracker(state, PROPOSER_INDEX, OTHER_K, OTHER_R)
yield from run_process_whisk_registration(spec, state, body, valid=False)