Skip to content

Commit a5db569

Browse files
committed
Simplify Phase 1 types: remove verbose docs, deduplicate deser_pubkey_hex, pin state root hash
1 parent 5688c31 commit a5db569

File tree

4 files changed

+34
-152
lines changed

4 files changed

+34
-152
lines changed

crates/common/test-fixtures/src/lib.rs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ethlambda_types::{
55
},
66
block::{Block as DomainBlock, BlockBody as DomainBlockBody},
77
checkpoint::Checkpoint as DomainCheckpoint,
8+
genesis::deser_pubkey_hex,
89
primitives::{BitList, H256, VariableList},
910
state::{ChainConfig, State, Validator as DomainValidator, ValidatorPubkeyBytes},
1011
};
@@ -92,11 +93,9 @@ impl From<BlockHeader> for ethlambda_types::block::BlockHeader {
9293
#[derive(Debug, Clone, Deserialize)]
9394
pub struct Validator {
9495
index: u64,
95-
#[serde(rename = "attestationPubkey")]
96-
#[serde(deserialize_with = "deser_pubkey_hex")]
96+
#[serde(rename = "attestationPubkey", deserialize_with = "deser_pubkey_hex")]
9797
attestation_pubkey: ValidatorPubkeyBytes,
98-
#[serde(rename = "proposalPubkey")]
99-
#[serde(deserialize_with = "deser_pubkey_hex")]
98+
#[serde(rename = "proposalPubkey", deserialize_with = "deser_pubkey_hex")]
10099
proposal_pubkey: ValidatorPubkeyBytes,
101100
}
102101

@@ -273,22 +272,3 @@ pub struct TestInfo {
273272
#[serde(rename = "fixtureFormat")]
274273
pub fixture_format: String,
275274
}
276-
277-
// ============================================================================
278-
// Helpers
279-
// ============================================================================
280-
281-
pub fn deser_pubkey_hex<'de, D>(d: D) -> Result<ValidatorPubkeyBytes, D::Error>
282-
where
283-
D: serde::Deserializer<'de>,
284-
{
285-
use serde::Deserialize;
286-
use serde::de::Error;
287-
288-
let value = String::deserialize(d)?;
289-
let pubkey: ValidatorPubkeyBytes = hex::decode(value.strip_prefix("0x").unwrap_or(&value))
290-
.map_err(|_| D::Error::custom("ValidatorPubkey value is not valid hex"))?
291-
.try_into()
292-
.map_err(|_| D::Error::custom("ValidatorPubkey length != 52"))?;
293-
Ok(pubkey)
294-
}

crates/common/types/src/block.rs

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,9 @@ use crate::{
1010
state::ValidatorRegistryLimit,
1111
};
1212

13-
/// Envelope carrying a block and its aggregated signatures.
1413
#[derive(Clone, Encode, Decode)]
1514
pub struct SignedBlock {
16-
/// The block being signed.
1715
pub message: Block,
18-
19-
/// Aggregated signature payload for the block.
20-
///
21-
/// Contains per-attestation aggregated proofs and the proposer's signature
22-
/// over the block root using the proposal key.
2316
pub signature: BlockSignatures,
2417
}
2518

@@ -33,116 +26,66 @@ impl core::fmt::Debug for SignedBlock {
3326
}
3427
}
3528

36-
/// Signature payload for the block.
3729
#[derive(Clone, Encode, Decode)]
3830
pub struct BlockSignatures {
39-
/// Attestation signatures for the aggregated attestations in the block body.
40-
///
41-
/// Each entry corresponds to an aggregated attestation from the block body and
42-
/// contains the leanVM aggregated signature proof bytes for the participating validators.
43-
///
44-
/// TODO:
45-
/// - Eventually this field will be replaced by a single SNARK aggregating *all* signatures.
31+
/// One aggregated proof per attestation in the block body.
4632
pub attestation_signatures: AttestationSignatures,
47-
48-
/// Proposer's signature over the block root using the proposal key.
33+
/// Proposer's signature over `hash_tree_root(block)` using the proposal key.
4934
pub proposer_signature: XmssSignature,
5035
}
5136

52-
/// List of per-attestation aggregated signature proofs.
53-
///
54-
/// Each entry corresponds to an aggregated attestation from the block body.
55-
///
56-
/// It contains:
57-
/// - the participants bitfield,
58-
/// - proof bytes from leanVM signature aggregation.
5937
pub type AttestationSignatures =
6038
ssz_types::VariableList<AggregatedSignatureProof, ValidatorRegistryLimit>;
6139

