Skip to content

Commit 1738234

Browse files
authored
Add DA-related constraints to the aggregated proof circuit. (#2593)
1 parent 1cb7b41 commit 1738234

File tree

5 files changed

+86
-24
lines changed

5 files changed

+86
-24
lines changed

crates/full-node/sov-aggregated-proof/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/full-node/sov-aggregated-proof/program/src/main.rs

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,88 @@ sp1_zkvm::entrypoint!(main);
44

55
use demo_stf::MultiAddressEvmSolana;
66
use sha2::{Digest, Sha256};
7-
use sov_aggregated_proof_shared::DeferredProofInput;
7+
use sov_aggregated_proof_shared::{AggregatedProofWitness, DeferredProofInput};
88
use sov_mock_da::MockDaSpec;
99
use sov_mock_zkvm::MockZkvm;
1010
use sov_modules_api::configurable_spec::ConfigurableSpec;
1111
use sov_modules_api::da::BlockHeaderTrait;
1212
use sov_modules_api::execution_mode::Zk;
13+
use sov_modules_api::DaSpec;
1314
use sov_modules_api::Spec;
1415
use sov_modules_api::StateTransitionPublicData;
1516
use sov_modules_api::Storage;
1617
use sov_sp1_adapter::SP1;
1718

1819
type S = ConfigurableSpec<MockDaSpec, SP1, MockZkvm, MultiAddressEvmSolana, Zk>;
1920

21+
type StPubData<S: Spec, Da: DaSpec> =
22+
StateTransitionPublicData<<S as Spec>::Address, Da, <<S as Spec>::Storage as Storage>::Root>;
23+
2024
pub fn main() {
21-
let proof_inputs = sp1_zkvm::io::read::<Vec<DeferredProofInput>>();
25+
let witness = sp1_zkvm::io::read::<AggregatedProofWitness<MockDaSpec>>();
26+
let proof_inputs = witness.proof_inputs;
27+
let vkey_hash = witness.vkey_hash;
28+
29+
verify::<S, MockDaSpec>(proof_inputs, vkey_hash);
30+
}
31+
32+
fn verify<S: Spec, Da: DaSpec>(proof_inputs: Vec<DeferredProofInput<Da>>, vkey_hash: [u32; 8]) {
33+
assert!(
34+
!proof_inputs.is_empty(),
35+
"Aggregated proof must contain at least one proof input"
36+
);
37+
38+
// `None` means no predecessor to check against (first iteration).
39+
let mut expected_prev_hash = None;
40+
let mut expected_state_root = None;
2241

2342
for (index, proof_input) in proof_inputs.iter().enumerate() {
24-
let stf_public_data: StateTransitionPublicData<
25-
<S as Spec>::Address,
26-
MockDaSpec,
27-
<<S as Spec>::Storage as Storage>::Root,
28-
> = bincode::deserialize(proof_input.public_values.as_slice()).unwrap();
29-
30-
let da_block_header = &proof_input.da_block_header;
31-
assert_eq!(da_block_header.hash(), stf_public_data.slot_hash);
32-
33-
println!("[guest] verifying proof {index}");
34-
let public_values_digest: [u8; 32] = Sha256::digest(&proof_input.public_values).into();
35-
sp1_zkvm::lib::verify::verify_sp1_proof(&proof_input.vkey_hash, &public_values_digest);
36-
println!("[guest] verified proof {index}");
43+
let stf_public_data =
44+
deserialize_pub_data::<S, Da>(proof_input.public_values.as_slice(), index);
45+
46+
// Check that DA blocks form a chain.
47+
{
48+
let da_block_header = &proof_input.da_block_header;
49+
let current_block_hash = da_block_header.hash();
50+
51+
if let Some(expected_prev_hash) = &expected_prev_hash {
52+
assert_eq!(
53+
expected_prev_hash,
54+
&da_block_header.prev_hash(),
55+
"DA block chain broken at index {index}: prev_hash mismatch"
56+
);
57+
}
58+
59+
// Check that the slot hash from the public input matches the current block hash.
60+
assert_eq!(
61+
current_block_hash, stf_public_data.slot_hash,
62+
"Slot hash mismatch at index {index}: DA block header hash doesn't match public data"
63+
);
64+
expected_prev_hash = Some(current_block_hash);
65+
}
66+
67+
// Check that state roots are sequentially related by the state transition.
68+
{
69+
if let Some(expected_state_root) = &expected_state_root {
70+
assert_eq!(
71+
expected_state_root, &stf_public_data.initial_state_root,
72+
"State root discontinuity at index {index}: previous final_state_root != current initial_state_root"
73+
);
74+
}
75+
76+
verify_sp1_proof(proof_input, vkey_hash);
77+
expected_state_root = Some(stf_public_data.final_state_root.clone());
78+
}
3779
}
3880
}
81+
82+
fn verify_sp1_proof<Da: DaSpec>(proof_input: &DeferredProofInput<Da>, vkey_hash: [u32; 8]) {
83+
let public_values_digest: [u8; 32] = Sha256::digest(&proof_input.public_values).into();
84+
sp1_zkvm::lib::verify::verify_sp1_proof(&vkey_hash, &public_values_digest);
85+
}
86+
87+
fn deserialize_pub_data<S: Spec, Da: DaSpec>(data: &[u8], index: usize) -> StPubData<S, Da> {
88+
bincode::deserialize(data).unwrap_or_else(|error| {
89+
panic!("Failed to deserialize public values from proof input {index}: {error}")
90+
})
91+
}

crates/full-node/sov-aggregated-proof/script/src/main.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::time::Instant;
55

66
use anyhow::{bail, ensure, Context};
77
use slop_algebra::PrimeField32;
8+
use sov_aggregated_proof_shared::AggregatedProofWitness;
89
use sov_aggregated_proof_shared::DeferredProofInput;
910
use sov_mock_da::MockDaSpec;
1011
use sov_sp1_adapter::BlockHeaderWithProof;
@@ -59,17 +60,21 @@ fn main() -> anyhow::Result<()> {
5960
index
6061
);
6162

62-
let deferred_proof_input = DeferredProofInput {
63+
let deferred_proof_input = DeferredProofInput::<MockDaSpec> {
6364
public_values: proof.public_values.to_vec(),
64-
vkey_hash: inner_vk_hash,
6565
da_block_header: block_header_with_proof.da_block_header,
6666
};
6767

6868
proof_inputs.push(deferred_proof_input);
6969
stdin.write_proof(*recursion_proof.clone(), verification_key.vk.clone());
7070
}
7171

72-
stdin.write(&proof_inputs);
72+
let witness = AggregatedProofWitness {
73+
proof_inputs,
74+
vkey_hash: inner_vk_hash,
75+
};
76+
77+
stdin.write(&witness);
7378

7479
println!("[host] starting outer compressed proof");
7580
let outer_proof = prover

crates/full-node/sov-aggregated-proof/shared/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ publish = false
66

77
[dependencies]
88
serde = { version = "1", default-features = false, features = ["alloc", "derive"] }
9-
10-
sov-mock-da = { workspace = true }
9+
sov-modules-api = { workspace = true }
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
use serde::{Deserialize, Serialize};
2-
use sov_mock_da::MockBlockHeader;
2+
use sov_modules_api::DaSpec;
33

44
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
5-
pub struct DeferredProofInput {
5+
pub struct DeferredProofInput<Da: DaSpec> {
66
pub public_values: Vec<u8>,
7+
pub da_block_header: Da::BlockHeader,
8+
}
9+
10+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11+
pub struct AggregatedProofWitness<Da: DaSpec> {
12+
pub proof_inputs: Vec<DeferredProofInput<Da>>,
713
pub vkey_hash: [u32; 8],
8-
pub da_block_header: MockBlockHeader,
914
}

0 commit comments

Comments
 (0)