Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 27 additions & 22 deletions lean_client/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion lean_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ libp2p = {version = "0.56.0", default-features = false, features = [
] }
alloy-consensus = "1.0.38"
alloy-primitives = "1.4.0"
anyhow = "1.0"
anyhow = "1.0.100"
thiserror = "2.0.18"
paste = "1.0.15"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
Expand Down Expand Up @@ -67,3 +68,4 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
tracing = "0.1.41"
hex = "0.4"
libp2p-identity = { version = "0.2", features = ["secp256k1"] }
anyhow = { workspace = true }
2 changes: 2 additions & 0 deletions lean_client/containers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ name = "containers"
path = "src/lib.rs"

[dependencies]
anyhow = { workspace = true }
ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop" }
ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" }
typenum = "1"
Expand All @@ -22,6 +23,7 @@ sha2 = "0.10"
leansig = { git = "https://github.com/leanEthereum/leanSig", branch = "main", optional = true }

[dev-dependencies]
anyhow = { workspace = true }
rstest = "0.18"
pretty_assertions = "1.4"
serde_json = "1.0"
66 changes: 29 additions & 37 deletions lean_client/containers/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
Attestation, Attestations, BlockSignatures, Bytes32, Signature, Slot, State, ValidatorIndex,
};
use anyhow::{anyhow, bail, ensure, Context, Result};
use serde::{Deserialize, Serialize};
use ssz_derive::Ssz;

Expand Down Expand Up @@ -114,7 +115,7 @@ impl SignedBlockWithAttestation {
///
/// - Spec: <https://github.com/leanEthereum/leanSpec/blob/main/src/lean_spec/subspecs/containers/block/block.py#L35>
/// - XMSS Library: <https://github.com/leanEthereum/leanSig>
pub fn verify_signatures(&self, parent_state: State) -> bool {
pub fn verify_signatures(&self, parent_state: State) -> Result<()> {
// Unpack the signed block components
let block = &self.message.block;
let signatures = &self.signature;
Expand Down Expand Up @@ -157,7 +158,7 @@ impl SignedBlockWithAttestation {
// The ordering must be preserved:
// 1. Block body attestations,
// 2. The proposer attestation.
assert!(
ensure!(
signatures_vec.len() == all_attestations.len(),
"Number of signatures does not match number of attestations"
);
Expand All @@ -167,15 +168,14 @@ impl SignedBlockWithAttestation {

// Verify each attestation signature
for (attestation, signature) in all_attestations.iter().zip(signatures_vec.iter()) {
// Ensure validator exists in the active set
assert!(
attestation.validator_id.0 < num_validators,
"Validator index out of range"
);

let validator = validators
.get(attestation.validator_id.0)
.expect("validator must exist");
.with_context(|| {
format!(
"Validator {} not found in state",
attestation.validator_id.0
)
})?;

// Verify the XMSS signature
//
Expand All @@ -198,41 +198,33 @@ impl SignedBlockWithAttestation {

// Deserialize the public key using Serializable trait
type PubKey = <SIGTargetSumLifetime20W2NoOff as SignatureScheme>::PublicKey;
let pubkey = match PubKey::from_bytes(pubkey_bytes) {
Ok(pk) => pk,
Err(e) => {
eprintln!(
"Failed to deserialize public key at slot {:?}: {:?}",
attestation.data.slot, e
);
return false;
}
};
let pubkey = PubKey::from_bytes(pubkey_bytes).map_err(|e| {
anyhow!(
"Failed to deserialize public key at slot {:?}: {:?}",
attestation.data.slot,
e
)
})?;

// Get signature bytes - use as_bytes() method
let sig_bytes = signature.as_bytes();

// Deserialize the signature using Serializable trait
type Sig = <SIGTargetSumLifetime20W2NoOff as SignatureScheme>::Signature;
let sig = match Sig::from_bytes(sig_bytes) {
Ok(s) => s,
Err(e) => {
eprintln!(
"Failed to deserialize signature at slot {:?}: {:?}",
attestation.data.slot, e
);
return false;
}
};
let sig = Sig::from_bytes(sig_bytes).map_err(|e| {
anyhow!(
"Failed to deserialize signature at slot {:?}: {:?}",
attestation.data.slot,
e
)
})?;

// Verify the signature
if !SIGTargetSumLifetime20W2NoOff::verify(&pubkey, epoch, &message_bytes, &sig) {
eprintln!(
"XMSS signature verification failed at slot {:?}",
attestation.data.slot
);
return false;
}
ensure!(
SIGTargetSumLifetime20W2NoOff::verify(&pubkey, epoch, &message_bytes, &sig),
"XMSS signature verification failed at slot {:?}",
attestation.data.slot
);
}

#[cfg(not(feature = "xmss-verify"))]
Expand All @@ -246,6 +238,6 @@ impl SignedBlockWithAttestation {
}
}

true
Ok(())
}
}
9 changes: 6 additions & 3 deletions lean_client/containers/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use ssz_derive::Ssz;
use std::fs::File;
Expand All @@ -20,10 +21,12 @@ pub struct GenesisConfig {
}

impl GenesisConfig {
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
let file = File::open(path)?;
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let file = File::open(&path)
.with_context(|| format!("Failed to open genesis config file: {:?}", path.as_ref()))?;
let reader = BufReader::new(file);
let config = serde_yaml::from_reader(reader)?;
let config =
serde_yaml::from_reader(reader).context("Failed to parse genesis config YAML")?;
Ok(config)
}
}
Loading