Skip to content

Commit bb9991a

Browse files
tcoratgerfselmo
andauthored
chore: uint base types and skeleton for ssz (leanEthereum#34)
* chore: uint base types and skeleton for ssz * some fix - wip * fix tests * fix silent truncation of floats * add max method * fix constructor * Update src/lean_spec/types/uint.py Co-authored-by: felipe <fselmo2@gmail.com> * add pydantic checks * fix some operations and tests * fix max method * fix max * add test for max method --------- Co-authored-by: felipe <fselmo2@gmail.com>
1 parent 7776c43 commit bb9991a

15 files changed

Lines changed: 683 additions & 41 deletions

File tree

src/lean_spec/subspecs/chain/config.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212

1313
# --- Time Parameters ---
1414

15-
SLOT_DURATION_MS: Final = 4000
15+
SLOT_DURATION_MS: Final = Uint64(4000)
1616
"""The fixed duration of a single slot in milliseconds."""
1717

18-
SECONDS_PER_SLOT: Final = SLOT_DURATION_MS // 1000
18+
SECONDS_PER_SLOT: Final = SLOT_DURATION_MS // Uint64(1000)
1919
"""The fixed duration of a single slot in seconds."""
2020

21-
JUSTIFICATION_LOOKBACK_SLOTS: Final = 3
21+
JUSTIFICATION_LOOKBACK_SLOTS: Final = Uint64(3)
2222
"""The number of slots to lookback for justification."""
2323

24-
PROPOSER_REORG_CUTOFF_BPS: Final = 2500
24+
PROPOSER_REORG_CUTOFF_BPS: Final = Uint64(2500)
2525
"""
2626
The deadline within a slot (in basis points) for a proposer to publish a
2727
block.
@@ -31,23 +31,23 @@
3131
(2500 bps = 25% of slot duration).
3232
"""
3333

34-
VOTE_DUE_BPS: Final = 5000
34+
VOTE_DUE_BPS: Final = Uint64(5000)
3535
"""
3636
The deadline within a slot (in basis points) by which validators must
3737
submit their votes.
3838
3939
(5000 bps = 50% of slot duration).
4040
"""
4141

42-
FAST_CONFIRM_DUE_BPS: Final = 7500
42+
FAST_CONFIRM_DUE_BPS: Final = Uint64(7500)
4343
"""
4444
The deadline within a slot (in basis points) for achieving a fast
4545
confirmation.
4646
4747
(7500 bps = 75% of slot duration).
4848
"""
4949

50-
VIEW_FREEZE_CUTOFF_BPS: Final = 7500
50+
VIEW_FREEZE_CUTOFF_BPS: Final = Uint64(7500)
5151
"""
5252
The cutoff within a slot (in basis points) after which the current view is
5353
considered 'frozen', preventing further changes.
@@ -57,15 +57,15 @@
5757

5858
# --- State List Length Presets ---
5959

60-
HISTORICAL_ROOTS_LIMIT: Final = 2**18
60+
HISTORICAL_ROOTS_LIMIT: Final = Uint64(2**18)
6161
"""
6262
The maximum number of historical block roots to store in the state.
6363
6464
With a 4-second slot, this corresponds to a history
6565
of approximately 12.1 days.
6666
"""
6767

68-
VALIDATOR_REGISTRY_LIMIT: Final = 2**12
68+
VALIDATOR_REGISTRY_LIMIT: Final = Uint64(2**12)
6969
"""The maximum number of validators that can be in the registry."""
7070

7171

src/lean_spec/subspecs/containers/state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def get_justifications(self) -> Dict[Bytes32, List[bool]]:
9999
# Cache the validator registry limit for concise slicing calculations.
100100
#
101101
# This value determines the size of the block of votes for each root.
102-
limit = DEVNET_CONFIG.validator_registry_limit
102+
limit = DEVNET_CONFIG.validator_registry_limit.as_int()
103103

104104
# Build the entire justifications map.
105105
return {
@@ -134,7 +134,7 @@ def set_justifications(self, justifications: Dict[Bytes32, List[bool]]) -> None:
134134
new_roots: List[Bytes32] = []
135135
# It will store the single, concatenated list of all votes.
136136
flat_votes: List[bool] = []
137-
limit = DEVNET_CONFIG.validator_registry_limit
137+
limit = DEVNET_CONFIG.validator_registry_limit.as_int()
138138

139139
# Iterate through the roots in sorted order for deterministic output.
140140
for root in sorted(justifications.keys()):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""SSZ (Simple Serialize) implementation."""
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Constants defined in the SSZ specification."""
2+
3+
BYTES_PER_CHUNK: int = 32
4+
"""The number of bytes in a Merkle tree chunk."""
5+
6+
ZERO_HASH: bytes = b"\x00" * BYTES_PER_CHUNK
7+
"""A zero hash, used for padding in the Merkle tree."""
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Generalized Index implementation."""
2+
3+
from typing import List
4+
5+
from pydantic import BaseModel, Field
6+
7+
8+
class GeneralizedIndex(BaseModel):
9+
"""
10+
Represents a Generalized Merkle Tree Index.
11+
12+
Helper methods are provided for tree navigation.
13+
"""
14+
15+
value: int = Field(..., gt=0, description="The index value, must be a positive integer.")
16+
17+
@property
18+
def depth(self) -> int:
19+
"""The depth of the node in the tree."""
20+
return self.value.bit_length() - 1
21+
22+
def get_bit(self, position: int) -> bool:
23+
"""Returns the bit at a specific position (from the right)."""
24+
return (self.value >> position) & 1 == 1
25+
26+
@property
27+
def sibling(self) -> "GeneralizedIndex":
28+
"""Returns the index of the sibling node."""
29+
return type(self)(value=self.value ^ 1)
30+
31+
@property
32+
def parent(self) -> "GeneralizedIndex":
33+
"""Returns the index of the parent node."""
34+
if self.value <= 1:
35+
raise ValueError("Root node has no parent.")
36+
return type(self)(value=self.value // 2)
37+
38+
def get_branch_indices(self) -> List["GeneralizedIndex"]:
39+
"""Gets the indices of the sibling nodes along the path to the root."""
40+
indices = [self.sibling]
41+
while indices[-1].value > 1:
42+
indices.append(indices[-1].parent.sibling)
43+
return indices[:-1]
44+
45+
def get_path_indices(self) -> List["GeneralizedIndex"]:
46+
"""Gets the indices of the nodes along the path to the root."""
47+
indices: List["GeneralizedIndex"] = [self]
48+
while indices[-1].value > 1:
49+
indices.append(indices[-1].parent)
50+
return indices[:-1]

src/lean_spec/subspecs/xmss/interface.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
TEST_TARGET_SUM_ENCODER,
1616
TargetSumEncoder,
1717
)
18+
from lean_spec.types.uint import Uint64
1819

1920
from .constants import (
2021
PROD_CONFIG,
@@ -117,7 +118,7 @@ def key_gen(self, activation_epoch: int, num_active_epochs: int) -> Tuple[Public
117118
# Derive the secret start of the chain from the master PRF key.
118119
#
119120
# This ensures each chain is unique and cryptographically secure.
120-
start_digest = self.prf.apply(prf_key, epoch, chain_index)
121+
start_digest = self.prf.apply(prf_key, epoch, Uint64(chain_index))
121122

122123
# Compute the public end of the chain by applying the hash function
123124
# `BASE - 1` times. This is the public part of the one-time key.
@@ -234,7 +235,7 @@ def sign(self, sk: SecretKey, epoch: int, message: bytes) -> Signature:
234235
ots_hashes: List[HashDigest] = []
235236
for chain_index, steps in enumerate(codeword):
236237
# Derive the secret start of the current chain using the master PRF key.
237-
start_digest = self.prf.apply(sk.prf_key, epoch, chain_index)
238+
start_digest = self.prf.apply(sk.prf_key, epoch, Uint64(chain_index))
238239
# Walk the hash chain for the number of `steps` specified by the
239240
# corresponding digit in the codeword.
240241
#

src/lean_spec/subspecs/xmss/prf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from typing import List
1515

1616
from lean_spec.subspecs.koalabear import Fp
17-
from lean_spec.types.uint64 import Uint64
17+
from lean_spec.types.uint import Uint64
1818

1919
from .constants import (
2020
PRF_KEY_LENGTH,

src/lean_spec/types/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .base import StrictBaseModel
44
from .basispt import BasisPoint
55
from .hash import Bytes32
6-
from .uint64 import Uint64
6+
from .uint import Uint64
77
from .validator import ValidatorIndex
88

99
__all__ = [

src/lean_spec/types/basispt.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from pydantic import Field
44
from typing_extensions import Annotated
55

6-
from ..types.uint64 import Uint64
6+
from .uint import Uint64
77

88
BasisPoint = Annotated[
99
Uint64,
10-
Field(le=10000, description="A value in basis points (1/10000)."),
10+
Field(le=Uint64(10000), description="A value in basis points (1/10000)."),
1111
]
1212
"""
1313
A type alias for basis points.

0 commit comments

Comments
 (0)