Skip to content

Commit e16c4b9

Browse files
committed
refactor: simplified agg_layer sdk api
1 parent 5981a77 commit e16c4b9

File tree

3 files changed

+75
-114
lines changed

3 files changed

+75
-114
lines changed
Lines changed: 59 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,120 @@
11
// Modules
2-
32
mod helpers;
43
mod types;
54

65
// Makes only the two types on this use public
76
pub use types::{AggregationModeVerificationData, ProofVerificationAggModeError};
87

9-
use helpers::{fetch_verified_proofs_events, get_blob_data_from_verified_proof_event};
10-
use types::Hash32;
11-
12-
//
138
use crate::{
149
common::types::Network, eth::aligned_proof_agg_service::aligned_proof_aggregation_service,
1510
};
1611
use ethers::{
1712
providers::{Http, Provider},
1813
types::Bytes,
1914
};
15+
use helpers::{fetch_verified_proofs_events, get_blob_data_from_verified_proof_event};
2016
use lambdaworks_crypto::merkle_tree::merkle::MerkleTree;
17+
use types::Hash32;
18+
19+
pub enum ProofStatus {
20+
Verified {
21+
merkle_root: [u8; 32],
22+
merkle_path: Vec<[u8; 32]>,
23+
},
24+
Invalid,
25+
NotFound,
26+
}
2127

