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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ ethlambda-types.workspace = true
spawned-concurrency.workspace = true

tracing.workspace = true
thiserror.workspace = true

prometheus.workspace = true
20 changes: 15 additions & 5 deletions crates/blockchain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
use ethlambda_storage::Store;
use ethlambda_types::{block::SignedBlockWithAttestation, primitives::TreeHash};
use ethlambda_types::{block::{SignedBlockWithAttestation, VerifySignatureError}, primitives::TreeHash};
use spawned_concurrency::tasks::{CallResponse, CastResponse, GenServer, GenServerHandle};
use tracing::{error, warn};

pub struct BlockChain {
handle: GenServerHandle<BlockChainServer>,
}


#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Signed block signature verification failed")]
VerifySignatureError(#[from] VerifySignatureError),
}

impl BlockChain {
pub fn spawn(store: Store) -> BlockChain {
BlockChain {
Expand Down Expand Up @@ -35,9 +42,9 @@ impl BlockChainServer {
let slot = signed_block.message.block.slot;
update_head_slot(slot);

let block = signed_block.message.block;
let proposer_attestation = signed_block.message.proposer_attestation;
let signatures = signed_block.signature;
let block = &signed_block.message.block;
let proposer_attestation = &signed_block.message.proposer_attestation;
let signatures = &signed_block.signature;

let block_root = block.tree_hash_root();

Expand All @@ -51,7 +58,10 @@ impl BlockChainServer {
return;
};

// TODO: validate block signatures
if let Err(err) = signed_block.verify_signatures(&pre_state) {
error!(%slot, %block_root, parent=%block.parent_root, "Signed block signature verification failed: {}", err);
return;
}

let state_changes = ethlambda_state_transition::state_transition(&pre_state, &block);
}
Expand Down
67 changes: 64 additions & 3 deletions crates/common/types/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use leansig::{signature::SignatureScheme, serialization::Serializable};
use ssz_derive::{Decode, Encode};
use ssz_types::typenum::{Diff, U488, U3600, U4096};
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;

use thiserror::Error;
use crate::{
attestation::{Attestation, Attestations},
primitives::H256,
signature::{Signature, SignatureSize},
state::ValidatorRegistryLimit,
signature::{LeanPublicKey, LeanSignatureScheme, Signature, SignatureSize},
state::{State, ValidatorRegistryLimit},
};

/// Envelope carrying a block, an attestation from proposer, and aggregated signatures.
Expand All @@ -27,6 +29,65 @@ pub struct SignedBlockWithAttestation {
pub signature: BlockSignatures,
}

#[derive(Error, Debug)]
pub enum VerifySignatureError {
#[error("Number of signatures {0} does not match number of attestations {1}")]
NumberOfSignaturesMismatch(usize, usize),
#[error("Validator {0} index out of range")]
ValidatorOutOfRange(u64),
#[error("Failed to deserialize public key")]
PublicKeyDeserializationError,
#[error("Failed to deserialize signature")]
SignatureDeserializationError,
#[error("Signature verification failed")]
SignatureVerificationFailed,
}

impl SignedBlockWithAttestation {
/// Verify all XMSS signatures in this signed block.

/// This function ensures that every attestation included in the block
/// (both on-chain attestations from the block body and the proposer's
/// own attestation) is properly signed by the claimed validator using
/// their registered XMSS public key.
pub fn verify_signatures(&self, parent_state: &State) -> Result<bool, VerifySignatureError> {
let block = &self.message.block;
let signature = &self.signature;

let all_attestations: Vec<_> = block.body.attestations
.iter()
.chain(std::iter::once(&self.message.proposer_attestation))
.collect();

let validators = &parent_state.validators;

if signature.len() != all_attestations.len() {
return Err(VerifySignatureError::NumberOfSignaturesMismatch(signature.len(), all_attestations.len()));
}

for (attestation, signature) in all_attestations.iter().zip(signature.iter()) {
let validator_id = attestation.validator_id;

// Ensure validator is in the active set
if validator_id < validators.len() as u64 {
return Err(VerifySignatureError::ValidatorOutOfRange(validator_id));
}

let validator = &validators[validator_id as usize];

// Verify the XMSS signature
let lean_public_key = LeanPublicKey::from_bytes(&validator.pubkey).map_err(|_| VerifySignatureError::PublicKeyDeserializationError)?;
let lean_signature = Signature::from_bytes(signature).map_err(|_| VerifySignatureError::SignatureDeserializationError)?;
if !LeanSignatureScheme::verify(&lean_public_key, attestation.data.slot as u32, &attestation.tree_hash_root(), &lean_signature) {
return Err(VerifySignatureError::SignatureVerificationFailed);
}
}


Ok(true)
}
}

// Manual Debug impl because leanSig signatures don't implement Debug.
impl core::fmt::Debug for SignedBlockWithAttestation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Expand Down
4 changes: 3 additions & 1 deletion crates/common/types/src/signature.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use leansig::signature::SignatureScheme;
use ssz_types::typenum::{Diff, U488, U3600};

type LeanSignatureScheme = leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8;
pub type LeanSignatureScheme = leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8;

type LeanSigSignature = <LeanSignatureScheme as SignatureScheme>::Signature;

pub type Signature = LeanSigSignature;

pub type SignatureSize = Diff<U3600, U488>;

pub type LeanPublicKey = <LeanSignatureScheme as SignatureScheme>::PublicKey;