Skip to content

Commit 715276e

Browse files
authored
chore: remove default xmss signature scheme (leanEthereum#159)
1 parent 00b2139 commit 715276e

6 files changed

Lines changed: 32 additions & 30 deletions

File tree

packages/testing/src/consensus_testing/keys.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from lean_spec.subspecs.ssz.hash import hash_tree_root
88
from lean_spec.subspecs.xmss.containers import PublicKey, SecretKey
99
from lean_spec.subspecs.xmss.interface import (
10-
TEST_SIGNATURE_SCHEME as DEFAULT_SIGNATURE_SCHEME,
10+
TEST_SIGNATURE_SCHEME,
11+
GeneralizedXmssScheme,
1112
)
1213
from lean_spec.types import ValidatorIndex
1314

@@ -36,7 +37,11 @@ class XmssKeyManager:
3637
DEFAULT_MAX_SLOT = Slot(100)
3738
"""Default maximum slot horizon if not specified."""
3839

39-
def __init__(self, max_slot: Optional[Slot] = None) -> None:
40+
def __init__(
41+
self,
42+
max_slot: Optional[Slot] = None,
43+
scheme: GeneralizedXmssScheme = TEST_SIGNATURE_SCHEME,
44+
) -> None:
4045
"""
4146
Initialize the key manager.
4247
@@ -45,13 +50,17 @@ def __init__(self, max_slot: Optional[Slot] = None) -> None:
4550
max_slot : Slot, optional
4651
Highest slot number for which keys must remain valid.
4752
Defaults to `Slot(100)`.
53+
scheme : GeneralizedXmssScheme, optional
54+
The XMSS scheme to use.
55+
Defaults to `TEST_SIGNATURE_SCHEME`.
4856
4957
Notes:
5058
-----
5159
Internally, keys are stored in a single dictionary:
5260
`{ValidatorIndex → KeyPair}`.
5361
"""
5462
self.max_slot = max_slot if max_slot is not None else self.DEFAULT_MAX_SLOT
63+
self.scheme = scheme
5564
self._key_pairs: dict[ValidatorIndex, KeyPair] = {}
5665

5766
def __getitem__(self, validator_index: ValidatorIndex) -> KeyPair:
@@ -98,7 +107,7 @@ def __getitem__(self, validator_index: ValidatorIndex) -> KeyPair:
98107
# The seed is set to 0 for deterministic test keys.
99108
from lean_spec.types import Uint64
100109

101-
pk, sk = DEFAULT_SIGNATURE_SCHEME.key_gen(Uint64(0), Uint64(num_active_epochs))
110+
pk, sk = self.scheme.key_gen(Uint64(0), Uint64(num_active_epochs))
102111

103112
# Store as a cohesive unit and return.
104113
key_pair = KeyPair(public=pk, secret=sk)
@@ -139,27 +148,22 @@ def sign_attestation(self, attestation: Attestation) -> Signature:
139148
# Each slot gets its own epoch to avoid key reuse.
140149
epoch = attestation.data.slot
141150

142-
# Advance the key's prepared window until it covers the target epoch.
143-
#
144-
# We use the scheme that the key was generated with.
145-
scheme = DEFAULT_SIGNATURE_SCHEME
146-
147151
# Loop until the epoch is inside the prepared interval
148-
prepared_interval = scheme.get_prepared_interval(sk)
152+
prepared_interval = self.scheme.get_prepared_interval(sk)
149153
while int(epoch) not in prepared_interval:
150154
# Check if we're advancing past the key's total lifetime
151-
activation_interval = scheme.get_activation_interval(sk)
155+
activation_interval = self.scheme.get_activation_interval(sk)
152156
if prepared_interval.stop >= activation_interval.stop:
153157
raise ValueError(
154158
f"Cannot sign for epoch {epoch}: "
155159
f"it is beyond the key's max lifetime {activation_interval.stop}"
156160
)
157161

158162
# Advance the key and get the new key object
159-
sk = scheme.advance_preparation(sk)
163+
sk = self.scheme.advance_preparation(sk)
160164

161165
# Update the prepared interval for the next loop check
162-
prepared_interval = scheme.get_prepared_interval(sk)
166+
prepared_interval = self.scheme.get_prepared_interval(sk)
163167

164168
# Update the cached key pair with the new, advanced secret key.
165169
# This ensures the *next* call to sign() uses the advanced state.
@@ -171,10 +175,10 @@ def sign_attestation(self, attestation: Attestation) -> Signature:
171175
message = bytes(hash_tree_root(attestation))
172176

173177
# Generate the XMSS signature using the validator's (now prepared) secret key.
174-
xmss_sig = scheme.sign(sk, epoch, message)
178+
xmss_sig = self.scheme.sign(sk, epoch, message)
175179

176180
# Convert the signature to the wire format (byte array).
177-
signature_bytes = xmss_sig.to_bytes(scheme.config)
181+
signature_bytes = xmss_sig.to_bytes(self.scheme.config)
178182

179183
# Ensure the signature meets the consensus spec length (3100 bytes).
180184
#

packages/testing/src/consensus_testing/test_fixtures/fork_choice.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from lean_spec.subspecs.containers.state.state import State
2828
from lean_spec.subspecs.forkchoice import Store
2929
from lean_spec.subspecs.ssz import hash_tree_root
30-
from lean_spec.subspecs.xmss.interface import DEFAULT_SIGNATURE_SCHEME
30+
from lean_spec.subspecs.xmss.interface import TEST_SIGNATURE_SCHEME
3131
from lean_spec.types import Bytes32, Uint64, ValidatorIndex
3232

3333
from ..keys import XmssKeyManager
@@ -186,15 +186,15 @@ def make_fixture(self) -> ForkChoiceTest:
186186
key_manager = (
187187
shared_key_manager
188188
if self.max_slot <= shared_key_manager.max_slot
189-
else XmssKeyManager(max_slot=self.max_slot)
189+
else XmssKeyManager(max_slot=self.max_slot, scheme=TEST_SIGNATURE_SCHEME)
190190
)
191191

192192
# Update validator pubkeys to match key_manager's generated keys
193193
updated_validators = [
194194
validator.model_copy(
195195
update={
196196
"pubkey": key_manager[ValidatorIndex(i)].public.to_bytes(
197-
DEFAULT_SIGNATURE_SCHEME.config
197+
key_manager.scheme.config
198198
)
199199
}
200200
)

src/lean_spec/subspecs/containers/signature.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66

77
from ..xmss.containers import PublicKey
88
from ..xmss.containers import Signature as XmssSignature
9-
from ..xmss.interface import DEFAULT_SIGNATURE_SCHEME
9+
from ..xmss.interface import TEST_SIGNATURE_SCHEME, GeneralizedXmssScheme
1010

1111

1212
class Signature(Bytes3100):
1313
"""Represents aggregated signature produced by the leanVM (SNARKs in the future)."""
1414

15-
def verify(self, public_key: PublicKey, epoch: Uint64, message: bytes) -> bool:
15+
def verify(
16+
self,
17+
public_key: PublicKey,
18+
epoch: Uint64,
19+
message: bytes,
20+
scheme: GeneralizedXmssScheme = TEST_SIGNATURE_SCHEME,
21+
) -> bool:
1622
"""Verify the signature using XMSS verification algorithm."""
1723
try:
18-
scheme = DEFAULT_SIGNATURE_SCHEME
1924
# Signature container is always 3100 bytes, but scheme config may expect less.
2025
# Slice to the expected size if needed, assumes padding to the right.
2126
signature_data = bytes(self)[: scheme.config.SIGNATURE_LEN_BYTES]

src/lean_spec/subspecs/containers/validator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from lean_spec.types import Bytes52, Container
44

55
from ..xmss.containers import PublicKey
6-
from ..xmss.interface import DEFAULT_SIGNATURE_SCHEME
6+
from ..xmss.interface import TEST_SIGNATURE_SCHEME, GeneralizedXmssScheme
77

88

99
class Validator(Container):
@@ -12,6 +12,6 @@ class Validator(Container):
1212
pubkey: Bytes52
1313
"""XMSS one-time signature public key."""
1414

15-
def get_pubkey(self) -> PublicKey:
15+
def get_pubkey(self, scheme: GeneralizedXmssScheme = TEST_SIGNATURE_SCHEME) -> PublicKey:
1616
"""Get the XMSS public key from this validator."""
17-
return PublicKey.from_bytes(bytes(self.pubkey), DEFAULT_SIGNATURE_SCHEME.config)
17+
return PublicKey.from_bytes(bytes(self.pubkey), scheme.config)

src/lean_spec/subspecs/xmss/interface.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,3 @@ def advance_preparation(self, sk: SecretKey) -> SecretKey:
589589
TEST_RAND,
590590
)
591591
"""A lightweight instance for test environments."""
592-
593-
DEFAULT_SIGNATURE_SCHEME = PROD_SIGNATURE_SCHEME
594-
"""The default signature scheme to use."""

tests/conftest.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,3 @@
77
# Create a profile named "no_deadline" with deadline disabled.
88
settings.register_profile("no_deadline", deadline=None)
99
settings.load_profile("no_deadline")
10-
11-
# Enable test mode for XMSS signature scheme
12-
# This uses smaller parameters (TEST_CONFIG) for faster test execution
13-
xmss.interface.DEFAULT_SIGNATURE_SCHEME = xmss.interface.TEST_SIGNATURE_SCHEME

0 commit comments

Comments
 (0)