Skip to content

Commit 0c0759e

Browse files
ilitterixqft
andauthored
feat(l2): add timed prover API (#5478)
**Motivation** To measure execution and proving time correctly. This means removing the client initialization from the equation. In the case of ZisK, we use it's own timing exposed via a JSON file generated after proving. **Description** Adds `_timed` versions for execution and proving for all zkVM backends. --------- Co-authored-by: Estéfano Bargas <estefano.bargas@fing.edu.uy>
1 parent e982a7d commit 0c0759e

File tree

10 files changed

+272
-51
lines changed

10 files changed

+272
-51
lines changed

crates/l2/prover/Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,7 @@ openvm = [
7979
profiling = ["sp1-sdk?/profiling"]
8080

8181
gpu = ["risc0-zkvm?/cuda", "sp1-sdk?/cuda", "openvm-sdk?/cuda"]
82-
l2 = [
83-
"guest_program/l2",
84-
"ethrex-l2/l2",
85-
]
82+
l2 = ["guest_program/l2", "ethrex-l2/l2"]
8683

8784
# temporary feature until we fix cargo-zisk setup-rom from failing in the CI
8885
ci = ["guest_program/ci"]

crates/l2/prover/src/backend/exec.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::time::Instant;
1+
use std::time::{Duration, Instant};
22
use tracing::{info, warn};
33

44
use ethrex_l2_common::{
@@ -16,6 +16,14 @@ pub fn execute(input: ProgramInput) -> Result<(), Box<dyn std::error::Error>> {
1616
Ok(())
1717
}
1818

19+
pub fn execute_timed(input: ProgramInput) -> Result<Duration, Box<dyn std::error::Error>> {
20+
let now = Instant::now();
21+
execution_program(input)?;
22+
let duration = now.elapsed();
23+
24+
Ok(duration)
25+
}
26+
1927
pub fn prove(
2028
input: ProgramInput,
2129
_format: ProofFormat,
@@ -25,6 +33,18 @@ pub fn prove(
2533
Ok(output)
2634
}
2735

36+
pub fn prove_timed(
37+
input: ProgramInput,
38+
_format: ProofFormat,
39+
) -> Result<(ProgramOutput, Duration), Box<dyn std::error::Error>> {
40+
warn!("\"exec\" prover backend generates no proof, only executes");
41+
let now = Instant::now();
42+
let output = execution_program(input)?;
43+
let duration = now.elapsed();
44+
45+
Ok((output, duration))
46+
}
47+
2848
pub fn verify(_proof: &ProgramOutput) -> Result<(), Box<dyn std::error::Error>> {
2949
warn!("\"exec\" prover backend generates no proof, verification always succeeds");
3050
Ok(())

crates/l2/prover/src/backend/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub mod zisk;
1818
#[cfg(feature = "openvm")]
1919
pub mod openvm;
2020

21-
#[derive(Default, Debug, Deserialize, Serialize, Copy, Clone, ValueEnum)]
21+
#[derive(Default, Debug, Deserialize, Serialize, Copy, Clone, ValueEnum, PartialEq)]
2222
pub enum Backend {
2323
#[default]
2424
Exec,

crates/l2/prover/src/backend/openvm.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ pub fn execute(input: ProgramInput) -> Result<(), Box<dyn std::error::Error>> {
2424
Ok(())
2525
}
2626

27+
pub fn execute_timed(
28+
input: ProgramInput,
29+
) -> Result<std::time::Duration, Box<dyn std::error::Error>> {
30+
let sdk = Sdk::standard();
31+
32+
let mut stdin = StdIn::default();
33+
let bytes = rkyv::to_bytes::<Error>(&input)?;
34+
stdin.write_bytes(bytes.as_slice());
35+
36+
let start = std::time::Instant::now();
37+
sdk.execute(PROGRAM_ELF, stdin.clone())?;
38+
let duration = start.elapsed();
39+
40+
Ok(duration)
41+
}
42+
2743
pub fn prove(
2844
input: ProgramInput,
2945
format: ProofFormat,
@@ -48,6 +64,32 @@ pub fn prove(
4864
Ok(proof)
4965
}
5066

67+
pub fn prove_timed(
68+
input: ProgramInput,
69+
format: ProofFormat,
70+
) -> Result<(ProveOutput, std::time::Duration), Box<dyn std::error::Error>> {
71+
let sdk = Sdk::standard();
72+
73+
let mut stdin = StdIn::default();
74+
let bytes = rkyv::to_bytes::<Error>(&input)?;
75+
stdin.write_bytes(bytes.as_slice());
76+
77+
let start = std::time::Instant::now();
78+
let proof = match format {
79+
ProofFormat::Compressed => {
80+
let (proof, _) = sdk.prove(PROGRAM_ELF, stdin.clone())?;
81+
ProveOutput::Compressed(proof)
82+
}
83+
ProofFormat::Groth16 => {
84+
let proof = sdk.prove_evm(PROGRAM_ELF, stdin.clone())?;
85+
ProveOutput::Groth16(proof)
86+
}
87+
};
88+
let duration = start.elapsed();
89+
90+
Ok((proof, duration))
91+
}
92+
5193
pub fn to_batch_proof(
5294
_proof: ProveOutput,
5395
_format: ProofFormat,

crates/l2/prover/src/backend/risc0.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use risc0_zkvm::{
1212
serde::Error as Risc0SerdeError,
1313
};
1414
use rkyv::rancor::Error as RkyvError;
15-
use std::time::Instant;
15+
use std::time::{Duration, Instant};
1616
use tracing::info;
1717

1818
#[derive(thiserror::Error, Debug)]
@@ -39,15 +39,26 @@ pub fn execute(input: ProgramInput) -> Result<(), Box<dyn std::error::Error>> {
3939

4040
let executor = default_executor();
4141

42-
let now = Instant::now();
4342
let _session_info = executor.execute(env, ZKVM_RISC0_PROGRAM_ELF)?;
44-
let elapsed = now.elapsed();
45-
46-
info!("Successfully executed RISC0 program in {elapsed:.2?}");
4743

4844
Ok(())
4945
}
5046

47+
pub fn execute_timed(input: ProgramInput) -> Result<Duration, Box<dyn std::error::Error>> {
48+
let bytes = rkyv::to_bytes::<RkyvError>(&input)?;
49+
let env = ExecutorEnv::builder()
50+
.write_slice(bytes.as_slice())
51+
.build()?;
52+
53+
let executor = default_executor();
54+
55+
let start = Instant::now();
56+
let _session_info = executor.execute(env, ZKVM_RISC0_PROGRAM_ELF)?;
57+
let duration = start.elapsed();
58+
59+
Ok(duration)
60+
}
61+
5162
pub fn prove(
5263
input: ProgramInput,
5364
format: ProofFormat,
@@ -67,15 +78,37 @@ pub fn prove(
6778
ProofFormat::Groth16 => ProverOpts::groth16(),
6879
};
6980

70-
let now = Instant::now();
7181
let prove_info = prover.prove_with_opts(env, ZKVM_RISC0_PROGRAM_ELF, &prover_opts)?;
72-
let elapsed = now.elapsed();
73-
74-
info!("Successfully proved RISC0 program in {elapsed:.2?}");
7582

7683
Ok(prove_info.receipt)
7784
}
7885

86+
pub fn prove_timed(
87+
input: ProgramInput,
88+
format: ProofFormat,
89+
) -> Result<(Receipt, Duration), Box<dyn std::error::Error>> {
90+
let mut stdout = Vec::new();
91+
92+
let bytes = rkyv::to_bytes::<RkyvError>(&input)?;
93+
let env = ExecutorEnv::builder()
94+
.stdout(&mut stdout)
95+
.write_slice(bytes.as_slice())
96+
.build()?;
97+
98+
let prover = default_prover();
99+
100+
let prover_opts = match format {
101+
ProofFormat::Compressed => ProverOpts::succinct(),
102+
ProofFormat::Groth16 => ProverOpts::groth16(),
103+
};
104+
105+
let start = Instant::now();
106+
let prove_info = prover.prove_with_opts(env, ZKVM_RISC0_PROGRAM_ELF, &prover_opts)?;
107+
let duration = start.elapsed();
108+
109+
Ok((prove_info.receipt, duration))
110+
}
111+
79112
pub fn verify(receipt: &Receipt) -> Result<(), Error> {
80113
receipt.verify(ZKVM_RISC0_PROGRAM_ID)?;
81114
Ok(())

crates/l2/prover/src/backend/sp1.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ use sp1_sdk::{
1313
HashableKey, Prover, SP1ProofMode, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin,
1414
SP1VerifyingKey,
1515
};
16-
use std::{fmt::Debug, sync::OnceLock, time::Instant};
16+
use std::{
17+
fmt::Debug,
18+
sync::OnceLock,
19+
time::{Duration, Instant},
20+
};
1721
use tracing::info;
1822
use url::Url;
1923

@@ -84,15 +88,25 @@ pub fn execute(input: ProgramInput) -> Result<(), Box<dyn std::error::Error>> {
8488

8589
let setup = PROVER_SETUP.get_or_init(|| init_prover_setup(None));
8690

87-
let now = Instant::now();
8891
setup.client.execute(ZKVM_SP1_PROGRAM_ELF, &stdin)?;
89-
let elapsed = now.elapsed();
90-
91-
info!("Successfully executed SP1 program in {elapsed:.2?}");
9292

9393
Ok(())
9494
}
9595

96+
pub fn execute_timed(input: ProgramInput) -> Result<Duration, Box<dyn std::error::Error>> {
97+
let mut stdin = SP1Stdin::new();
98+
let bytes = rkyv::to_bytes::<Error>(&input)?;
99+
stdin.write_slice(bytes.as_slice());
100+
101+
let setup = PROVER_SETUP.get_or_init(|| init_prover_setup(None));
102+
103+
let start = Instant::now();
104+
setup.client.execute(ZKVM_SP1_PROGRAM_ELF, &stdin)?;
105+
let duration = start.elapsed();
106+
107+
Ok(duration)
108+
}
109+
96110
pub fn prove(
97111
input: ProgramInput,
98112
format: ProofFormat,
@@ -103,21 +117,38 @@ pub fn prove(
103117

104118
let setup = PROVER_SETUP.get_or_init(|| init_prover_setup(None));
105119

106-
// contains the receipt along with statistics about execution of the guest
107120
let format = match format {
108121
ProofFormat::Compressed => SP1ProofMode::Compressed,
109122
ProofFormat::Groth16 => SP1ProofMode::Groth16,
110123
};
111124

112-
let now = Instant::now();
113125
let proof = setup.client.prove(&setup.pk, &stdin, format)?;
114-
let elapsed = now.elapsed();
115-
116-
info!("Successfully proved SP1 program in {elapsed:.2?}");
117126

118127
Ok(ProveOutput::new(proof, setup.vk.clone()))
119128
}
120129

130+
pub fn prove_timed(
131+
input: ProgramInput,
132+
format: ProofFormat,
133+
) -> Result<(ProveOutput, Duration), Box<dyn std::error::Error>> {
134+
let mut stdin = SP1Stdin::new();
135+
let bytes = rkyv::to_bytes::<Error>(&input)?;
136+
stdin.write_slice(bytes.as_slice());
137+
138+
let setup = PROVER_SETUP.get_or_init(|| init_prover_setup(None));
139+
140+
let format = match format {
141+
ProofFormat::Compressed => SP1ProofMode::Compressed,
142+
ProofFormat::Groth16 => SP1ProofMode::Groth16,
143+
};
144+
145+
let start = Instant::now();
146+
let proof = setup.client.prove(&setup.pk, &stdin, format)?;
147+
let duration = start.elapsed();
148+
149+
Ok((ProveOutput::new(proof, setup.vk.clone()), duration))
150+
}
151+
121152
pub fn verify(output: &ProveOutput) -> Result<(), Box<dyn std::error::Error>> {
122153
let setup = PROVER_SETUP.get_or_init(|| init_prover_setup(None));
123154
setup.client.verify(&output.proof, &output.vk)?;

crates/l2/prover/src/backend/zisk.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,34 @@ pub fn execute(input: ProgramInput) -> Result<(), Box<dyn std::error::Error>> {
3636
Ok(())
3737
}
3838

39+
pub fn execute_timed(
40+
input: ProgramInput,
41+
) -> Result<std::time::Duration, Box<dyn std::error::Error>> {
42+
write_elf_file()?;
43+
44+
let input_bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&input)?;
45+
std::fs::write(INPUT_PATH, input_bytes.as_slice())?;
46+
47+
let start = std::time::Instant::now();
48+
let args = vec!["--elf", ELF_PATH, "--inputs", INPUT_PATH];
49+
let output = Command::new("ziskemu")
50+
.args(args)
51+
.stdin(Stdio::inherit())
52+
.stderr(Stdio::inherit())
53+
.output()?;
54+
55+
if !output.status.success() {
56+
return Err(format!(
57+
"ZisK execution failed: {}",
58+
String::from_utf8_lossy(&output.stderr)
59+
)
60+
.into());
61+
}
62+
let duration = start.elapsed();
63+
64+
Ok(duration)
65+
}
66+
3967
pub fn prove(
4068
input: ProgramInput,
4169
format: ProofFormat,
@@ -84,6 +112,30 @@ pub fn prove(
84112
Ok(output)
85113
}
86114

115+
pub fn prove_timed(
116+
input: ProgramInput,
117+
format: ProofFormat,
118+
) -> Result<(ProveOutput, std::time::Duration), Box<dyn std::error::Error>> {
119+
let proof = prove(input, format)?;
120+
121+
#[derive(serde::Deserialize)]
122+
struct ZisKResult {
123+
#[serde(rename = "cycles")]
124+
_cycles: u64,
125+
#[serde(rename = "id")]
126+
_id: String,
127+
time: f64,
128+
}
129+
130+
let zisk_result_bytes = std::fs::read(format!("{OUTPUT_DIR_PATH}/result.json"))?;
131+
132+
let zisk_result: ZisKResult = serde_json::from_slice(&zisk_result_bytes)?;
133+
134+
let duration = std::time::Duration::from_secs_f64(zisk_result.time);
135+
136+
Ok((proof, duration))
137+
}
138+
87139
pub fn verify(_output: &ProgramOutput) -> Result<(), Box<dyn std::error::Error>> {
88140
unimplemented!("verify is not implemented for ZisK backend")
89141
}

0 commit comments

Comments
 (0)