62-
/// Cryptographic proof that a set of validators signed a message.
63-
///
64-
/// This container encapsulates the output of the leanVM signature aggregation,
65-
/// combining the participant set with the proof bytes. This design ensures
66-
/// the proof is self-describing: it carries information about which validators
67-
/// it covers.
68-
///
69-
/// The proof can verify that all participants signed the same message in the
70-
/// same epoch, using a single verification operation instead of checking
71-
/// each signature individually.
40+
/// Aggregated leanVM signature proof covering a set of validators.
7241
#[derive(Debug, Clone, Encode, Decode)]
7342
pub struct AggregatedSignatureProof {
74-
/// Bitfield indicating which validators' signatures are included.
7543
pub participants: AggregationBits,
76-
/// The raw aggregated proof bytes from leanVM.
7744
pub proof_data: ByteListMiB,
7845
}
7946

8047
pub type ByteListMiB = ByteList<U1048576>;
8148

8249
impl AggregatedSignatureProof {
83-
/// Create a new aggregated signature proof.
8450
pub fn new(participants: AggregationBits, proof_data: ByteListMiB) -> Self {
8551
Self {
8652
participants,
8753
proof_data,
8854
}
8955
}
9056

91-
/// Create an empty proof with the given participants bitfield.
92-
///
93-
/// Used as a placeholder when actual aggregation is not yet implemented.
57+
/// Empty proof (placeholder for test paths without real aggregation).
9458
pub fn empty(participants: AggregationBits) -> Self {
9559
Self {
9660
participants,
9761
proof_data: ByteList::empty(),
9862
}
9963
}
10064

101-
/// Returns the validator indices that are set in the participants bitfield.
10265
pub fn participant_indices(&self) -> impl Iterator<Item = u64> + '_ {
10366
validator_indices(&self.participants)
10467
}
10568
}
10669

107-
/// The header of a block, containing metadata.
108-
///
109-
/// Block headers summarize blocks without storing full content. The header
110-
/// includes references to the parent and the resulting state. It also contains
111-
/// a hash of the block body.
112-
///
113-
/// Headers are smaller than full blocks. They're useful for tracking the chain
114-
/// without storing everything.
11570
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Encode, Decode, TreeHash)]
11671
pub struct BlockHeader {
117-
/// The slot in which the block was proposed
11872
pub slot: u64,
119-
/// The index of the validator that proposed the block
12073
pub proposer_index: u64,
121-
/// The root of the parent block
12274
pub parent_root: H256,
123-
/// The root of the state after applying transactions in this block
12475
pub state_root: H256,
125-
/// The root of the block body
12676
pub body_root: H256,
12777
}
12878

129-
/// A complete block including header and body.
13079
#[derive(Debug, Clone, Encode, Decode, TreeHash)]
13180
pub struct Block {
132-
/// The slot in which the block was proposed.
13381
pub slot: u64,
134-
/// The index of the validator that proposed the block.
13582
pub proposer_index: u64,
136-
/// The root of the parent block.
13783
pub parent_root: H256,
138-
/// The root of the state after applying transactions in this block.
13984
pub state_root: H256,
140-
/// The block's payload.
14185
pub body: BlockBody,
14286
}
14387

