Skip to content

Commit afe7fde

Browse files
avilagaston9ilitteri
authored andcommitted
feat(l2): verify batches in chunks with aligned (lambdaclass#3242)
**Motivation** Aligned verifies proofs in batches, so it's possible to have an array of proofs ready to be verified at once. **Description** - Modifies `l1_proof_verifier` to check all already aggregated proofs and build a single verify transaction for them. - Updates `verifyBatchAligned()` in the `OnChainProposer` contract to accept an array of proofs. > [!WARNING] > lambdaclass#3276 was accidentally merged into this PR, so the diff includes changes from both. Closes lambdaclass#3168 --------- Co-authored-by: Ivan Litteri <[email protected]>
1 parent cea8238 commit afe7fde

File tree

9 files changed

+321
-185
lines changed

9 files changed

+321
-185
lines changed

cmd/ethrex/l2/options.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use ethrex_rpc::clients::eth::{
1010
BACKOFF_FACTOR, MAX_NUMBER_OF_RETRIES, MAX_RETRY_DELAY, MIN_RETRY_DELAY,
1111
get_address_from_secret_key,
1212
};
13+
use reqwest::Url;
1314
use secp256k1::SecretKey;
1415
use std::net::{IpAddr, Ipv4Addr};
1516

@@ -146,7 +147,7 @@ impl From<SequencerOptions> for SequencerConfig {
146147
aligned: AlignedConfig {
147148
aligned_mode: opts.aligned_opts.aligned,
148149
aligned_verifier_interval_ms: opts.aligned_opts.aligned_verifier_interval_ms,
149-
beacon_url: opts.aligned_opts.beacon_url.unwrap_or_default(),
150+
beacon_urls: opts.aligned_opts.beacon_url.unwrap_or_default(),
150151
network: resolve_aligned_network(
151152
&opts.aligned_opts.aligned_network.unwrap_or_default(),
152153
),
@@ -165,7 +166,7 @@ pub struct EthOptions {
165166
env = "ETHREX_ETH_RPC_URL",
166167
help = "List of rpc urls to use.",
167168
help_heading = "Eth options",
168-
num_args = 1..10
169+
num_args = 1..
169170
)]
170171
pub rpc_url: Vec<String>,
171172
#[arg(
@@ -448,10 +449,11 @@ pub struct AlignedOptions {
448449
value_name = "BEACON_URL",
449450
required_if_eq("aligned", "true"),
450451
env = "ETHREX_ALIGNED_BEACON_URL",
451-
help = "Beacon url to use.",
452-
help_heading = "Aligned options"
452+
help = "List of beacon urls to use.",
453+
help_heading = "Aligned options",
454+
num_args = 1..,
453455
)]
454-
pub beacon_url: Option<String>,
456+
pub beacon_url: Option<Vec<Url>>,
455457
#[arg(
456458
long,
457459
value_name = "ETHREX_ALIGNED_NETWORK",
@@ -488,7 +490,7 @@ impl Default for AlignedOptions {
488490
Self {
489491
aligned: false,
490492
aligned_verifier_interval_ms: 5000,
491-
beacon_url: Some("http://127.0.0.1:58801".to_string()),
493+
beacon_url: Some(vec![Url::parse("http://127.0.0.1:58801").unwrap()]),
492494
aligned_network: Some("devnet".to_string()),
493495
fee_estimate: "instant".to_string(),
494496
aligned_sp1_elf_path: Some(format!(

crates/l2/contracts/src/l1/OnChainProposer.sol

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ contract OnChainProposer is
258258
// TODO: organize each zkvm proof arguments in their own structs
259259
require(
260260
ALIGNEDPROOFAGGREGATOR == DEV_MODE,
261-
"OnChainProposer: ALIGNEDPROOFAGGREGATOR is set. Use verifyBatchAligned instead"
261+
"OnChainProposer: ALIGNEDPROOFAGGREGATOR is set. Use verifyBatchesAligned instead"
262262
);
263263
require(
264264
batchNumber == lastVerifiedBatch + 1,
@@ -313,59 +313,71 @@ contract OnChainProposer is
313313
}
314314

315315
/// @inheritdoc IOnChainProposer
316-
function verifyBatchAligned(
317-
uint256 batchNumber,
318-
bytes calldata alignedPublicInputs,
319-
bytes32[] calldata alignedMerkleProof
316+
function verifyBatchesAligned(
317+
uint256 firstBatchNumber,
318+
bytes[] calldata alignedPublicInputsList,
319+
bytes32[][] calldata alignedMerkleProofsList
320320
) external override onlySequencer whenNotPaused {
321321
require(
322322
ALIGNEDPROOFAGGREGATOR != DEV_MODE,
323323
"OnChainProposer: ALIGNEDPROOFAGGREGATOR is not set"
324324
);
325325
require(
326-
batchNumber == lastVerifiedBatch + 1,
327-
"OnChainProposer: batch already verified"
326+
alignedPublicInputsList.length == alignedMerkleProofsList.length,
327+
"OnChainProposer: input/proof array length mismatch"
328328
);
329329
require(
330-
batchCommitments[batchNumber].newStateRoot != bytes32(0),
331-
"OnChainProposer: cannot verify an uncommitted batch"
330+
firstBatchNumber == lastVerifiedBatch + 1,
331+
"OnChainProposer: incorrect first batch number"
332332
);
333333

334-
// If the verification fails, it will revert.
335-
_verifyPublicData(batchNumber, alignedPublicInputs[8:]);
334+
uint256 batchNumber = firstBatchNumber;
336335

337-
bytes memory callData = abi.encodeWithSignature(
338-
"verifyProofInclusion(bytes32[],bytes32,bytes)",
339-
alignedMerkleProof,
340-
SP1_VERIFICATION_KEY,
341-
alignedPublicInputs
342-
);
343-
(bool callResult, bytes memory response) = ALIGNEDPROOFAGGREGATOR
344-
.staticcall(callData);
345-
require(
346-
callResult,
347-
"OnChainProposer: call to ALIGNEDPROOFAGGREGATOR failed"
348-
);
349-
bool proofVerified = abi.decode(response, (bool));
350-
require(
351-
proofVerified,
352-
"OnChainProposer: Aligned proof verification failed"
353-
);
336+
for (uint256 i = 0; i < alignedPublicInputsList.length; i++) {
337+
require(
338+
batchCommitments[batchNumber].newStateRoot != bytes32(0),
339+
"OnChainProposer: cannot verify an uncommitted batch"
340+
);
354341

355-
lastVerifiedBatch = batchNumber;
342+
// Verify public data for the batch
343+
_verifyPublicData(batchNumber, alignedPublicInputsList[i][8:]);
356344

357-
// The first 2 bytes are the number of deposits.
358-
uint16 deposits_amount = uint16(
359-
bytes2(
360-
batchCommitments[batchNumber].processedDepositLogsRollingHash
361-
)
362-
);
363-
if (deposits_amount > 0) {
364-
ICommonBridge(BRIDGE).removePendingDepositLogs(deposits_amount);
365-
}
345+
bytes memory callData = abi.encodeWithSignature(
346+
"verifyProofInclusion(bytes32[],bytes32,bytes)",
347+
alignedMerkleProofsList[i],
348+
SP1_VERIFICATION_KEY,
349+
alignedPublicInputsList[i]
350+
);
351+
(bool callResult, bytes memory response) = ALIGNEDPROOFAGGREGATOR
352+
.staticcall(callData);
353+
require(
354+
callResult,
355+
"OnChainProposer: call to ALIGNEDPROOFAGGREGATOR failed"
356+
);
366357

367-
// Remove previous batch commitment as it is no longer needed.
368-
delete batchCommitments[batchNumber - 1];
358+
bool proofVerified = abi.decode(response, (bool));
359+
require(
360+
proofVerified,
361+
"OnChainProposer: Aligned proof verification failed"
362+
);
363+
364+
// The first 2 bytes are the number of deposits.
365+
uint16 deposits_amount = uint16(
366+
bytes2(
367+
batchCommitments[batchNumber]
368+
.processedDepositLogsRollingHash
369+
)
370+
);
371+
if (deposits_amount > 0) {
372+
ICommonBridge(BRIDGE).removePendingDepositLogs(deposits_amount);
373+
}
374+
375+
// Remove previous batch commitment
376+
delete batchCommitments[batchNumber - 1];
377+
378+
lastVerifiedBatch = batchNumber;
379+
batchNumber++;
380+
}
369381

370382
emit BatchVerified(lastVerifiedBatch);
371383
}
@@ -418,8 +430,14 @@ contract OnChainProposer is
418430
function revertBatch(
419431
uint256 batchNumber
420432
) external override onlySequencer whenPaused {
421-
require(batchNumber >= lastVerifiedBatch, "OnChainProposer: can't revert verified batch");
422-
require(batchNumber < lastCommittedBatch, "OnChainProposer: no batches are being reverted");
433+
require(
434+
batchNumber >= lastVerifiedBatch,
435+
"OnChainProposer: can't revert verified batch"
436+
);
437+
require(
438+
batchNumber < lastCommittedBatch,
439+
"OnChainProposer: no batches are being reverted"
440+
);
423441

424442
// Remove old batches
425443
for (uint256 i = batchNumber; i < lastCommittedBatch; i++) {

crates/l2/contracts/src/l1/based/OnChainProposer.sol

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ contract OnChainProposer is
313313
// TODO: organize each zkvm proof arguments in their own structs
314314
require(
315315
ALIGNEDPROOFAGGREGATOR == DEV_MODE,
316-
"OnChainProposer: ALIGNEDPROOFAGGREGATOR is set. Use verifyBatchAligned instead"
316+
"OnChainProposer: ALIGNEDPROOFAGGREGATOR is set. Use verifyBatchesAligned instead"
317317
);
318318
require(
319319
batchCommitments[batchNumber].newStateRoot != bytes32(0),
@@ -364,56 +364,71 @@ contract OnChainProposer is
364364
}
365365

366366
/// @inheritdoc IOnChainProposer
367-
function verifyBatchAligned(
368-
uint256 batchNumber,
369-
bytes calldata alignedPublicInputs,
370-
bytes32 alignedProgramVKey,
371-
bytes32[] calldata alignedMerkleProof
372-
) external {
367+
function verifyBatchesAligned(
368+
uint256 firstBatchNumber,
369+
bytes[] calldata alignedPublicInputsList,
370+
bytes32[][] calldata alignedMerkleProofsList
371+
) external override onlySequencer whenNotPaused {
373372
require(
374373
ALIGNEDPROOFAGGREGATOR != DEV_MODE,
375374
"OnChainProposer: ALIGNEDPROOFAGGREGATOR is not set"
376375
);
377376
require(
378-
batchCommitments[batchNumber].newStateRoot != bytes32(0),
379-
"OnChainProposer: cannot verify an uncommitted batch"
380-
);
381-
382-
// If the verification fails, it will revert.
383-
_verifyPublicData(batchNumber, alignedPublicInputs[8:]);
384-
385-
bytes memory callData = abi.encodeWithSignature(
386-
"verifyProofInclusion(bytes32[],bytes32,bytes)",
387-
alignedMerkleProof,
388-
alignedProgramVKey,
389-
alignedPublicInputs
390-
);
391-
(bool callResult, bytes memory response) = ALIGNEDPROOFAGGREGATOR
392-
.staticcall(callData);
393-
require(
394-
callResult,
395-
"OnChainProposer: call to ALIGNEDPROOFAGGREGATOR failed"
377+
alignedPublicInputsList.length == alignedMerkleProofsList.length,
378+
"OnChainProposer: input/proof array length mismatch"
396379
);
397-
bool proofVerified = abi.decode(response, (bool));
398380
require(
399-
proofVerified,
400-
"OnChainProposer: Aligned proof verification failed"
381+
firstBatchNumber == lastVerifiedBatch + 1,
382+
"OnChainProposer: incorrect first batch number"
401383
);
402384

403-
lastVerifiedBatch = batchNumber;
385+
uint256 batchNumber = firstBatchNumber;
404386

405-
// The first 2 bytes are the number of deposits.
406-
uint16 deposits_amount = uint16(
407-
bytes2(
408-
batchCommitments[batchNumber].processedDepositLogsRollingHash
409-
)
410-
);
411-
if (deposits_amount > 0) {
412-
ICommonBridge(BRIDGE).removePendingDepositLogs(deposits_amount);
413-
}
387+
for (uint256 i = 0; i < alignedPublicInputsList.length; i++) {
388+
require(
389+
batchCommitments[batchNumber].newStateRoot != bytes32(0),
390+
"OnChainProposer: cannot verify an uncommitted batch"
391+
);
414392

415-
// Remove previous batch commitment as it is no longer needed.
416-
delete batchCommitments[batchNumber - 1];
393+
// Verify public data for the batch
394+
_verifyPublicData(batchNumber, alignedPublicInputsList[i][8:]);
395+
396+
bytes memory callData = abi.encodeWithSignature(
397+
"verifyProofInclusion(bytes32[],bytes32,bytes)",
398+
alignedMerkleProofsList[i],
399+
SP1_VERIFICATION_KEY,
400+
alignedPublicInputsList[i]
401+
);
402+
(bool callResult, bytes memory response) = ALIGNEDPROOFAGGREGATOR
403+
.staticcall(callData);
404+
require(
405+
callResult,
406+
"OnChainProposer: call to ALIGNEDPROOFAGGREGATOR failed"
407+
);
408+
409+
bool proofVerified = abi.decode(response, (bool));
410+
require(
411+
proofVerified,
412+
"OnChainProposer: Aligned proof verification failed"
413+
);
414+
415+
// The first 2 bytes are the number of deposits.
416+
uint16 deposits_amount = uint16(
417+
bytes2(
418+
batchCommitments[batchNumber]
419+
.processedDepositLogsRollingHash
420+
)
421+
);
422+
if (deposits_amount > 0) {
423+
ICommonBridge(BRIDGE).removePendingDepositLogs(deposits_amount);
424+
}
425+
426+
// Remove previous batch commitment
427+
delete batchCommitments[batchNumber - 1];
428+
429+
lastVerifiedBatch = batchNumber;
430+
batchNumber++;
431+
}
417432

418433
emit BatchVerified(lastVerifiedBatch);
419434
}

crates/l2/contracts/src/l1/based/interfaces/IOnChainProposer.sol

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ interface IOnChainProposer {
8080
// TODO: imageid, programvkey and riscvvkey should be constants
8181
// TODO: organize each zkvm proof arguments in their own structs
8282

83-
/// @notice Method used to verify a batch of L2 blocks in Aligned.
84-
/// @param alignedPublicInputs The public inputs bytes of the proof.
85-
/// @param alignedProgramVKey The public verifying key.
86-
/// @param alignedMerkleProof The Merkle proof (sibling hashes) needed to reconstruct the Merkle root.
87-
function verifyBatchAligned(
88-
uint256 batchNumber,
89-
bytes calldata alignedPublicInputs,
90-
bytes32 alignedProgramVKey,
91-
bytes32[] calldata alignedMerkleProof
83+
/// @notice Method used to verify a sequence of L2 batches in Aligned, starting from `firstBatchNumber`.
84+
/// Each proof corresponds to one batch, and batch numbers must increase by 1 sequentially.
85+
/// @param firstBatchNumber The batch number of the first proof to verify. Must be `lastVerifiedBatch + 1`.
86+
/// @param alignedPublicInputsList An array of public input bytes, one per proof.
87+
/// @param alignedMerkleProofsList An array of Merkle proofs (sibling hashes), one per proof.
88+
function verifyBatchesAligned(
89+
uint256 firstBatchNumber,
90+
bytes[] calldata alignedPublicInputsList,
91+
bytes32[][] calldata alignedMerkleProofsList
9292
) external;
9393
}

crates/l2/contracts/src/l1/interfaces/IOnChainProposer.sol

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,15 @@ interface IOnChainProposer {
8282
// TODO: imageid, programvkey and riscvvkey should be constants
8383
// TODO: organize each zkvm proof arguments in their own structs
8484

85-
/// @notice Method used to verify a batch of L2 blocks in Aligned.
86-
/// @param alignedPublicInputs The public inputs bytes of the proof.
87-
/// @param alignedMerkleProof The Merkle proof (sibling hashes) needed to reconstruct the Merkle root.
88-
function verifyBatchAligned(
89-
uint256 batchNumber,
90-
bytes calldata alignedPublicInputs,
91-
bytes32[] calldata alignedMerkleProof
85+
/// @notice Method used to verify a sequence of L2 batches in Aligned, starting from `firstBatchNumber`.
86+
/// Each proof corresponds to one batch, and batch numbers must increase by 1 sequentially.
87+
/// @param firstBatchNumber The batch number of the first proof to verify. Must be `lastVerifiedBatch + 1`.
88+
/// @param alignedPublicInputsList An array of public input bytes, one per proof.
89+
/// @param alignedMerkleProofsList An array of Merkle proofs (sibling hashes), one per proof.
90+
function verifyBatchesAligned(
91+
uint256 firstBatchNumber,
92+
bytes[] calldata alignedPublicInputsList,
93+
bytes32[][] calldata alignedMerkleProofsList
9294
) external;
9395

9496
/// @notice Allows unverified batches to be reverted

crates/l2/sequencer/configs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use aligned_sdk::common::types::Network;
22
use ethrex_common::{Address, U256};
3+
use reqwest::Url;
34
use secp256k1::SecretKey;
45
use std::net::IpAddr;
56

@@ -85,7 +86,7 @@ pub struct BlockFetcherConfig {
8586
pub struct AlignedConfig {
8687
pub aligned_mode: bool,
8788
pub aligned_verifier_interval_ms: u64,
88-
pub beacon_url: String,
89+
pub beacon_urls: Vec<Url>,
8990
pub network: Network,
9091
pub fee_estimate: String,
9192
pub aligned_sp1_elf_path: String,

0 commit comments

Comments
 (0)