ecrecover/
ecrecover.rs
1use clap::Parser;
2use eyre::Result;
3use k256::ecdsa::{SigningKey, VerifyingKey};
4use num_bigint::BigUint;
5use openvm_algebra_circuit::{
6 ModularExtension, ModularExtensionExecutor, ModularExtensionPeriphery,
7};
8use openvm_algebra_transpiler::ModularTranspilerExtension;
9use openvm_benchmarks_prove::util::BenchmarkCli;
10use openvm_circuit::{
11 arch::{instructions::exe::VmExe, SystemConfig},
12 derive::VmConfig,
13};
14use openvm_ecc_circuit::{
15 CurveConfig, WeierstrassExtension, WeierstrassExtensionExecutor, WeierstrassExtensionPeriphery,
16 SECP256K1_CONFIG,
17};
18use openvm_ecc_transpiler::EccTranspilerExtension;
19use openvm_keccak256_circuit::{Keccak256, Keccak256Executor, Keccak256Periphery};
20use openvm_keccak256_transpiler::Keccak256TranspilerExtension;
21use openvm_rv32im_circuit::{
22 Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M,
23 Rv32MExecutor, Rv32MPeriphery,
24};
25use openvm_rv32im_transpiler::{
26 Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension,
27};
28use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField32};
29use openvm_stark_sdk::{bench::run_with_metric_collection, p3_baby_bear::BabyBear};
30use openvm_transpiler::{transpiler::Transpiler, FromElf};
31use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
32use serde::{Deserialize, Serialize};
33use tiny_keccak::{Hasher, Keccak};
34
35fn make_input(signing_key: &SigningKey, msg: &[u8]) -> Vec<BabyBear> {
36 let mut hasher = Keccak::v256();
37 hasher.update(msg);
38 let mut prehash = [0u8; 32];
39 hasher.finalize(&mut prehash);
40 let (signature, recid) = signing_key.sign_prehash_recoverable(&prehash).unwrap();
41 let mut input = prehash.to_vec();
43 let v = recid.to_byte() + 27u8;
44 input.extend_from_slice(&[0; 31]);
45 input.push(v);
46 input.extend_from_slice(signature.to_bytes().as_ref());
47
48 input.into_iter().map(BabyBear::from_canonical_u8).collect()
49}
50
51#[derive(Clone, Debug, VmConfig, derive_new::new, Serialize, Deserialize)]
52pub struct Rv32ImEcRecoverConfig {
53 #[system]
54 pub system: SystemConfig,
55 #[extension]
56 pub base: Rv32I,
57 #[extension]
58 pub mul: Rv32M,
59 #[extension]
60 pub io: Rv32Io,
61 #[extension]
62 pub modular: ModularExtension,
63 #[extension]
64 pub keccak: Keccak256,
65 #[extension]
66 pub weierstrass: WeierstrassExtension,
67}
68
69impl Rv32ImEcRecoverConfig {
70 pub fn for_curves(curves: Vec<CurveConfig>) -> Self {
71 let primes: Vec<BigUint> = curves
72 .iter()
73 .flat_map(|c| [c.modulus.clone(), c.scalar.clone()])
74 .collect();
75 Self {
76 system: SystemConfig::default().with_continuations(),
77 base: Default::default(),
78 mul: Default::default(),
79 io: Default::default(),
80 modular: ModularExtension::new(primes),
81 keccak: Default::default(),
82 weierstrass: WeierstrassExtension::new(curves),
83 }
84 }
85}
86
87fn main() -> Result<()> {
88 let args = BenchmarkCli::parse();
89
90 let elf = args.build_bench_program("ecrecover")?;
91 let exe = VmExe::from_elf(
92 elf,
93 Transpiler::<BabyBear>::default()
94 .with_extension(Rv32ITranspilerExtension)
95 .with_extension(Rv32MTranspilerExtension)
96 .with_extension(Rv32IoTranspilerExtension)
97 .with_extension(Keccak256TranspilerExtension)
98 .with_extension(ModularTranspilerExtension)
99 .with_extension(EccTranspilerExtension),
100 )?;
101
102 run_with_metric_collection("OUTPUT_PATH", || -> Result<()> {
103 let mut rng = ChaCha8Rng::seed_from_u64(12345);
104 let signing_key: SigningKey = SigningKey::random(&mut rng);
105 let verifying_key = VerifyingKey::from(&signing_key);
106 let mut hasher = Keccak::v256();
107 let mut expected_address = [0u8; 32];
108 hasher.update(
109 &verifying_key
110 .to_encoded_point(false)
111 .as_bytes()[1..],
112 );
113 hasher.finalize(&mut expected_address);
114 expected_address[..12].fill(0); let mut input_stream = vec![expected_address
116 .into_iter()
117 .map(BabyBear::from_canonical_u8)
118 .collect::<Vec<_>>()];
119
120 let msg = ["Elliptic", "Curve", "Digital", "Signature", "Algorithm"];
121 input_stream.extend(
122 msg.iter()
123 .map(|s| make_input(&signing_key, s.as_bytes()))
124 .collect::<Vec<_>>(),
125 );
126 args.bench_from_exe(
127 "ecrecover_program",
128 Rv32ImEcRecoverConfig::for_curves(vec![SECP256K1_CONFIG.clone()]),
129 exe,
130 input_stream.into(),
131 )
132 })
133}