Skip to content

Commit de494cb

Browse files
Merge branch 'staging' into feataggmode/separate-pollet-http-server
2 parents 988e8f5 + 8019324 commit de494cb

22 files changed

Lines changed: 539 additions & 38 deletions

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ proof_aggregator_start_dev: is_aggregator_set reset_last_aggregated_block ./aggr
252252
proof_aggregator_start_dev_ethereum_package: is_aggregator_set reset_last_aggregated_block ./aggregation_mode/target/release/proof_aggregator_dev ## Starts proof aggregator with mock proofs (DEV mode) in ethereum package. Parameters: AGGREGATOR=<sp1|risc0>
253253
AGGREGATOR=$(AGGREGATOR) RISC0_DEV_MODE=1 ./aggregation_mode/target/release/proof_aggregator_dev config-files/config-proof-aggregator-mock-ethereum-package.yaml
254254

255+
proof_aggregator_test_without_compiling_agg_programs:
256+
cd aggregation_mode && SKIP_AGG_PROGRAMS_BUILD=1 cargo test -p proof_aggregator --tests -- --nocapture
257+
255258
### All CPU proof aggregator receipts
256259
./aggregation_mode/target/release/proof_aggregator_cpu: $(AGGREGATION_MODE_SOURCES)
257260
AGGREGATOR=$(AGGREGATOR) cargo build --features prove --manifest-path ./aggregation_mode/Cargo.toml --release --bin proof_aggregator_cpu
@@ -326,6 +329,22 @@ agg_mode_batcher_poller_start_local: agg_mode_run_migrations
326329
agg_mode_batcher_poller_start_ethereum_package: agg_mode_run_migrations
327330
cargo run --manifest-path ./aggregation_mode/Cargo.toml --release --bin agg_mode_batcher_poller -- config-files/config-agg-mode-batcher-ethereum-package.yaml
328331

