Skip to content

Commit e84a1fd

Browse files
atanmarkoEkleog-Polygoninvocamanman
authored
feat: remove gers and unclaims (#271)
Updated the aggkit-prover API to include list of removed global exit roots and bridge exit unclaims in the `GenerateAggchainProofRequest`. Built on top of #260 Fixes #254 --------- Co-authored-by: Leo Gaspard <lgaspard@polygon.technology> Co-authored-by: invocamanman <32056120+invocamanman@users.noreply.github.com>
1 parent fab1add commit e84a1fd

File tree

30 files changed

+2618
-1609
lines changed

30 files changed

+2618
-1609
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ alloy-primitives = { version = "1.2", features = ["serde", "k256"] }
8080
anyhow = "1.0"
8181
arc-swap = "1.7"
8282
async-trait = "0.1.82"
83-
base64 = "0.22.0"
83+
base64 = "0.22"
8484
buildstructor = "0.5.4"
8585
bytes = "1.10"
8686
clap = { version = "4.5", features = ["derive", "env"] }
@@ -104,6 +104,7 @@ insta = { git = "https://github.com/freyskeyd/insta", branch = "chore/updating-d
104104
"toml",
105105
"yaml",
106106
] }
107+
itertools = "0.14.0"
107108
jsonrpsee = { version = "0.24.7", features = ["full"] }
108109
k256 = "0.13.4"
109110
lazy_static = "1.5"
-196 Bytes
Binary file not shown.

