Skip to content

Commit e62c4f0

Browse files
authored
test fixtures: use pre generated keys (#216)
* test fixtures: use pre generated keys * more pythonic
1 parent d717c2d commit e62c4f0

20 files changed

Lines changed: 497 additions & 392 deletions

File tree

packages/testing/src/consensus_testing/keys.py

Lines changed: 232 additions & 175 deletions
Large diffs are not rendered by default.

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,9 @@
2828
from lean_spec.subspecs.koalabear import Fp
2929
from lean_spec.subspecs.ssz import hash_tree_root
3030
from lean_spec.subspecs.xmss.constants import PROD_CONFIG
31-
from lean_spec.subspecs.xmss.containers import (
32-
HashDigestList,
33-
HashTreeOpening,
34-
Randomness,
35-
Signature,
36-
)
31+
from lean_spec.subspecs.xmss.containers import Signature
3732
from lean_spec.subspecs.xmss.interface import TEST_SIGNATURE_SCHEME
33+
from lean_spec.subspecs.xmss.types import HashDigestList, HashTreeOpening, Randomness
3834
from lean_spec.types import Bytes32, Uint64, ValidatorIndex
3935

4036
from ..keys import XmssKeyManager

packages/testing/src/consensus_testing/test_keys.json

Lines changed: 50 additions & 0 deletions
Large diffs are not rendered by default.

src/lean_spec/subspecs/xmss/__init__.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@
66
"""
77

88
from .constants import PROD_CONFIG, TEST_CONFIG
9-
from .containers import (
10-
HashTreeOpening,
11-
PublicKey,
12-
SecretKey,
13-
Signature,
14-
)
9+
from .containers import PublicKey, SecretKey, Signature
1510
from .interface import GeneralizedXmssScheme
11+
from .types import HashTreeOpening
1612

1713
__all__ = [
1814
"GeneralizedXmssScheme",

src/lean_spec/subspecs/xmss/containers.py

Lines changed: 18 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,177 +1,28 @@
1-
"""Defines the data containers for the Generalized XMSS signature scheme."""
1+
"""
2+
Data containers for the Generalized XMSS signature scheme.
3+
4+
This module defines the high-level containers: PublicKey, Signature, and SecretKey.
5+
Base types (HashDigestVector, Parameter, etc.) are defined in types.py.
6+
"""
27

38
from __future__ import annotations
49

510
from typing import TYPE_CHECKING
611

7-
from lean_spec.subspecs.koalabear import Fp
8-
912
from ...types import Uint64
10-
from ...types.byte_arrays import BaseBytes
11-
from ...types.collections import SSZList, SSZVector
1213
from ...types.container import Container
13-
from .constants import PRF_KEY_LENGTH, PROD_CONFIG
14+
from .subtree import HashSubTree
15+
from .types import (
16+
HashDigestList,
17+
HashDigestVector,
18+
HashTreeOpening,
19+
Parameter,
20+
PRFKey,
21+
Randomness,
22+
)
1423

1524
if TYPE_CHECKING:
1625
from .interface import GeneralizedXmssScheme
17-
from .subtree import HashSubTree
18-
19-
20-
class PRFKey(BaseBytes):
21-
"""
22-
The PRF master secret key.
23-
24-
This is a high-entropy byte string that acts as the single root secret from
25-
which all one-time signing keys are deterministically derived.
26-
"""
27-
28-
LENGTH = PRF_KEY_LENGTH
29-
30-
31-
HASH_DIGEST_LENGTH = PROD_CONFIG.HASH_LEN_FE
32-
"""
33-
The fixed length of a hash digest in field elements.
34-
35-
Derived from `PROD_CONFIG.HASH_LEN_FE`. This corresponds to the output length
36-
of the Poseidon2 hash function used in the XMSS scheme.
37-
38-
TODO: Make the configuration generic and don't hardcode `PROD_CONFIG`.
39-
"""
40-
41-
# Calculate the maximum number of nodes in a sparse Merkle tree layer:
42-
# - A bottom tree has at most 2^(LOG_LIFETIME/2) leaves
43-
# - With padding, we may add up to 2 additional nodes
44-
# - To be generous and future-proof, we use 2^(LOG_LIFETIME/2 + 1)
45-
NODE_LIST_LIMIT = 1 << (PROD_CONFIG.LOG_LIFETIME // 2 + 1)
46-
"""
47-
The maximum number of nodes that can be stored in a sparse Merkle tree layer.
48-
49-
Calculated as `2^(LOG_LIFETIME/2 + 1)` from PROD_CONFIG to accommodate:
50-
- Bottom trees with up to `2^(LOG_LIFETIME/2)` nodes
51-
- Padding overhead (up to 2 additional nodes)
52-
- Future-proofing with 2x margin
53-
54-
TODO: Make the configuration generic and don't hardcode `PROD_CONFIG`.
55-
"""
56-
57-
58-
class HashDigestVector(SSZVector):
59-
"""
60-
A single hash digest represented as a fixed-size vector of field elements.
61-
62-
This is the SSZ-compliant representation of a Poseidon2 hash output.
63-
In SSZ notation: `Vector[Fp, HASH_DIGEST_LENGTH]`
64-
65-
The fixed size enables efficient serialization when used in collections,
66-
as SSZ can pack these back-to-back without per-element offsets.
67-
"""
68-
69-
ELEMENT_TYPE = Fp
70-
LENGTH = HASH_DIGEST_LENGTH
71-
72-
73-
class HashDigestList(SSZList):
74-
"""
75-
Variable-length list of hash digests.
76-
77-
In SSZ notation: `List[Vector[Fp, HASH_DIGEST_LENGTH], NODE_LIST_LIMIT]`
78-
79-
This type is used to represent collections of hash digests in the XMSS scheme.
80-
"""
81-
82-
ELEMENT_TYPE = HashDigestVector
83-
LIMIT = NODE_LIST_LIMIT
84-
85-
86-
class Parameter(SSZVector):
87-
"""
88-
The public parameter P.
89-
90-
This is a unique, randomly generated value associated with a single key pair. It
91-
is mixed into every hash computation to "personalize" the hash function, preventing
92-
certain cross-key attacks. It is public knowledge.
93-
94-
TODO: Make the configuration generic and don't hardcode `PROD_CONFIG`.
95-
"""
96-
97-
ELEMENT_TYPE = Fp
98-
LENGTH = PROD_CONFIG.PARAMETER_LEN
99-
100-
101-
class Randomness(SSZVector):
102-
"""
103-
The randomness `rho` (ρ) used during signing.
104-
105-
This value provides a variable input to the message hash, allowing the signer to
106-
repeatedly try hashing until a valid "codeword" is found. It must be included in
107-
the final signature for the verifier to reproduce the same hash.
108-
109-
SSZ notation: `Vector[Fp, RAND_LEN_FE]`
110-
111-
TODO: Make the configuration generic and don't hardcode `PROD_CONFIG`.
112-
"""
113-
114-
ELEMENT_TYPE = Fp
115-
LENGTH = PROD_CONFIG.RAND_LEN_FE
116-
117-
118-
class HashTreeOpening(Container):
119-
"""
120-
A Merkle authentication path.
121-
122-
This object contains the minimal proof required to connect a specific leaf
123-
to the Merkle root. It consists of the list of all sibling nodes along the
124-
path from the leaf to the top of the tree.
125-
126-
SSZ Container with fields:
127-
- siblings: List[Vector[Fp, HASH_DIGEST_LENGTH], NODE_LIST_LIMIT]
128-
"""
129-
130-
siblings: HashDigestList
131-
"""SSZ-compliant list of sibling hashes, from bottom to top."""
132-
133-
134-
class HashTreeLayer(Container):
135-
"""
136-
Represents a single horizontal "slice" of the sparse Merkle tree.
137-
138-
Because the tree is sparse, we only store the nodes that are actually computed
139-
for the active range of leaves, not the entire conceptual layer.
140-
"""
141-
142-
start_index: Uint64
143-
"""The starting index of the first node in this layer."""
144-
nodes: HashDigestList
145-
"""SSZ-compliant list of hash digests stored for this layer."""
146-
147-
148-
LAYERS_LIMIT = PROD_CONFIG.LOG_LIFETIME + 1
149-
"""
150-
The maximum number of layers in a subtree.
151-
152-
This is `LOG_LIFETIME + 1` to accommodate all layers from 0 (leaves) to LOG_LIFETIME (root),
153-
inclusive. For PROD_CONFIG with LOG_LIFETIME=32, this allows up to 33 layers.
154-
155-
TODO: Make the configuration generic and don't hardcode `PROD_CONFIG`.
156-
"""
157-
158-
159-
class HashTreeLayers(SSZList):
160-
"""
161-
Variable-length list of Merkle tree layers.
162-
163-
In SSZ notation: `List[HashTreeLayer, LAYERS_LIMIT]`
164-
165-
This type represents the layers of a subtree, from the lowest layer up to the root.
166-
167-
The number of layers varies based on the subtree structure:
168-
- Bottom trees: `LOG_LIFETIME/2` layers
169-
- Top trees: `LOG_LIFETIME/2` layers
170-
- Maximum: `LOG_LIFETIME + 1` layers
171-
"""
172-
173-
ELEMENT_TYPE = HashTreeLayer
174-
LIMIT = LAYERS_LIMIT
17526

17627

17728
class PublicKey(Container):
@@ -277,7 +128,7 @@ class SecretKey(Container):
277128
`sqrt(LIFETIME)`, with a minimum of `2 * sqrt(LIFETIME)`.
278129
"""
279130

280-
top_tree: "HashSubTree"
131+
top_tree: HashSubTree
281132
"""
282133
The top tree containing the root and top `LOG_LIFETIME/2` layers.
283134
@@ -297,15 +148,15 @@ class SecretKey(Container):
297148
298149
"""
299150

300-
left_bottom_tree: "HashSubTree"
151+
left_bottom_tree: HashSubTree
301152
"""
302153
The left bottom tree in the sliding window.
303154
304155
This covers epochs:
305156
[left_bottom_tree_index * sqrt(LIFETIME), (left_bottom_tree_index + 1) * sqrt(LIFETIME))
306157
"""
307158

308-
right_bottom_tree: "HashSubTree"
159+
right_bottom_tree: HashSubTree
309160
"""
310161
The right bottom tree in the sliding window.
311162

src/lean_spec/subspecs/xmss/interface.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
TEST_CONFIG,
2626
XmssConfig,
2727
)
28-
from .containers import HashDigestVector, PublicKey, SecretKey, Signature
28+
from .containers import PublicKey, SecretKey, Signature
2929
from .merkle_tree import (
3030
PROD_MERKLE_TREE,
3131
TEST_MERKLE_TREE,
@@ -38,6 +38,7 @@
3838
TEST_TWEAK_HASHER,
3939
TweakHasher,
4040
)
41+
from .types import HashDigestVector
4142
from .utils import bottom_tree_from_prf_key, expand_activation_time
4243

4344

@@ -389,7 +390,7 @@ def sign(self, sk: SecretKey, epoch: Uint64, message: bytes) -> Signature:
389390
# - The Merkle path,
390391
# - The randomness `rho` needed for verification.
391392
# Wrap ots_hashes in SSZ types
392-
from .containers import HashDigestList, HashDigestVector
393+
from .types import HashDigestList, HashDigestVector
393394

394395
ssz_hashes = [HashDigestVector(data=hash_digest) for hash_digest in ots_hashes]
395396
return Signature(path=path, rho=rho, hashes=HashDigestList(data=ssz_hashes))

src/lean_spec/subspecs/xmss/merkle_tree.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@
4242
TEST_CONFIG,
4343
XmssConfig,
4444
)
45-
from .containers import (
46-
HashDigestList,
47-
HashDigestVector,
48-
HashTreeLayer,
49-
HashTreeLayers,
50-
HashTreeOpening,
51-
Parameter,
52-
)
5345
from .rand import PROD_RAND, TEST_RAND, Rand
5446
from .subtree import HashSubTree
5547
from .tweak_hash import (
@@ -58,6 +50,14 @@
5850
TreeTweak,
5951
TweakHasher,
6052
)
53+
from .types import (
54+
HashDigestList,
55+
HashDigestVector,
56+
HashTreeLayer,
57+
HashTreeLayers,
58+
HashTreeOpening,
59+
Parameter,
60+
)
6161
from .utils import get_padded_layer
6262

6363

src/lean_spec/subspecs/xmss/message_hash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@
4747
TWEAK_PREFIX_MESSAGE,
4848
XmssConfig,
4949
)
50-
from .containers import Parameter, Randomness
5150
from .hypercube import (
5251
hypercube_find_layer,
5352
hypercube_part_size,
5453
map_to_vertex,
5554
)
55+
from .types import Parameter, Randomness
5656
from .utils import int_to_base_p
5757

5858

src/lean_spec/subspecs/xmss/prf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
TEST_CONFIG,
2525
XmssConfig,
2626
)
27-
from .containers import PRFKey, Randomness
27+
from .types import PRFKey, Randomness
2828

2929
PRF_DOMAIN_SEP: bytes = bytes(
3030
[

src/lean_spec/subspecs/xmss/rand.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from ..koalabear import Fp, P
1111
from .constants import PROD_CONFIG, TEST_CONFIG, XmssConfig
12-
from .containers import Parameter, Randomness
12+
from .types import Parameter, Randomness
1313

1414

1515
class Rand(StrictBaseModel):

0 commit comments

Comments
 (0)