14488
impl Block {
145-
/// Extract the block header, computing the body root.
14689
pub fn header(&self) -> BlockHeader {
14790
BlockHeader {
14891
slot: self.slot,
@@ -153,10 +96,6 @@ impl Block {
15396
}
15497
}
15598

156-
/// Reconstruct a block from header and body.
157-
///
158-
/// The caller should ensure that `header.body_root` matches `body.tree_hash_root()`.
159-
/// This is verified with a debug assertion but not in release builds.
16099
pub fn from_header_and_body(header: BlockHeader, body: BlockBody) -> Self {
161100
debug_assert_eq!(
162101
header.body_root,
@@ -173,19 +112,10 @@ impl Block {
173112
}
174113
}
175114

176-
/// The body of a block, containing payload data.
177-
///
178-
/// Currently, the main operation is voting. Validators submit attestations which are
179-
/// packaged into blocks.
180115
#[derive(Debug, Default, Clone, Encode, Decode, TreeHash)]
181116
pub struct BlockBody {
182-
/// Plain validator attestations carried in the block body.
183-
///
184-
/// Individual signatures live in the aggregated block signature list, so
185-
/// these entries contain only attestation data without per-attestation signatures.
186117
pub attestations: AggregatedAttestations,
187118
}
188119

189-
/// List of aggregated attestations included in a block.
190120
pub type AggregatedAttestations =
191121
ssz_types::VariableList<AggregatedAttestation, ValidatorRegistryLimit>;

crates/common/types/src/genesis.rs

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use serde::Deserialize;
22

33
use crate::state::{Validator, ValidatorPubkeyBytes};
44

5-
/// A single validator entry in the genesis config with dual public keys.
65
#[derive(Debug, Clone, Deserialize)]
76
pub struct GenesisValidatorEntry {
87
#[serde(deserialize_with = "deser_pubkey_hex")]
@@ -33,7 +32,7 @@ impl GenesisConfig {
3332
}
3433
}
3534

36-
fn deser_pubkey_hex<'de, D>(d: D) -> Result<ValidatorPubkeyBytes, D::Error>
35+
pub fn deser_pubkey_hex<'de, D>(d: D) -> Result<ValidatorPubkeyBytes, D::Error>
3736
where
3837
D: serde::Deserializer<'de>,
3938
{
@@ -56,21 +55,12 @@ mod tests {
5655
state::{State, Validator},
5756
};
5857

59-
const ATT_PUBKEY_A: &str = "cd323f232b34ab26d6db7402c886e74ca81cfd3a0c659d2fe022356f25592f7d2d25ca7b19604f5a180037046cf2a02e1da4a800";
60-
const PROP_PUBKEY_A: &str = "b7b0f72e24801b02bda64073cb4de6699a416b37dfead227d7ca3922647c940fa03e4c012e8a0e656b731934aeac124a5337e333";
61-
const ATT_PUBKEY_B: &str = "8d9cbc508b20ef43e165f8559c1bdd18aaeda805ef565a4f9ffd6e4fbed01c05e143e305017847445859650d6dd06e6efb3f8410";
62-
const ATT_PUBKEY_C: &str = "b7b0f72e24801b02bda64073cb4de6699a416b37dfead227d7ca3922647c940fa03e4c012e8a0e656b731934aeac124a5337e333";
58+
const PUBKEY_A: &str = "cd323f232b34ab26d6db7402c886e74ca81cfd3a0c659d2fe022356f25592f7d2d25ca7b19604f5a180037046cf2a02e1da4a800";
59+
const PUBKEY_B: &str = "b7b0f72e24801b02bda64073cb4de6699a416b37dfead227d7ca3922647c940fa03e4c012e8a0e656b731934aeac124a5337e333";
60+
const PUBKEY_C: &str = "8d9cbc508b20ef43e165f8559c1bdd18aaeda805ef565a4f9ffd6e4fbed01c05e143e305017847445859650d6dd06e6efb3f8410";
6361

64-
const TEST_CONFIG_YAML: &str = r#"# Genesis Settings
62+
const TEST_CONFIG_YAML: &str = r#"
6563
GENESIS_TIME: 1770407233
66-
67-
# Key Settings
68-
ACTIVE_EPOCH: 18
69-
70-
# Validator Settings
71-
VALIDATOR_COUNT: 3
72-
73-
# Genesis Validator Pubkeys
7464
GENESIS_VALIDATORS:
7565
- attestation_pubkey: "cd323f232b34ab26d6db7402c886e74ca81cfd3a0c659d2fe022356f25592f7d2d25ca7b19604f5a180037046cf2a02e1da4a800"
7666
proposal_pubkey: "b7b0f72e24801b02bda64073cb4de6699a416b37dfead227d7ca3922647c940fa03e4c012e8a0e656b731934aeac124a5337e333"
@@ -87,29 +77,21 @@ GENESIS_VALIDATORS:
8777

8878
assert_eq!(config.genesis_time, 1770407233);
8979
assert_eq!(config.genesis_validators.len(), 3);
90-
assert_eq!(
91-
config.genesis_validators[0].attestation_pubkey,
92-
hex::decode(ATT_PUBKEY_A).unwrap().as_slice()
93-
);
94-
assert_eq!(
95-
config.genesis_validators[0].proposal_pubkey,
96-
hex::decode(PROP_PUBKEY_A).unwrap().as_slice()
97-
);
98-
assert_eq!(
99-
config.genesis_validators[1].attestation_pubkey,
100-
hex::decode(ATT_PUBKEY_B).unwrap().as_slice()
101-
);
102-
assert_eq!(
103-
config.genesis_validators[2].attestation_pubkey,
104-
hex::decode(ATT_PUBKEY_C).unwrap().as_slice()
105-
);
80+
81+
let v0 = &config.genesis_validators[0];
82+
assert_eq!(v0.attestation_pubkey, *hex::decode(PUBKEY_A).unwrap());
83+
assert_eq!(v0.proposal_pubkey, *hex::decode(PUBKEY_B).unwrap());
84+
85+
let v1 = &config.genesis_validators[1];
86+
assert_eq!(v1.attestation_pubkey, *hex::decode(PUBKEY_C).unwrap());
87+
assert_eq!(v1.proposal_pubkey, *hex::decode(PUBKEY_A).unwrap());
10688
}
10789

10890
#[test]
10991
fn state_from_genesis_uses_defaults() {
11092
let validators = vec![Validator {
111-
attestation_pubkey: hex::decode(ATT_PUBKEY_A).unwrap().try_into().unwrap(),
112-
proposal_pubkey: hex::decode(PROP_PUBKEY_A).unwrap().try_into().unwrap(),
93+
attestation_pubkey: hex::decode(PUBKEY_A).unwrap().try_into().unwrap(),
94+
proposal_pubkey: hex::decode(PUBKEY_B).unwrap().try_into().unwrap(),
11395
index: 0,
11496
}];
11597

@@ -134,24 +116,22 @@ GENESIS_VALIDATORS:
134116
let state = State::from_genesis(config.genesis_time, validators);
135117
let root = state.tree_hash_root();
136118

137-
// Pin the state root so changes are caught immediately.
138-
// NOTE: This hash changed in devnet4 due to the Validator SSZ layout change
139-
// (single pubkey → attestation_pubkey + proposal_pubkey) and test data change.
140-
// Will be recomputed once we can run this test.
141-
// For now, just verify the root is deterministic by checking it's non-zero.
142-
assert_ne!(
143-
root,
144-
crate::primitives::H256::ZERO,
145-
"state root should be non-zero"
146-
);
119+
// Pin the state root so SSZ layout changes are caught immediately.
120+
let expected =
121+
hex::decode("babcdc9235a29dfc0d605961df51cfc85732f85291c2beea8b7510a92ec458fe")
122+
.unwrap();
123+
assert_eq!(root.as_slice(), &expected[..], "state root mismatch");
147124

148125
let mut block = state.latest_block_header;
149126
block.state_root = root;
150127
let block_root = block.tree_hash_root();
151-
assert_ne!(
152-
block_root,
153-
crate::primitives::H256::ZERO,
154-
"block root should be non-zero"
128+
let expected_block_root =
129+
hex::decode("66a8beaa81d2aaeac7212d4bf8f5fea2bd22d479566a33a83c891661c21235ef")
130+
.unwrap();
131+
assert_eq!(
132+
block_root.as_slice(),
133+
&expected_block_root[..],
134+
"block root mismatch"
155135
);
156136
}
157137
}

crates/common/types/src/state.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,12 @@ pub type JustificationRoots = ssz_types::VariableList<H256, HistoricalRootsLimit
6161
pub type JustificationValidators =
6262
ssz_types::BitList<ssz_types::typenum::Prod<HistoricalRootsLimit, ValidatorRegistryLimit>>;
6363

64-
/// Represents a validator's static metadata and operational interface.
65-
///
66-
/// Each validator has two independent XMSS keys: one for signing attestations
67-
/// and one for signing block proposals. This allows signing both in the same
68-
/// slot without violating OTS (one-time signature) constraints.
6964
#[derive(Debug, Clone, Serialize, Encode, Decode, TreeHash)]
7065
pub struct Validator {
71-
/// XMSS public key used for attestation signing.
7266
#[serde(serialize_with = "serialize_pubkey_hex")]
7367
pub attestation_pubkey: ValidatorPubkeyBytes,
74-
/// XMSS public key used for block proposal signing.
7568
#[serde(serialize_with = "serialize_pubkey_hex")]
7669
pub proposal_pubkey: ValidatorPubkeyBytes,
77-
/// Validator index in the registry.
7870
pub index: u64,
7971
}
8072

0 commit comments

Comments
 (0)