77from lean_spec .subspecs .ssz .hash import hash_tree_root
88from lean_spec .subspecs .xmss .containers import PublicKey , SecretKey
99from lean_spec .subspecs .xmss .interface import (
10- TEST_SIGNATURE_SCHEME as DEFAULT_SIGNATURE_SCHEME ,
10+ TEST_SIGNATURE_SCHEME ,
11+ GeneralizedXmssScheme ,
1112)
1213from 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 #
0 commit comments