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
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,11 @@ jobs:

- name: Run fixture-based tests
uses: ./.github/actions/run-fixture-tests

# Reuses the release build from the test step; validates the benchmark
# harness end-to-end and its JSON output contract in a few seconds.
- name: Benchmark smoke (mock crypto)
run: |
cargo run --release --bin ethlambda -- benchmark synthetic --mock-crypto \
--num-validators 4 --warmup-slots 4 --iterations 3 --format json \
| jq -e '.schema_version == 1 and (.samples | length == 3)'
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 bin/ethlambda/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ tracing.workspace = true
tracing-subscriber = "0.3"

serde.workspace = true
serde_json.workspace = true
serde_yaml_ng.workspace = true
hex.workspace = true

Expand Down
73 changes: 73 additions & 0 deletions bin/ethlambda/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
use std::path::PathBuf;

use vergen_git2::{Emitter, Git2Builder, RustcBuilder};

/// Crate names whose resolved git revision is embedded in the binary, one per
/// upstream crypto repository: `leansig` for leanSig, `lean-multisig` for
/// leanVM (the direct dependency `ethlambda-crypto` builds against).
const LEANSIG_PACKAGE: &str = "leansig";
const LEANVM_PACKAGE: &str = "lean-multisig";

fn main() -> Result<(), Box<dyn std::error::Error>> {
let git2 = Git2Builder::default().branch(true).sha(true).build()?;
let rustc = RustcBuilder::default()
Expand All @@ -12,5 +20,70 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.add_instructions(&git2)?
.emit()?;

emit_crypto_revs();

Ok(())
}

/// Embed the resolved leanSig and leanVM git revisions from the workspace
/// Cargo.lock.
///
/// The crypto dependencies are pinned upstream (leansig to a moving branch,
/// leanVM to a rev), so a `cargo update` or a rev bump changes the measured
/// crypto with little or no ethlambda diff; benchmark reports embed these
/// revisions to keep results interpretable across lock bumps.
fn emit_crypto_revs() {
let revs = lockfile_git_revs();
for (package, env_var) in [
(LEANSIG_PACKAGE, "ETHLAMBDA_LEANSIG_REV"),
(LEANVM_PACKAGE, "ETHLAMBDA_LEANVM_REV"),
] {
let rev = revs
.as_ref()
.and_then(|revs| revs.get(package).cloned())
.unwrap_or_else(|| "unknown".to_string());
println!("cargo:rustc-env={env_var}={rev}");
}
if let Some(lockfile) = workspace_lockfile() {
println!("cargo:rerun-if-changed={}", lockfile.display());
}
}

fn workspace_lockfile() -> Option<PathBuf> {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").ok()?;
Some(PathBuf::from(manifest_dir).join("../../Cargo.lock"))
}

/// Map each git-sourced package in the lockfile to its resolved revision.
///
/// Both fields of a `[[package]]` block are collected before the revision is
/// extracted, so the result does not depend on TOML field order within the
/// table (a lock-file reformatter emitting `source` before `name` would
/// otherwise silently yield "unknown").
fn lockfile_git_revs() -> Option<std::collections::HashMap<String, String>> {
let lockfile = std::fs::read_to_string(workspace_lockfile()?).ok()?;
let mut revs = std::collections::HashMap::new();
// A lockfile is a flat sequence of `[[package]]` blocks; splitting on the
// header gives one chunk per package (the first chunk is the file preamble,
// which has no `name` and is skipped).
for block in lockfile.split("[[package]]") {
let mut name = None;
let mut source = None;
for line in block.lines() {
let line = line.trim();
if let Some(value) = line.strip_prefix("name = ") {
name = Some(value.trim_matches('"').to_string());
} else if let Some(value) = line.strip_prefix("source = ") {
source = Some(value.trim_matches('"').to_string());
}
}
// source = "git+https://github.com/leanEthereum/leanSig?branch=devnet4#<rev>"
let (Some(name), Some(source)) = (name, source) else {
continue;
};
if let Some(rev) = source.strip_prefix("git+").and_then(|s| s.rsplit_once('#')) {
revs.insert(name, rev.1.to_string());
}
}
Some(revs)
}
25 changes: 24 additions & 1 deletion bin/ethlambda/src/benchmark/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod corpus;
mod report;

use std::collections::{BTreeMap, HashMap};
use std::path::PathBuf;
use std::time::Instant;

use ethlambda_blockchain::block_builder::ProposerConfig;
Expand Down Expand Up @@ -119,6 +120,19 @@ struct CommonOptions {
/// Mirrors the node flag: distinct AttestationData cap per built block.
#[arg(long, default_value = "3")]
max_attestations_per_block: usize,
/// Report format printed to stdout. Logs go to stderr, so JSON output can
/// be piped directly (e.g. into jq).
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
format: OutputFormat,
/// Also write the JSON report to this file.
#[arg(long)]
output: Option<PathBuf>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
enum OutputFormat {
Human,
Json,
}

pub(crate) fn run(options: BenchmarkOptions) -> eyre::Result<()> {
Expand Down Expand Up @@ -168,7 +182,16 @@ fn run_synthetic(options: SyntheticOptions) -> eyre::Result<()> {
);

let report = Report::new(Environment::collect(), Params::from(&options), samples);
println!("{}", report.human_table());

match common.format {
OutputFormat::Human => println!("{}", report.human_table()),
OutputFormat::Json => println!("{}", report.to_json()?),
}
if let Some(path) = &common.output {
std::fs::write(path, report.to_json()?)
.wrap_err_with(|| format!("failed to write report to {}", path.display()))?;
eprintln!("report written to {}", path.display());
}

Ok(())
}
Expand Down
Loading