332+
AGG_MODE_SENDER ?= 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
333+
agg_mode_batcher_send_payment:
334+
@cast send --value 1ether \
335+
0x922D6956C99E12DFeB3224DEA977D0939758A1Fe \
336+
--private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
337+
338+
agg_mode_batcher_send_sp1_proof:
339+
@NONCE=$$(curl -s http://127.0.0.1:8089/nonce/0x70997970C51812dc3A010C7d01b50e0d17dc79C8 | jq -r '.data.nonce'); \
340+
curl -X POST \
341+
-H "Content-Type: multipart/form-data" \
342+
-F "nonce=$${NONCE}" \
343+
-F "proof=@scripts/test_files/sp1/sp1_fibonacci_5_0_0.proof" \
344+
-F "program_vk=@scripts/test_files/sp1/sp1_fibonacci_5_0_0_vk.bin" \
345+
-F "signature_hex=0x0" \
346+
http://127.0.0.1:8089/proof/sp1
347+
329348
__AGGREGATOR__: ## ____
330349

331350
aggregator_start: ## Start the Aggregator. Parameters: ENVIRONMENT=<devnet|testnet|mainnet>, AGG_CONFIG_FILE

aggregation_mode/Cargo.lock

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

aggregation_mode/batcher/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ serde = { workspace = true }
88
serde_json = { workspace = true }
99
serde_yaml = { workspace = true }
1010
aligned-sdk = { workspace = true }
11+
sp1-sdk = { workspace = true }
1112
tracing = { version = "0.1", features = ["log"] }
1213
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
14+
bincode = "1.3.3"
1315
actix-web = "4"
16+
actix-multipart = "0.7.2"
1417
alloy = { workspace = true }
1518
tokio = { version = "1", features = ["time"]}
1619
# TODO: enable tls

aggregation_mode/batcher/src/http.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ use std::{
33
time::{SystemTime, UNIX_EPOCH},
44
};
55

6+
use actix_multipart::form::MultipartForm;
67
use actix_web::{
78
web::{self, Data},
89
App, HttpRequest, HttpResponse, HttpServer, Responder,
910
};
1011
use aligned_sdk::aggregation_layer::AggregationModeProvingSystem;
12+
use sp1_sdk::{SP1ProofWithPublicValues, SP1VerifyingKey};
1113
use sqlx::types::BigDecimal;
1214

1315
use super::{
@@ -18,7 +20,8 @@ use super::{
1820
use crate::{
1921
config::Config,
2022
db::Db,
21-
types::{SubmitProofRequest, SubmitProofRequestMessageRisc0, SubmitProofRequestMessageSP1},
23+
types::{SubmitProofRequestRisc0, SubmitProofRequestSP1},
24+
verifiers::{verify_sp1_proof, VerificationError},
2225
};
2326

2427
#[derive(Clone, Debug)]
@@ -60,7 +63,6 @@ impl BatcherServer {
6063
};
6164

6265
// TODO: validate valid ethereum address
63-
6466
let Some(state) = req.app_data::<Data<BatcherServer>>() else {
6567
return HttpResponse::InternalServerError()
6668
.json(AppResponse::new_unsucessfull("Internal server error", 500));
@@ -80,11 +82,8 @@ impl BatcherServer {
8082

8183
async fn post_proof_sp1(
8284
req: HttpRequest,
83-
body: web::Json<SubmitProofRequest<SubmitProofRequestMessageSP1>>,
85+
MultipartForm(data): MultipartForm<SubmitProofRequestSP1>,
8486
) -> impl Responder {
85-
let data = body.into_inner();
86-
87-
// TODO: validate signature
8887
let recovered_address = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_lowercase();
8988

9089
let Some(state) = req.app_data::<Data<BatcherServer>>() else {
@@ -98,7 +97,7 @@ impl BatcherServer {
9897
.json(AppResponse::new_unsucessfull("Internal server error", 500));
9998
};
10099

101-
if data.nonce != (count as u64) {
100+
if data.nonce.0 != (count as u64) {
102101
return HttpResponse::BadRequest().json(AppResponse::new_unsucessfull(
103102
&format!("Invalid nonce, expected nonce = {count}"),
104103
400,
@@ -136,15 +135,42 @@ impl BatcherServer {
136135
));
137136
}
138137

139-
// TODO: decode proof and validate it
138+
let Ok(proof_content) = tokio::fs::read(data.proof.file.path()).await else {
139+
return HttpResponse::InternalServerError()
140+
.json(AppResponse::new_unsucessfull("Internal server error", 500));
141+
};
142+
143+
let Ok(proof) = bincode::deserialize::<SP1ProofWithPublicValues>(&proof_content) else {
144+
return HttpResponse::BadRequest()
145+
.json(AppResponse::new_unsucessfull("Invalid SP1 proof", 400));
146+
};
147+
148+
let Ok(vk_content) = tokio::fs::read(data.program_vk.file.path()).await else {
149+
return HttpResponse::InternalServerError()
150+
.json(AppResponse::new_unsucessfull("Internal server error", 500));
151+
};
152+
153+
let Ok(vk) = bincode::deserialize::<SP1VerifyingKey>(&vk_content) else {
154+
return HttpResponse::BadRequest()
155+
.json(AppResponse::new_unsucessfull("Invalid vk", 400));
156+
};
157+
158+
if let Err(e) = verify_sp1_proof(&proof, &vk) {
159+
let message = match e {
160+
VerificationError::InvalidProof => "Proof verification failed",
161+
VerificationError::UnsupportedProof => "Unsupported proof",
162+
};
163+
164+
return HttpResponse::BadRequest().json(AppResponse::new_unsucessfull(message, 400));
165+
};
140166

141167
match state
142168
.db
143169
.insert_task(
144170
&recovered_address,
145171
AggregationModeProvingSystem::SP1.as_u16() as i32,
146-
&data.message.proof,
147-
&data.message.program_vk_commitment,
172+
&proof_content,
173+
&vk_content,
148174
None,
149175
)
150176
.await
@@ -160,7 +186,7 @@ impl BatcherServer {
160186
/// TODO: complete for risc0 (see `post_proof_sp1`)
161187
async fn post_proof_risc0(
162188
_req: HttpRequest,
163-
_body: web::Json<SubmitProofRequest<SubmitProofRequestMessageRisc0>>,
189+
MultipartForm(_): MultipartForm<SubmitProofRequestRisc0>,
164190
) -> impl Responder {
165191
HttpResponse::Ok().json(AppResponse::new_sucessfull(serde_json::json!({})))
166192
}

aggregation_mode/batcher/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ pub mod config;
22
pub mod db;
33
mod helpers;
44
pub mod http;
5+
pub mod server;
56
mod types;
7+
mod verifiers;

aggregation_mode/batcher/src/types.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
12
use serde::{Deserialize, Serialize};
23
use serde_json::Value;
34

@@ -32,21 +33,18 @@ pub(super) struct GetProofMerklePathQueryParams {
3233
pub id: Option<String>,
3334
}
3435

35-
#[derive(Serialize, Deserialize, Clone, Debug)]
36-
pub(super) struct SubmitProofRequest<T> {
37-
pub nonce: u64,
38-
pub message: T,
39-
pub signature: String,
40-
}
41-
#[derive(Serialize, Deserialize, Clone, Debug)]
42-
pub(super) struct SubmitProofRequestMessageSP1 {
43-
pub proof: Vec<u8>,
44-
pub program_vk_commitment: Vec<u8>,
36+
#[derive(Debug, MultipartForm)]
37+
pub(super) struct SubmitProofRequestSP1 {
38+
pub nonce: Text<u64>,
39+
pub proof: TempFile,
40+
pub program_vk: TempFile,
41+
pub _signature_hex: Text<String>,
4542
}
4643

47-
#[derive(Serialize, Deserialize, Clone, Debug)]
48-
pub(super) struct SubmitProofRequestMessageRisc0 {
49-
pub proof: Vec<u8>,
50-
pub program_image_id: Vec<u8>,
51-
pub public_inputs: Vec<u8>,
44+
#[derive(Debug, MultipartForm)]
45+
pub(super) struct SubmitProofRequestRisc0 {
46+
pub _nonce: Text<u64>,
47+
pub _risc0_receipt: TempFile,
48+
pub _program_image_id_hex: Text<String>,
49+
pub _signature_hex: Text<String>,
5250
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::sync::LazyLock;
2+
3+
use sp1_sdk::{CpuProver, Prover, ProverClient, SP1ProofWithPublicValues, SP1VerifyingKey};
4+
5+
static SP1_PROVER_CLIENT_CPU: LazyLock<CpuProver> =
6+
LazyLock::new(|| ProverClient::builder().cpu().build());
7+
8+
pub enum VerificationError {
9+
InvalidProof,
10+
UnsupportedProof,
11+
}
12+
13+
pub fn verify_sp1_proof(
14+
proof: &SP1ProofWithPublicValues,
15+
vk: &SP1VerifyingKey,
16+
) -> Result<(), VerificationError> {
17+
let client = &*SP1_PROVER_CLIENT_CPU;
18+
19+
match proof.proof {
20+
sp1_sdk::SP1Proof::Compressed(_) => client
21+
.verify(proof, vk)
22+
.map_err(|_| VerificationError::InvalidProof),
23+
_ => Err(VerificationError::UnsupportedProof),
24+
}?;
25+
26+
Ok(())
27+
}

aggregation_mode/proof_aggregator/build.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
11
use risc0_build::{DockerOptionsBuilder, GuestOptionsBuilder};
22
use std::collections::HashMap;
3+
use std::path::PathBuf;
4+
5+
// This allows us to skip the guest build in CI or local environments where it's not needed (reducing the build time)
6+
// Note: To use this flag, the aggregation programs should be already compiled, otherwise the compilation will be done anyway.
7+
fn should_skip_build() -> bool {
8+
if std::env::var("SKIP_AGG_PROGRAMS_BUILD")
9+
.map(|v| v == "1")
10+
.unwrap_or(false)
11+
{
12+
let out_dir = std::env::var("OUT_DIR").unwrap();
13+
let methods_path = PathBuf::from(out_dir).join("methods.rs");
14+
15+
methods_path.exists()
16+
} else {
17+
false
18+
}
19+
}
320

421
// Reference: https://docs.succinct.xyz/docs/sp1/writing-programs/compiling#advanced-build-options-1
522
fn main() {
23+
if should_skip_build() {
24+
println!("cargo:warning=SKIP_AGG_PROGRAMS_BUILD=1: methods.rs already exists, skipping aggregation programs build");
25+
return;
26+
} else {
27+
println!("cargo:warning=SKIP_AGG_PROGRAMS_BUILD=1 set, but path does not exist, running full build");
28+
}
29+
630
sp1_build::build_program_with_args("./aggregation_programs/sp1", {
731
sp1_build::BuildArgs {
832
output_directory: Some("./aggregation_programs/sp1/elf".to_string()),

aggregation_mode/proof_aggregator/src/backend/config.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ pub struct Config {
2323
pub ecdsa: ECDSAConfig,
2424
pub proofs_per_chunk: u16,
2525
pub total_proofs_limit: u16,
26-
pub sp1_chunk_aggregator_vk_hash: String,
2726
pub risc0_chunk_aggregator_image_id: String,
27+
pub sp1_chunk_aggregator_vk_hash: String,
28+
pub monthly_budget_eth: f64,
2829
}
2930

3031
impl Config {

0 commit comments

Comments
 (0)