Skip to content
Closed
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
61 changes: 40 additions & 21 deletions Cargo.lock

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

7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ clap = { version = "4.3", features = ["derive", "env"] }
# XMSS signatures + recursive aggregation (leanVM).
# leanVM's `main` internalized XMSS (dropping the external leanSig dependency)
# and reworked the aggregation API (see "Xmss api rework").
# Pinned to a `main` commit (`c83b40f`) for reproducible builds; bump the rev to
# track main forward.
xmss = { git = "https://github.com/leanEthereum/leanVM.git", rev = "c83b40f01e809f1bd3238d17a821a1ff3ed8171d" }
rec_aggregation = { git = "https://github.com/leanEthereum/leanVM.git", rev = "c83b40f01e809f1bd3238d17a821a1ff3ed8171d" }
# Pinned to a `main` commit for reproducible builds; bump the revs to track main.
xmss = { git = "https://github.com/leanEthereum/leanVM.git", rev = "380da82c330eac2841252a655a86ea48ec173bb6" }
lean-multisig = { git = "https://github.com/leanEthereum/leanVM.git", rev = "380da82c330eac2841252a655a86ea48ec173bb6" }

# SSZ codec used by leanVM's xmss pubkey/signature types (ethereum_ssz, not libssz).
ssz = { package = "ethereum_ssz", version = "0.10" }
Expand Down
6 changes: 2 additions & 4 deletions crates/common/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ version.workspace = true
[dependencies]
ethlambda-types.workspace = true

# rec_aggregation is leanVM's recursive XMSS aggregation prover/verifier;
# xmss provides the public-key/signature types its API consumes.
rec_aggregation.workspace = true
xmss.workspace = true
lean-multisig.workspace = true

thiserror.workspace = true
rand.workspace = true
tracing.workspace = true

[features]
shadow-integration = []
Expand Down
42 changes: 27 additions & 15 deletions crates/common/crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,42 @@ use ethlambda_types::{
primitives::H256,
signature::{LeanSigPublicKey, LeanSigSignature, ValidatorPublicKey, ValidatorSignature},
};
use rec_aggregation::{
use lean_multisig::{
MultiMessageAggregateSignature as LMType2, SingleMessageAggregateSignature as LMType1,
aggregate_single_message_signatures, init_aggregation_bytecode,
merge_single_message_aggregates, split_multi_message_aggregate, verify_multi_message_aggregate,
aggregate_single_message_signatures, merge_single_message_aggregates, setup_prover,
setup_verifier, split_multi_message_aggregate, verify_multi_message_aggregate,
verify_single_message_aggregate,
};
use std::sync::{Mutex, MutexGuard};
use thiserror::Error;
use tracing::error;

#[cfg(feature = "shadow-integration")]
pub mod shadow_cost;

/// log(1/rate) for the WHIR commitment scheme used inside the aggregation prover.
const LOG_INV_RATE: usize = 2;

/// Ensure the aggregation bytecode is compiled. Safe to call multiple times.
///
/// leanVM's `main` replaced the old `setup_prover`/`setup_verifier` split with a
/// single self-referential aggregation bytecode used by both the prover and the
/// verifier; `init_aggregation_bytecode` is idempotent (`OnceLock::get_or_init`).
/// leanVM allows one proof at a time per process; a second one panics.
static PROVER_PERMIT: Mutex<()> = Mutex::new(());

/// The permit guards no data, so a poisoned lock is recovered rather than propagated:
/// one panicking prover must not brick every later proof. It is still an incident.
fn prover_permit() -> MutexGuard<'static, ()> {
PROVER_PERMIT.lock().unwrap_or_else(|poisoned| {
error!("a previous proving job panicked while holding the permit; continuing");
poisoned.into_inner()
})
}

/// Engages leanVM's arena; skipping it stays correct but slow.
pub fn ensure_prover_ready() {
init_aggregation_bytecode();
setup_prover();
}

/// Ensure the aggregation bytecode is compiled. Safe to call multiple times.
///
/// Deserializing any proof (`from_bytes`) also requires the bytecode, so this
/// must run before any verification path.
/// Needed before any decode, not just before verifying.
pub fn ensure_verifier_ready() {
init_aggregation_bytecode();
setup_verifier();
}

/// Error type for signature aggregation operations.
Expand Down Expand Up @@ -187,6 +194,7 @@ pub fn aggregate_signatures(
}

ensure_prover_ready();
let _permit = prover_permit();

let raw_xmss: Vec<(LeanSigPublicKey, LeanSigSignature)> = public_keys
.into_iter()
Expand Down Expand Up @@ -242,6 +250,7 @@ pub fn aggregate_mixed(
}

ensure_prover_ready();
let _permit = prover_permit();

let children_native: Vec<LMType1> = children
.into_iter()
Expand Down Expand Up @@ -296,6 +305,7 @@ pub fn aggregate_proofs(
}

ensure_prover_ready();
let _permit = prover_permit();

let children_native: Vec<LMType1> = children
.into_iter()
Expand Down Expand Up @@ -391,6 +401,7 @@ pub fn merge_type_1s_into_type_2(
}

ensure_prover_ready();
let _permit = prover_permit();

let type_1s_native: Vec<LMType1> = type_1s
.into_iter()
Expand Down Expand Up @@ -485,6 +496,7 @@ pub fn split_type_2_by_message(
}

ensure_prover_ready();
let _permit = prover_permit();

let pubkeys_per_info: Vec<Vec<LeanSigPublicKey>> = pubkeys_per_component
.into_iter()
Expand Down Expand Up @@ -515,8 +527,8 @@ pub fn split_type_2_by_message(
#[cfg(test)]
mod tests {
use super::*;
use lean_multisig::{xmss_key_gen_from_seed, xmss_sign};
use ssz::Encode as _;
use xmss::{xmss_key_gen_from_seed, xmss_sign};

/// Generate a test keypair and sign a message.
///
Expand Down
Loading