2228
/// Given the [`AggregationModeVerificationData`], this function checks whether the proof was included in a
2329
/// in a recent aggregated proof and verifies the corresponding Merkle root commitment.
2430
///
25-
/// Note: This functionality is currently in Beta. As a result, we cannot determine with certainty
26-
/// which specific aggregation a proof belongs to. Instead, we check the events from the specified `from_block`.
27-
///
28-
/// Note: The `from_block` must not be older than 18 days,
31+
/// ### Notes
32+
/// - This functionality is currently in Beta. As a result, we cannot determine with certainty.
33+
/// which specific aggregation a proof belongs to. Instead, we check the events from the specified `from_block`.
34+
/// - The `from_block` must not be older than 18 days,
2935
/// as blobs expire after that period and will no longer be retrievable.
30-
/// If not provided, it defaults to fetch logs from [`FROM_BLOCKS_AGO_DEFAULT`]
36+
/// - If not provided, it defaults to fetch logs from [`FROM_BLOCKS_AGO_DEFAULT`]
3137
///
32-
/// The step-by-step verification process includes:
38+
/// ### The verification process includes:
3339
/// 1. Querying the blob versioned hash from the events emitted by the aligned proof aggregation service contract since `from_block`
3440
/// 2. Retrieving the corresponding beacon block using the block's parent beacon root
3541
/// 3. Fetching the blobs associated with that slot
3642
/// 4. Filtering the blob that matches the queried blob versioned hash
3743
/// 5. Decoding the blob to extract the proofs commitments
3844
/// 6. Checking if the given proof commitment exists within the blob's proofs
3945
/// 7. Reconstructing the Merkle root and verifying it against the root stored in the contract
40-
pub async fn is_proof_verified(
41-
verification_data: AggregationModeVerificationData,
46+
///
47+
/// This function is typically used in conjunction with `verifyProofInclusion` for complete on-chain verification.
48+
pub async fn check_proof_verification(
49+
verification_data: &AggregationModeVerificationData,
4250
network: Network,
4351
eth_rpc_url: String,
4452
beacon_client_url: String,
4553
from_block: Option<u64>,
46-
) -> Result<[u8; 32], ProofVerificationAggModeError> {
54+
) -> Result<ProofStatus, ProofVerificationAggModeError> {
4755
let logs = fetch_verified_proofs_events(network, eth_rpc_url.clone(), from_block).await?;
56+
let proof_commitment = verification_data.commitment();
4857

4958
for log in logs {
50-
let Ok((merkle_root, leaves)) = get_blob_data_from_verified_proof_event(
59+
let (merkle_root, leaves) = get_blob_data_from_verified_proof_event(
5160
eth_rpc_url.clone(),
5261
beacon_client_url.clone(),
5362
log,
5463
)
55-
.await
56-
else {
57-
continue;
58-
};
64+
.await?;
5965

6066
let leaves: Vec<Hash32> = leaves.iter().map(|leaf| Hash32(*leaf)).collect();
6167
let Some(merkle_tree) = MerkleTree::<Hash32>::build(&leaves) else {
6268
continue;
6369
};
6470

65-
if leaves.contains(&Hash32(verification_data.commitment())) {
66-
return if merkle_tree.root == merkle_root {
67-
Ok(merkle_root)
68-
} else {
69-
Err(ProofVerificationAggModeError::MerkleTreeProofVerification)
70-
};
71+
let Some(pos) = leaves.iter().position(|p| p.0 == proof_commitment) else {
72+
continue;
73+
};
74+
let Some(proof) = merkle_tree.get_proof_by_pos(pos) else {
75+
continue;
76+
};
77+
78+
let result = proof.verify::<Hash32>(&merkle_root, pos, &Hash32(proof_commitment));
79+
if !result {
80+
return Ok(ProofStatus::Invalid);
7181
}
82+
83+
return Ok(ProofStatus::Verified {
84+
merkle_path: proof.merkle_path,
85+
merkle_root: merkle_root,
86+
});
7287
}
7388

74-
Err(ProofVerificationAggModeError::ProofNotFoundInLogs)
89+
Ok(ProofStatus::NotFound)
7590
}
7691

77-
/// Performs the same verification as [`is_proof_verified`], but instead of verifying locally, it simulates
78-
/// an on-chain verification by calling the `verifyProofInclusion` function on the `ProofAggregationService` contract.
92+
/// Simulates an on-chain verification of the proof by calling the `verifyProofInclusion` function
93+
/// on the `ProofAggregationService` contract.
94+
///
95+
/// This function is intended to complement [`check_proof_verification`], which performs off-chain verification.
96+
/// After calling `check_proof_verification` to confirm the proof's inclusion and obtain the Merkle path,
97+
/// this function can be used to simulate the corresponding contract call.
7998
///
80-
/// This function:
81-
/// 1. Fetches the aggregated proof blob from the blockchain.
82-
/// 2. Constructs the corresponding Merkle tree from the proof commitments.
83-
/// 3. Calls the contract's `verifyProofInclusion` function with:
84-
/// - The Merkle path corresponding to the given [`AggregationModeVerificationData`].
85-
/// - The proof commitment, computed from the program ID and public inputs.
99+
/// ### How it works:
100+
/// 1. Uses the provided Merkle path (as returned by [`check_proof_verification`]).
101+
/// 2. Calls the `verifyProofInclusion` function on the contract with:
102+
/// - The Merkle path,
103+
/// - The proof program id.
104+
/// - The proof public inputs bytes
86105
///
87-
/// This is mainly useful for testing and simulation purposes to ensure that a given proof commitment
88-
/// would be accepted by the contract on-chain. For typical off-chain verification (e.g., in services or indexers),
89-
/// prefer using [`is_proof_verified`].
106+
/// ### Purpose:
107+
/// This is mainly useful for **testing or simulation**, to confirm that the on-chain contract would
108+
/// accept a given proof commitment and Merkle path. It does **not** perform an actual transaction on-chain,
109+
/// but instead simulates the call via `eth_call`.
90110
///
91-
/// Note: This function does not perform the actual on-chain transaction but simulates the contract call.
111+
/// For off-chain verification use cases, prefer using [`check_proof_verification`].
92112
pub async fn is_proof_verified_on_chain(
93113
verification_data: AggregationModeVerificationData,
114+
merkle_path: Vec<[u8; 32]>,
94115
network: Network,
95116
eth_rpc_url: String,
96-
beacon_client_url: String,
97-
from_block: Option<u64>,
98117
) -> Result<bool, ProofVerificationAggModeError> {
99-
let Some(merkle_path) = get_merkle_path_for_proof(
100-
network.clone(),
101-
eth_rpc_url.clone(),
102-
beacon_client_url,
103-
from_block,
104-
&verification_data,
105-
)
106-
.await?
107-
else {
108-
return Ok(false);
109-
};
110-
111118
let eth_rpc_provider = Provider::<Http>::try_from(eth_rpc_url)
112119
.map_err(|e| ProofVerificationAggModeError::EthereumProviderError(e.to_string()))?;
113120
let contract_provider = aligned_proof_aggregation_service(
@@ -129,54 +136,3 @@ pub async fn is_proof_verified_on_chain(
129136

130137
Ok(res)
131138
}
132-
133-
/// Given the [`AggregationModeVerificationData`], this function queries the blockchain logs starting from the
134-
/// specified `from_block` until it founds the proof.
135-
///
136-
/// Once the proof is found:
137-
/// 1. It retrieves the corresponding proof blob.
138-
/// 2. Constructs the Merkle tree based on the proof blob.
139-
/// 3. Returns the Merkle proof needed for verifying the proof.
140-
///
141-
/// Note: This function prepares the Merkle path for on-chain verification, and is typically used in combination with
142-
/// `verifyProofInclusion` to confirm proof validity within the ProofAggregationService contract.
143-
pub async fn get_merkle_path_for_proof(
144-
network: Network,
145-
eth_rpc_url: String,
146-
beacon_client_url: String,
147-
from_block: Option<u64>,
148-
verification_data: &AggregationModeVerificationData,
149-
) -> Result<Option<Vec<[u8; 32]>>, ProofVerificationAggModeError> {
150-
let logs = fetch_verified_proofs_events(network, eth_rpc_url.clone(), from_block).await?;
151-
let proof_commitment = verification_data.commitment();
152-
153-
for log in logs {
154-
let (merkle_root, leaves) = get_blob_data_from_verified_proof_event(
155-
eth_rpc_url.clone(),
156-
beacon_client_url.clone(),
157-
log,
158-
)
159-
.await?;
160-
161-
let leaves: Vec<Hash32> = leaves.iter().map(|leaf| Hash32(*leaf)).collect();
162-
let Some(merkle_tree) = MerkleTree::<Hash32>::build(&leaves) else {
163-
continue;
164-
};
165-
166-
let Some(pos) = leaves.iter().position(|p| p.0 == proof_commitment) else {
167-
continue;
168-
};
169-
let Some(proof) = merkle_tree.get_proof_by_pos(pos) else {
170-
continue;
171-
};
172-
173-
let result = proof.verify::<Hash32>(&merkle_root, pos, &Hash32(proof_commitment));
174-
if !result {
175-
return Err(ProofVerificationAggModeError::MerkleTreeProofVerification);
176-
}
177-
178-
return Ok(Some(proof.merkle_path));
179-
}
180-
181-
Ok(None)
182-
}

batcher/aligned-sdk/src/aggregation_layer/types.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,11 @@ impl IsMerkleTreeBackend for Hash32 {
9898
}
9999
}
100100

101-
#[derive(Debug)]
101+
#[derive(Debug, Clone)]
102102
pub enum ProofVerificationAggModeError {
103103
ProvingSystemNotSupportedInAggMode,
104104
EthereumProviderError(String),
105105
BeaconClient(BeaconClientError),
106-
ProofNotFoundInLogs,
107106
EventDecoding,
108107
MerkleTreeConstruction,
109-
MerkleTreeProofVerification,
110108
}

batcher/aligned-sdk/src/beacon.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ enum BeaconAPIResponse {
2020
Error { code: u64, message: String },
2121
}
2222

23-
#[derive(Debug)]
23+
#[derive(Debug, Clone)]
2424
pub enum BeaconClientError {
2525
Url(url::ParseError),
26-
ReqwestError(reqwest::Error),
26+
ReqwestError(String),
2727
APIError { code: u64, message: String },
28-
Deserialization(serde_json::Error),
28+
Deserialization(String),
2929
}
3030

3131
#[derive(Deserialize, Debug)]
@@ -84,8 +84,8 @@ impl BeaconClient {
8484
))
8585
.await?;
8686

87-
let res =
88-
Vec::<BeaconBlock>::deserialize(data).map_err(BeaconClientError::Deserialization)?;
87+
let res = Vec::<BeaconBlock>::deserialize(data)
88+
.map_err(|e| BeaconClientError::Deserialization(e.to_string()))?;
8989

9090
let block = res
9191
.into_iter()
@@ -99,7 +99,8 @@ impl BeaconClient {
9999
.beacon_get(&format!("/eth/v1/beacon/blob_sidecars/{}", slot))
100100
.await?;
101101

102-
Vec::<BlobData>::deserialize(data).map_err(BeaconClientError::Deserialization)
102+
Vec::<BlobData>::deserialize(data)
103+
.map_err(|e| BeaconClientError::Deserialization(e.to_string()))
103104
}
104105

105106
pub async fn get_blob_by_versioned_hash(
@@ -133,8 +134,14 @@ impl BeaconClient {
133134
.header("content-type", "application/json")
134135
.header("accept", "application/json");
135136

136-
let res = req.send().await.map_err(BeaconClientError::ReqwestError)?;
137-
let beacon_response = res.json().await.map_err(BeaconClientError::ReqwestError)?;
137+
let res = req
138+
.send()
139+
.await
140+
.map_err(|e| BeaconClientError::ReqwestError(e.to_string()))?;
141+
let beacon_response = res
142+
.json()
143+
.await
144+
.map_err(|e| BeaconClientError::ReqwestError(e.to_string()))?;
138145

139146
match beacon_response {
140147
BeaconAPIResponse::Success { data } => Ok(data),

0 commit comments

Comments
 (0)