crates/aggchain-proof-builder/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ pub enum Error {
6767
#[error("Unable to fetch trusted sequencer address")]
6868
UnableToFetchTrustedSequencerAddress(#[source] aggchain_proof_contracts::Error),
6969

70+
#[error("Filtering values overflow {0}")]
71+
FilteringValuesOverflow(usize),
72+
7073
#[error(transparent)]
7174
Other(eyre::Report),
7275
}

crates/aggchain-proof-builder/src/lib.rs

Lines changed: 208 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod error;
55
mod tests;
66

77
use std::{
8+
hash::Hash,
89
panic::AssertUnwindSafe,
910
sync::Arc,
1011
task::{Context, Poll},
@@ -29,7 +30,7 @@ use aggkit_prover_types::vkey_hash::{Sp1VKeyHash, VKeyHash};
2930
use agglayer_interop::types::{
3031
bincode, GlobalIndexWithLeafHash, ImportedBridgeExitCommitmentValues,
3132
};
32-
use agglayer_primitives::{Address, Digest};
33+
use agglayer_primitives::{Address, Digest, U256};
3334
use alloy::eips::BlockNumberOrTag;
3435
pub use error::Error;
3536
use eyre::Context as _;
@@ -121,6 +122,162 @@ pub struct AggchainProofBuilderResponse {
121122
pub public_values: AggchainProofPublicValues,
122123
}
123124

125+
/// Filters out values from a list based on a set of keys to remove, using a key
126+
/// extraction function.
127+
///
128+
/// This function iterates over `values`, removing up to N occurrences of each
129+
/// value whose key, as determined by `key_fn`, matches a key in
130+
/// `keys_to_remove`, where N is the number of times the key appears in
131+
/// `keys_to_remove`. The removal is performed in order, and only the first N
132+
/// matching values are removed for each key. Remaining values are preserved in
133+
/// their original order.
134+
///
135+
/// # Arguments
136+
///
137+
/// * `keys_to_remove` - A slice of keys indicating which values to remove. Each
138+
/// occurrence of a key in this slice will remove one matching value from
139+
/// `values`.
140+
/// * `values` - The slice of values to filter.
141+
/// * `key_fn` - A function that extracts a key from a value for comparison.
142+
///
143+
/// # Returns
144+
///
145+
/// Returns a `Result` containing a `Vec<V>` of the filtered values, or an error
146+
/// if an overflow occurs while counting removals.
147+
///
148+
/// # Example
149+
///
150+
/// ```
151+
/// use aggchain_proof_builder::filter_values;
152+
///
153+
/// let keys_to_remove = [1, 2, 2];
154+
/// let values = [1, 2, 2, 3, 4];
155+
/// let filtered = filter_values(&keys_to_remove, &values, |v| *v).unwrap();
156+
/// assert_eq!(filtered, vec![3, 4]);
157+
/// ```
158+
///
159+
/// # Errors
160+
///
161+
/// Returns `Error::FilteringValuesOverflow` if the removal count for any key
162+
/// would overflow `usize`.
163+
pub fn filter_values<K, V, KF>(
164+
keys_to_remove: &[K],
165+
values: &[V],
166+
mut key_fn: KF,
167+
) -> Result<Vec<V>, Error>
168+
where
169+
K: Eq + Hash + Copy,
170+
V: Clone,
171+
KF: FnMut(&V) -> K,
172+
{
173+
use std::collections::HashMap;
174+
175+
// Count how many times each key should be removed
176+
let mut removal_map: HashMap<K, usize> = HashMap::new();
177+
for &key in keys_to_remove {
178+
let count = removal_map.entry(key).or_insert(0);
179+
*count = count
180+
.checked_add(1)
181+
.ok_or(Error::FilteringValuesOverflow(*count))?;
182+
}
183+
184+
// For each value, if its key is in removal_map and count > 0, skip it and
185+
// decrement count
186+
let mut result = Vec::new();
187+
for value in values {
188+
let key = key_fn(value);
189+
if let Some(count) = removal_map.get_mut(&key) {
190+
if *count > 0 {
191+
*count -= 1;
192+
continue;
193+
}
194+
}
195+
result.push(value.clone());
196+
}
197+
198+
Ok(result)
199+
}
200+
201+
/// Filters, sorts, and maps items from an iterator based on a block number
202+
/// range.
203+
///
204+
/// This function takes an iterator of items, filters them to include only those
205+
/// whose block number (as determined by `block_number_fn`) falls within the
206+
/// specified `range`, sorts the filtered items using their `Ord`
207+
/// implementation, and then maps each item to a new type using the provided
208+
/// `map_fn`.
209+
///
210+
/// # Type Parameters
211+
/// - `T`: The type of the input items. Must implement `Ord`.
212+
/// - `F`: The mapping function type. Must be a function or closure that takes
213+
/// `T` and returns `U`.
214+
/// - `U`: The type of the output items.
215+
///
216+
/// # Arguments
217+
/// - `items`: An iterator of items to process.
218+
/// - `range`: The inclusive range of block numbers to filter by.
219+
/// - `block_number_fn`: A function that extracts the block number from an item.
220+
/// - `map_fn`: A function that maps each filtered and sorted item to the
221+
/// desired output type.
222+
///
223+
/// # Returns
224+
/// An iterator over the mapped items, filtered and sorted as described.
225+
///
226+
/// # Example
227+
/// ```no_run
228+
/// # use aggchain_proof_builder::filter_sort_map;
229+
/// # struct Item { block_number: u64 }
230+
/// # impl Item {
231+
/// # fn to_output_type(self) -> u64 { self.block_number }
232+
/// # }
233+
/// # impl Ord for Item {
234+
/// # fn cmp(&self, other: &Self) -> std::cmp::Ordering {
235+
/// # self.block_number.cmp(&other.block_number)
236+
/// # }
237+
/// # }
238+
/// # impl PartialOrd for Item {
239+
/// # fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
240+
/// # Some(self.cmp(other))
241+
/// # }
242+
/// # }
243+
/// # impl Eq for Item {}
244+
/// # impl PartialEq for Item {
245+
/// # fn eq(&self, other: &Self) -> bool {
246+
/// # self.block_number == other.block_number
247+
/// # }
248+
/// # }
249+
/// let item1 = Item { block_number: 100 };
250+
/// let item2 = Item { block_number: 150 };
251+
/// let item3 = Item { block_number: 250 };
252+
/// let items = vec![item1, item2, item3];
253+
/// let range = 100..=200;
254+
/// let result: Vec<_> = filter_sort_map(
255+
/// items,
256+
/// &range,
257+
/// |item| item.block_number,
258+
/// |item| item.to_output_type(),
259+
/// )
260+
/// .collect();
261+
/// assert_eq!(result, vec![100, 150]);
262+
/// ```
263+
pub fn filter_sort_map<T, F, U>(
264+
items: impl IntoIterator<Item = T>,
265+
range: &std::ops::RangeInclusive<u64>,
266+
block_number_fn: fn(&T) -> u64,
267+
map_fn: F,
268+
) -> impl Iterator<Item = U>
269+
where
270+
F: Fn(T) -> U,
271+
T: Ord,
272+
{
273+
let mut filtered_items: Vec<_> = items
274+
.into_iter()
275+
.filter(|item| range.contains(&block_number_fn(item)))
276+
.collect();
277+
filtered_items.sort();
278+
filtered_items.into_iter().map(map_fn)
279+
}
280+
124281
/// This service is responsible for building an Aggchain proof.
125282
#[derive(Clone)]
126283
#[allow(unused)]
@@ -292,29 +449,58 @@ impl<ContractsClient> AggchainProofBuilder<ContractsClient> {
292449
.await
293450
.map_err(Error::UnableToFetchTrustedSequencerAddress)?;
294451

295-
// From the request
296-
let inserted_gers: Vec<InsertedGER> = request
452+
// Retrieve all the raw GERs from the aggsender input.
453+
// Removed GERs from this list have invalid merkle proofs.
454+
let raw_inserted_gers: Vec<InsertedGER> = request
297455
.aggchain_proof_inputs
298456
.sorted_inserted_gers(&new_blocks_range);
299457

300-
// NOTE: Corresponds to all of them because we do not have removed GERs yet.
301-
let inserted_gers_hash_chain = inserted_gers
302-
.iter()
303-
.map(|inserted_ger| inserted_ger.ger())
304-
.collect();
305-
306-
// NOTE: Corresponds to all of them because we do not have unset claims yet.
307-
let bridge_exits_claimed: Vec<GlobalIndexWithLeafHash> = request
308-
.aggchain_proof_inputs
309-
.imported_bridge_exits
310-
.iter()
311-
.filter(|ib| new_blocks_range.contains(&ib.block_number))
312-
.map(|ib| GlobalIndexWithLeafHash {
458+
// All the bridge exits in the new blocks range, also those that are unclaimed.
459+
let all_imported_bridge_exits: Vec<GlobalIndexWithLeafHash> = filter_sort_map(
460+
request.aggchain_proof_inputs.imported_bridge_exits,
461+
&new_blocks_range,
462+
|ib| ib.block_number,
463+
|ib| GlobalIndexWithLeafHash {
313464
global_index: ib.global_index.into(),
314465
bridge_exit_hash: ib.bridge_exit_hash.0,
315-
})
466+
},
467+
)
468+
.collect();
469+
470+
// Prepare removed GERS for the proof.
471+
let removed_gers: Vec<Digest> = filter_sort_map(
472+
request.aggchain_proof_inputs.removed_gers,
473+
&new_blocks_range,
474+
|removed_ger| removed_ger.block_number,
475+
|removed_ger| removed_ger.global_exit_root,
476+
)
477+
.collect();
478+
479+
// Prepare inserted GERS for the proof, filtering out the removed ones.
480+
let inserted_gers = filter_values(&removed_gers, &raw_inserted_gers, |value| value.ger())?;
481+
482+
// Prepare the hash chain of all the GERs (inserted and removed) for the
483+
// proof.
484+
let raw_inserted_gers = raw_inserted_gers
485+
.into_iter()
486+
.map(|inserted_ger| inserted_ger.ger())
316487
.collect();
317488

489+
// Prepare unset claims input for the proof.
490+
let unset_claims: Vec<U256> = filter_sort_map(
491+
request.aggchain_proof_inputs.unclaims,
492+
&new_blocks_range,
493+
|unclaim| unclaim.block_number,
494+
|unclaim| unclaim.global_index,
495+
)
496+
.collect();
497+
498+
// Filter out the unset claims from the all imported bridge exits list.
499+
let filtered_claimed_imported_bridge_exits =
500+
filter_values(&unset_claims, &all_imported_bridge_exits, |value| {
501+
value.global_index
502+
})?;
503+
318504
let l1_info_tree_leaf = request.aggchain_proof_inputs.l1_info_tree_leaf;
319505
let mut fep_inputs = FepInputs {
320506
l1_head: l1_info_tree_leaf.inner.block_hash,
@@ -373,15 +559,15 @@ impl<ContractsClient> AggchainProofBuilder<ContractsClient> {
373559
origin_network: network_id,
374560
fep: fep_inputs,
375561
commit_imported_bridge_exits: ImportedBridgeExitCommitmentValues {
376-
claims: bridge_exits_claimed.clone(),
562+
claims: filtered_claimed_imported_bridge_exits,
377563
}
378564
.commitment(IMPORTED_BRIDGE_EXIT_COMMITMENT_VERSION),
379565
bridge_witness: BridgeWitness {
380566
inserted_gers,
381-
bridge_exits_claimed,
382-
global_indices_unset: vec![], // NOTE: no unset yet.
383-
raw_inserted_gers: inserted_gers_hash_chain,
384-
removed_gers: vec![], // NOTE: no removed GERs yet.
567+
imported_bridge_exits: all_imported_bridge_exits,
568+
removed_gers,
569+
raw_inserted_gers,
570+
unset_claims,
385571
prev_l2_block_sketch,
386572
new_l2_block_sketch,
387573
caller_address: static_call_caller_address,

crates/aggchain-proof-core/src/bridge/inserted_ger.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct InsertedGER {
1212
/// Block number in which the GER got inserted.
1313
pub block_number: u64,
1414
/// Index within the block.
15-
pub block_index: u64,
15+
pub log_index: u64,
1616
}
1717

1818
impl InsertedGER {

0 commit comments

Comments
 (0)