Skip to content

Commit 585fad0

Browse files
feat(agg-mode): validate signature and recover address (#2199)
Co-authored-by: maximopalopoli <[email protected]>
1 parent b6e50e4 commit 585fad0

File tree

20 files changed

+531
-24
lines changed

20 files changed

+531
-24
lines changed

Makefile

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,10 @@ agg_mode_batcher_send_payment:
330330
--private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
331331

332332
agg_mode_batcher_send_sp1_proof:
333-
@NONCE=$$(curl -s http://127.0.0.1:8089/nonce/0x70997970C51812dc3A010C7d01b50e0d17dc79C8 | jq -r '.data.nonce'); \
334-
curl -X POST \
335-
-H "Content-Type: multipart/form-data" \
336-
-F "nonce=$${NONCE}" \
337-
-F "proof=@scripts/test_files/sp1/sp1_fibonacci_5_0_0.proof" \
338-
-F "program_vk=@scripts/test_files/sp1/sp1_fibonacci_5_0_0_vk.bin" \
339-
-F "_signature_hex=0x0" \
340-
http://127.0.0.1:8089/proof/sp1
333+
@cargo run --manifest-path aggregation_mode/cli/Cargo.toml -- submit sp1 \
334+
--proof scripts/test_files/sp1/sp1_fibonacci_5_0_0.proof \
335+
--vk scripts/test_files/sp1/sp1_fibonacci_5_0_0_vk.bin \
336+
--private-key "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
341337

342338
__AGGREGATOR__: ## ____
343339

aggregation_mode/Cargo.lock

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aggregation_mode/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
resolver = "2"
3-
members = ["./batcher", "./proof_aggregator", "./db"]
3+
members = ["./batcher", "./proof_aggregator", "./db", "./sdk", "./cli"]
44

55
[workspace.package]
66
version = "0.1.0"

aggregation_mode/batcher/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2021"
77
serde = { workspace = true }
88
serde_json = { workspace = true }
99
serde_yaml = { workspace = true }
10+
agg_mode_sdk = { path = "../sdk"}
1011
aligned-sdk = { workspace = true }
1112
sp1-sdk = { workspace = true }
1213
tracing = { version = "0.1", features = ["log"] }

aggregation_mode/batcher/src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct Config {
88
pub db_connection_url: String,
99
pub eth_rpc_url: String,
1010
pub payment_service_address: String,
11+
pub network: String,
1112
pub max_daily_proofs_per_user: i64,
1213
}
1314

aggregation_mode/batcher/src/server/http.rs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use actix_web::{
88
web::{self, Data},
99
App, HttpRequest, HttpResponse, HttpServer, Responder,
1010
};
11+
use agg_mode_sdk::types::Network;
1112
use aligned_sdk::aggregation_layer::AggregationModeProvingSystem;
13+
use alloy::signers::Signature;
1214
use sp1_sdk::{SP1ProofWithPublicValues, SP1VerifyingKey};
1315
use sqlx::types::BigDecimal;
1416

@@ -28,11 +30,17 @@ use crate::{
2830
pub struct BatcherServer {
2931
db: Db,
3032
config: Config,
33+
network: Network,
3134
}
3235

3336
impl BatcherServer {
3437
pub fn new(db: Db, config: Config) -> Self {
35-
Self { db, config }
38+
let network = Network::from_str(&config.network).expect("A valid network in config file");
39+
Self {
40+
db,
41+
config,
42+
network,
43+
}
3644
}
3745

3846
pub async fn start(&self) {
@@ -93,13 +101,40 @@ impl BatcherServer {
93101
req: HttpRequest,
94102
MultipartForm(data): MultipartForm<SubmitProofRequestSP1>,
95103
) -> impl Responder {
96-
let recovered_address = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_lowercase();
97-
98104
let Some(state) = req.app_data::<Data<BatcherServer>>() else {
99105
return HttpResponse::InternalServerError()
100106
.json(AppResponse::new_unsucessfull("Internal server error", 500));
101107
};
108+
102109
let state = state.get_ref();
110+
let Ok(signature) = Signature::from_str(&data.signature_hex.0) else {
111+
return HttpResponse::InternalServerError()
112+
.json(AppResponse::new_unsucessfull("Invalid signature", 500));
113+
};
114+
115+
let Ok(proof_content) = tokio::fs::read(data.proof.file.path()).await else {
116+
return HttpResponse::InternalServerError()
117+
.json(AppResponse::new_unsucessfull("Internal server error", 500));
118+
};
119+
120+
let Ok(vk_content) = tokio::fs::read(data.program_vk.file.path()).await else {
121+
return HttpResponse::InternalServerError()
122+
.json(AppResponse::new_unsucessfull("Internal server error", 500));
123+
};
124+
125+
// reconstruct message and recover address
126+
let msg = agg_mode_sdk::gateway::types::SubmitSP1ProofMessage::new(
127+
data.nonce.0,
128+
proof_content.clone(),
129+
vk_content.clone(),
130+
);
131+
let Ok(recovered_address) =
132+
signature.recover_address_from_prehash(&msg.eip712_hash(&state.network).into())
133+
else {
134+
return HttpResponse::InternalServerError()
135+
.json(AppResponse::new_unsucessfull("Internal server error", 500));
136+
};
137+
let recovered_address = recovered_address.to_string().to_lowercase();
103138

104139
// Checking if this address has submited more proofs than the ones allowed per day
105140
let Ok(daily_tasks_by_address) = state
@@ -160,22 +195,11 @@ impl BatcherServer {
160195
400,
161196
));
162197
}
163-
164-
let Ok(proof_content) = tokio::fs::read(data.proof.file.path()).await else {
165-
return HttpResponse::InternalServerError()
166-
.json(AppResponse::new_unsucessfull("Internal server error", 500));
167-
};
168-
169198
let Ok(proof) = bincode::deserialize::<SP1ProofWithPublicValues>(&proof_content) else {
170199
return HttpResponse::BadRequest()
171200
.json(AppResponse::new_unsucessfull("Invalid SP1 proof", 400));
172201
};
173202

174-
let Ok(vk_content) = tokio::fs::read(data.program_vk.file.path()).await else {
175-
return HttpResponse::InternalServerError()
176-
.json(AppResponse::new_unsucessfull("Internal server error", 500));
177-
};
178-
179203
let Ok(vk) = bincode::deserialize::<SP1VerifyingKey>(&vk_content) else {
180204
return HttpResponse::BadRequest()
181205
.json(AppResponse::new_unsucessfull("Invalid vk", 400));

aggregation_mode/batcher/src/server/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub(super) struct SubmitProofRequestSP1 {
4444
pub nonce: Text<u64>,
4545
pub proof: TempFile,
4646
pub program_vk: TempFile,
47-
pub _signature_hex: Text<String>,
47+
pub signature_hex: Text<String>,
4848
}
4949

5050
#[derive(Debug, MultipartForm)]

aggregation_mode/cli/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "agg_mode_cli"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
serde = { workspace = true }
8+
tracing = { version = "0.1", features = ["log"] }
9+
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
10+
bincode = "1.3.3"
11+
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
12+
alloy = { workspace = true }
13+
agg_mode_sdk = { path = "../sdk"}
14+
sp1-sdk = "5.0.0"
15+
clap = { version = "4.5.4", features = ["derive"] }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod submit;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use agg_mode_sdk::{gateway::provider::AggregationModeGatewayProvider, types::Network};
2+
use alloy::signers::local::LocalSigner;
3+
use clap::{command, Args, Subcommand};
4+
use sp1_sdk::{SP1ProofWithPublicValues, SP1VerifyingKey};
5+
use std::{path::PathBuf, str::FromStr};
6+
7+
#[derive(Debug, Subcommand)]
8+
pub enum SubmitCommand {
9+
#[command(name = "sp1")]
10+
SP1(SubmitSP1Args),
11+
}
12+
13+
#[derive(Debug, Clone, Args)]
14+
pub struct SubmitSP1Args {
15+
#[arg(short = 'p', long = "proof")]
16+
proof_path: PathBuf,
17+
#[arg(long = "vk")]
18+
verifying_key_path: PathBuf,
19+
#[arg(long = "private-key")]
20+
private_key: String,
21+
#[arg(short = 'n', long = "network", default_value = "devnet", value_parser = parse_network)]
22+
network: Network,
23+
}
24+
25+
fn parse_network(value: &str) -> Result<Network, String> {
26+
Network::from_str(value).map_err(|_| format!("unsupported network supplied: {value}"))
27+
}
28+
29+
pub async fn run(args: SubmitSP1Args) {
30+
tracing::info!("Submitting SP1 proof to {:?} ", args.network);
31+
32+
let proof = load_proof(&args.proof_path).expect("Valid proof");
33+
let vk = load_vk(&args.verifying_key_path).expect("Valid vk");
34+
35+
let signer =
36+
LocalSigner::from_str(args.private_key.trim()).expect("failed to parse private key: {e}");
37+
38+
let provider = AggregationModeGatewayProvider::new_with_signer(args.network.clone(), signer)
39+
.expect("failed to initialize gateway client: {e:?}");
40+
41+
let response = provider
42+
.submit_sp1_proof(&proof, &vk)
43+
.await
44+
.expect("failed to submit proof: {e:?}");
45+
46+
tracing::info!(
47+
"Proof submitted successfully. Task ID: {}",
48+
response.data.task_id
49+
);
50+
}
51+
52+
fn load_proof(path: &PathBuf) -> Result<SP1ProofWithPublicValues, String> {
53+
let bytes = std::fs::read(path)
54+
.map_err(|e| format!("failed to read proof from {}: {e}", path.display()))?;
55+
56+
bincode::deserialize(&bytes)
57+
.map_err(|e| format!("failed to deserialize proof {}: {e}", path.display()))
58+
}
59+
60+
fn load_vk(path: &PathBuf) -> Result<SP1VerifyingKey, String> {
61+
let bytes = std::fs::read(path)
62+
.map_err(|e| format!("failed to read verifying key from {}: {e}", path.display()))?;
63+
64+
bincode::deserialize(&bytes).map_err(|e| {
65+
format!(
66+
"failed to deserialize verifying key {}: {e}",
67+
path.display()
68+
)
69+
})
70+
}

0 commit comments

Comments
 (0)