diff --git a/cmd/ethrex/l2/command.rs b/cmd/ethrex/l2/command.rs index 2a29d9ff20..2b59c4ac41 100644 --- a/cmd/ethrex/l2/command.rs +++ b/cmd/ethrex/l2/command.rs @@ -442,6 +442,8 @@ impl Command { deposit_logs_hash: H256::zero(), message_hashes, blobs_bundle: BlobsBundle::empty(), + commit_tx: None, + verify_tx: None, }; // Store batch info in L2 storage diff --git a/crates/common/types/batch.rs b/crates/common/types/batch.rs index 773a974502..c56f9872c3 100644 --- a/crates/common/types/batch.rs +++ b/crates/common/types/batch.rs @@ -1,8 +1,10 @@ +use serde::Serialize; + use crate::H256; use super::BlobsBundle; -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct Batch { pub number: u64, pub first_block: u64, @@ -10,5 +12,8 @@ pub struct Batch { pub state_root: H256, pub deposit_logs_hash: H256, pub message_hashes: Vec, + #[serde(skip_serializing)] pub blobs_bundle: BlobsBundle, + pub commit_tx: Option, + pub verify_tx: Option, } diff --git a/crates/l2/based/block_fetcher.rs b/crates/l2/based/block_fetcher.rs index db3441646e..a360498c06 100644 --- a/crates/l2/based/block_fetcher.rs +++ b/crates/l2/based/block_fetcher.rs @@ -203,30 +203,10 @@ async fn fetch(state: &mut BlockFetcherState) -> Result<(), BlockFetcherError> { "Node is {l2_batches_behind} batches behind. Last batch number known: {last_l2_batch_number_known}, last committed batch number: {last_l2_committed_batch_number}" ); - let batch_committed_logs = get_logs(state).await?; + let (batch_committed_logs, batch_verified_logs) = get_logs(state).await?; - let mut missing_batches_logs = - filter_logs(&batch_committed_logs, last_l2_batch_number_known).await?; - - missing_batches_logs.sort_by_key(|(_log, batch_number)| *batch_number); - - for (batch_committed_log, batch_number) in missing_batches_logs { - let batch_commit_tx_calldata = state - .eth_client - .get_transaction_by_hash(batch_committed_log.transaction_hash) - .await? - .ok_or(BlockFetcherError::InternalError(format!( - "Failed to get the receipt for transaction {:x}", - batch_committed_log.transaction_hash - )))? - .data; - - let batch = decode_batch_from_calldata(&batch_commit_tx_calldata)?; - - store_batch(state, &batch).await?; - - seal_batch(state, &batch, batch_number).await?; - } + process_committed_logs(batch_committed_logs, state, last_l2_batch_number_known).await?; + process_verified_logs(batch_verified_logs, state).await?; } info!("Node is up to date"); @@ -234,13 +214,16 @@ async fn fetch(state: &mut BlockFetcherState) -> Result<(), BlockFetcherError> { Ok(()) } -/// Fetch logs from the L1 chain for the BatchCommitted event. +/// Fetch logs from the L1 chain for the BatchCommitted and BatchVerified events. /// This function fetches logs, starting from the last fetched block number (aka the last block that was processed) /// and going up to the current block number. -async fn get_logs(state: &mut BlockFetcherState) -> Result, BlockFetcherError> { +async fn get_logs( + state: &mut BlockFetcherState, +) -> Result<(Vec, Vec), BlockFetcherError> { let last_l1_block_number = state.eth_client.get_block_number().await?; let mut batch_committed_logs = Vec::new(); + let mut batch_verified_logs = Vec::new(); while state.last_l1_block_fetched < last_l1_block_number { let new_last_l1_fetched_block = min( state.last_l1_block_fetched + state.fetch_block_step, @@ -254,7 +237,7 @@ async fn get_logs(state: &mut BlockFetcherState) -> Result, BlockFet ); // Fetch logs from the L1 chain for the BatchCommitted event. - let logs = state + let committed_logs = state .eth_client .get_logs( state.last_l1_block_fetched + 1, @@ -264,13 +247,64 @@ async fn get_logs(state: &mut BlockFetcherState) -> Result, BlockFet ) .await?; + // Fetch logs from the L1 chain for the BatchVerified event. + let verified_logs = state + .eth_client + .get_logs( + state.last_l1_block_fetched + 1, + new_last_l1_fetched_block, + state.on_chain_proposer_address, + keccak(b"BatchVerified(uint256)"), + ) + .await?; + // Update the last L1 block fetched. state.last_l1_block_fetched = new_last_l1_fetched_block; - batch_committed_logs.extend_from_slice(&logs); + batch_committed_logs.extend_from_slice(&committed_logs); + batch_verified_logs.extend_from_slice(&verified_logs); } - Ok(batch_committed_logs) + Ok((batch_committed_logs, batch_verified_logs)) +} + +/// Process the logs from the event `BatchCommitted`. +/// Gets the committed batches that are missing in the local store from the logs, +/// and seals the batch in the rollup store. +async fn process_committed_logs( + batch_committed_logs: Vec, + state: &mut BlockFetcherState, + last_l2_batch_number_known: u64, +) -> Result<(), BlockFetcherError> { + let mut missing_batches_logs = + filter_logs(&batch_committed_logs, last_l2_batch_number_known).await?; + + missing_batches_logs.sort_by_key(|(_log, batch_number)| *batch_number); + + for (batch_committed_log, batch_number) in missing_batches_logs { + let batch_commit_tx_calldata = state + .eth_client + .get_transaction_by_hash(batch_committed_log.transaction_hash) + .await? + .ok_or(BlockFetcherError::InternalError(format!( + "Failed to get the receipt for transaction {:x}", + batch_committed_log.transaction_hash + )))? + .data; + + let batch = decode_batch_from_calldata(&batch_commit_tx_calldata)?; + + store_batch(state, &batch).await?; + + seal_batch( + state, + &batch, + batch_number, + batch_committed_log.transaction_hash, + ) + .await?; + } + Ok(()) } /// Given the logs from the event `BatchCommitted`, @@ -391,8 +425,9 @@ async fn seal_batch( state: &mut BlockFetcherState, batch: &[Block], batch_number: U256, + commit_tx: H256, ) -> Result<(), BlockFetcherError> { - let batch = get_batch(state, batch, batch_number).await?; + let batch = get_batch(state, batch, batch_number, commit_tx).await?; state.rollup_store.seal_batch(batch).await?; @@ -453,6 +488,7 @@ async fn get_batch( state: &mut BlockFetcherState, batch: &[Block], batch_number: U256, + commit_tx: H256, ) -> Result { let deposits: Vec = batch .iter() @@ -539,5 +575,37 @@ async fn get_batch( deposit_logs_hash, message_hashes: get_batch_message_hashes(state, batch).await?, blobs_bundle, + commit_tx: Some(commit_tx), + verify_tx: None, }) } + +/// Process the logs from the event `BatchVerified`. +/// Gets the batch number from the logs and stores the verify transaction hash in the rollup store +async fn process_verified_logs( + batch_verified_logs: Vec, + state: &mut BlockFetcherState, +) -> Result<(), BlockFetcherError> { + for batch_verified_log in batch_verified_logs { + let batch_number = U256::from_big_endian( + batch_verified_log + .log + .topics + .get(1) + .ok_or(BlockFetcherError::InternalError( + "Failed to get verified batch number from BatchVerified log".to_string(), + ))? + .as_bytes(), + ); + + let verify_tx_hash = batch_verified_log.transaction_hash; + + state + .rollup_store + .store_verify_tx_by_batch(batch_number.as_u64(), verify_tx_hash) + .await?; + + info!("Stored verify transaction hash {verify_tx_hash:#x} for batch {batch_number}"); + } + Ok(()) +} diff --git a/crates/l2/networking/rpc/l2/batch.rs b/crates/l2/networking/rpc/l2/batch.rs new file mode 100644 index 0000000000..6b72db8743 --- /dev/null +++ b/crates/l2/networking/rpc/l2/batch.rs @@ -0,0 +1,102 @@ +use ethrex_common::types::{BlockHash, batch::Batch}; +use ethrex_storage::Store; +use serde::Serialize; +use serde_json::Value; +use tracing::info; + +use crate::{ + rpc::{RpcApiContext, RpcHandler}, + utils::RpcErr, +}; + +#[derive(Serialize)] +pub struct RpcBatch { + #[serde(flatten)] + pub batch: Batch, + #[serde(skip_serializing_if = "Option::is_none")] + pub block_hashes: Option>, +} + +impl RpcBatch { + pub async fn build(batch: Batch, block_hashes: bool, store: &Store) -> Result { + let block_hashes = if block_hashes { + Some(get_block_hashes( + batch.first_block, + batch.last_block, + store, + )?) + } else { + None + }; + + Ok(RpcBatch { + batch, + block_hashes, + }) + } +} + +fn get_block_hashes( + first_block: u64, + last_block: u64, + store: &Store, +) -> Result, RpcErr> { + let mut block_hashes = Vec::new(); + for block_number in first_block..=last_block { + let header = store + .get_block_header(block_number)? + .ok_or(RpcErr::Internal(format!( + "Failed to retrieve block header for block number {block_number}" + )))?; + let hash = header.hash(); + block_hashes.push(hash); + } + Ok(block_hashes) +} + +pub struct GetBatchByBatchNumberRequest { + pub batch_number: u64, + pub block_hashes: bool, +} + +impl RpcHandler for GetBatchByBatchNumberRequest { + fn parse(params: &Option>) -> Result { + let params = params.as_ref().ok_or(ethrex_rpc::RpcErr::BadParams( + "No params provided".to_owned(), + ))?; + if params.len() != 2 { + return Err(ethrex_rpc::RpcErr::BadParams( + "Expected 2 params".to_owned(), + ))?; + }; + // Parse BatchNumber + let hex_str = serde_json::from_value::(params[0].clone()) + .map_err(|e| ethrex_rpc::RpcErr::BadParams(e.to_string()))?; + + // Check that the BatchNumber is 0x prefixed + let hex_str = hex_str + .strip_prefix("0x") + .ok_or(ethrex_rpc::RpcErr::BadHexFormat(0))?; + + // Parse hex string + let batch_number = + u64::from_str_radix(hex_str, 16).map_err(|_| ethrex_rpc::RpcErr::BadHexFormat(0))?; + + let block_hashes = serde_json::from_value(params[1].clone())?; + + Ok(GetBatchByBatchNumberRequest { + batch_number, + block_hashes, + }) + } + + async fn handle(&self, context: RpcApiContext) -> Result { + info!("Requested batch with number: {}", self.batch_number); + let Some(batch) = context.rollup_store.get_batch(self.batch_number).await? else { + return Ok(Value::Null); + }; + let rpc_batch = RpcBatch::build(batch, self.block_hashes, &context.l1_ctx.storage).await?; + + serde_json::to_value(&rpc_batch).map_err(|error| RpcErr::Internal(error.to_string())) + } +} diff --git a/crates/l2/networking/rpc/l2/mod.rs b/crates/l2/networking/rpc/l2/mod.rs index 8bcaa2438f..2e12875449 100644 --- a/crates/l2/networking/rpc/l2/mod.rs +++ b/crates/l2/networking/rpc/l2/mod.rs @@ -1,2 +1,3 @@ +pub mod batch; pub mod l1_message; pub mod transaction; diff --git a/crates/l2/networking/rpc/rpc.rs b/crates/l2/networking/rpc/rpc.rs index ec61da9ad7..d06b3ede14 100644 --- a/crates/l2/networking/rpc/rpc.rs +++ b/crates/l2/networking/rpc/rpc.rs @@ -1,3 +1,4 @@ +use crate::l2::batch::GetBatchByBatchNumberRequest; use crate::l2::l1_message::GetL1MessageProof; use crate::utils::{RpcErr, RpcNamespace, resolve_namespace}; use axum::extract::State; @@ -207,6 +208,7 @@ pub async fn map_l2_requests(req: &RpcRequest, context: RpcApiContext) -> Result match req.method.as_str() { "ethrex_sendTransaction" => SponsoredTx::call(req, context).await, "ethrex_getMessageProof" => GetL1MessageProof::call(req, context).await, + "ethrex_getBatchByNumber" => GetBatchByBatchNumberRequest::call(req, context).await, unknown_ethrex_l2_method => { Err(ethrex_rpc::RpcErr::MethodNotFound(unknown_ethrex_l2_method.to_owned()).into()) } diff --git a/crates/l2/sequencer/l1_committer.rs b/crates/l2/sequencer/l1_committer.rs index eba9e6a2d9..efd5d168eb 100644 --- a/crates/l2/sequencer/l1_committer.rs +++ b/crates/l2/sequencer/l1_committer.rs @@ -214,6 +214,8 @@ async fn commit_next_batch_to_l1(state: &mut CommitterState) -> Result<(), Commi deposit_logs_hash, message_hashes, blobs_bundle, + commit_tx: None, + verify_tx: None, }; state.rollup_store.seal_batch(batch.clone()).await?; @@ -252,6 +254,11 @@ async fn commit_next_batch_to_l1(state: &mut CommitterState) -> Result<(), Commi }); ); + state + .rollup_store + .store_commit_tx_by_batch(batch.number, commit_tx_hash) + .await?; + info!( "Commitment sent for batch {}, with tx hash {commit_tx_hash:#x}.", batch.number diff --git a/crates/l2/sequencer/l1_proof_sender.rs b/crates/l2/sequencer/l1_proof_sender.rs index cad6fade69..09fa0a0030 100644 --- a/crates/l2/sequencer/l1_proof_sender.rs +++ b/crates/l2/sequencer/l1_proof_sender.rs @@ -343,6 +343,11 @@ pub async fn send_proof_to_contract( ) .await?; + state + .rollup_store + .store_verify_tx_by_batch(batch_number, verify_tx_hash) + .await?; + info!( ?batch_number, ?verify_tx_hash, diff --git a/crates/l2/sequencer/l1_proof_verifier.rs b/crates/l2/sequencer/l1_proof_verifier.rs index 8bf4fbacf3..944d06b708 100644 --- a/crates/l2/sequencer/l1_proof_verifier.rs +++ b/crates/l2/sequencer/l1_proof_verifier.rs @@ -185,6 +185,14 @@ impl L1ProofVerifier { ) .await?; + // Store the verify transaction hash for each batch that was aggregated. + for i in 0..aggregated_proofs_count { + let batch_number = first_batch_number + i; + self.rollup_store + .store_verify_tx_by_batch(batch_number, verify_tx_hash) + .await?; + } + Ok(Some(verify_tx_hash)) } diff --git a/crates/l2/storage/src/api.rs b/crates/l2/storage/src/api.rs index d315f86f25..e3e8e90b59 100644 --- a/crates/l2/storage/src/api.rs +++ b/crates/l2/storage/src/api.rs @@ -86,6 +86,28 @@ pub trait StoreEngineRollup: Debug + Send + Sync { batch_number: u64, ) -> Result>, RollupStoreError>; + async fn get_commit_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError>; + + async fn store_commit_tx_by_batch( + &self, + batch_number: u64, + commit_tx: H256, + ) -> Result<(), RollupStoreError>; + + async fn get_verify_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError>; + + async fn store_verify_tx_by_batch( + &self, + batch_number: u64, + verify_tx: H256, + ) -> Result<(), RollupStoreError>; + async fn update_operations_count( &self, transaction_inc: u64, diff --git a/crates/l2/storage/src/store.rs b/crates/l2/storage/src/store.rs index 498b892134..5ed86ffd6c 100644 --- a/crates/l2/storage/src/store.rs +++ b/crates/l2/storage/src/store.rs @@ -75,6 +75,8 @@ impl Store { deposit_logs_hash: H256::zero(), message_hashes: Vec::new(), blobs_bundle: BlobsBundle::empty(), + commit_tx: None, + verify_tx: None, }) .await?; // Sets the lastest sent batch proof to 0 @@ -190,6 +192,40 @@ impl Store { .await } + pub async fn get_commit_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + self.engine.get_commit_tx_by_batch(batch_number).await + } + + pub async fn store_commit_tx_by_batch( + &self, + batch_number: u64, + commit_tx: H256, + ) -> Result<(), RollupStoreError> { + self.engine + .store_commit_tx_by_batch(batch_number, commit_tx) + .await + } + + pub async fn get_verify_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + self.engine.get_verify_tx_by_batch(batch_number).await + } + + pub async fn store_verify_tx_by_batch( + &self, + batch_number: u64, + verify_tx: H256, + ) -> Result<(), RollupStoreError> { + self.engine + .store_verify_tx_by_batch(batch_number, verify_tx) + .await + } + pub async fn get_batch(&self, batch_number: u64) -> Result, RollupStoreError> { let Some(blocks) = self.get_block_numbers_by_batch(batch_number).await? else { return Ok(None); @@ -225,10 +261,7 @@ impl Store { let message_hashes = self .get_message_hashes_by_batch(batch_number) .await? - .ok_or(RollupStoreError::Custom( - "Failed while trying to retrieve the message hashes of a known batch. This is a bug." - .to_owned(), - ))?; + .unwrap_or_default(); let deposit_logs_hash = self .get_deposit_logs_hash_by_batch(batch_number) .await?.ok_or(RollupStoreError::Custom( @@ -236,6 +269,10 @@ impl Store { .to_owned(), ))?; + let commit_tx = self.get_commit_tx_by_batch(batch_number).await?; + + let verify_tx = self.get_verify_tx_by_batch(batch_number).await?; + Ok(Some(Batch { number: batch_number, first_block, @@ -244,6 +281,8 @@ impl Store { blobs_bundle, message_hashes, deposit_logs_hash, + commit_tx, + verify_tx, })) } @@ -264,6 +303,14 @@ impl Store { .await?; self.store_state_root_by_batch(batch.number, batch.state_root) .await?; + if let Some(commit_tx) = batch.commit_tx { + self.store_commit_tx_by_batch(batch.number, commit_tx) + .await?; + } + if let Some(verify_tx) = batch.verify_tx { + self.store_verify_tx_by_batch(batch.number, verify_tx) + .await?; + } Ok(()) } diff --git a/crates/l2/storage/src/store_db/in_memory.rs b/crates/l2/storage/src/store_db/in_memory.rs index ea18464f45..4c52469853 100644 --- a/crates/l2/storage/src/store_db/in_memory.rs +++ b/crates/l2/storage/src/store_db/in_memory.rs @@ -38,6 +38,10 @@ struct StoreInner { account_updates_by_block_number: HashMap>, /// Map of (ProverType, batch_number) to batch proof data batch_proofs: HashMap<(ProverType, u64), BatchProof>, + /// Map of batch number to commit transaction hash + commit_txs: HashMap, + /// Map of batch number to verify transaction hash + verify_txs: HashMap, } impl Store { @@ -172,6 +176,38 @@ impl StoreEngineRollup for Store { Ok(self.inner()?.blobs.get(&batch_number).cloned()) } + async fn get_commit_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + Ok(self.inner()?.commit_txs.get(&batch_number).cloned()) + } + + async fn store_commit_tx_by_batch( + &self, + batch_number: u64, + commit_tx: H256, + ) -> Result<(), RollupStoreError> { + self.inner()?.commit_txs.insert(batch_number, commit_tx); + Ok(()) + } + + async fn get_verify_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + Ok(self.inner()?.verify_txs.get(&batch_number).cloned()) + } + + async fn store_verify_tx_by_batch( + &self, + batch_number: u64, + verify_tx: H256, + ) -> Result<(), RollupStoreError> { + self.inner()?.verify_txs.insert(batch_number, verify_tx); + Ok(()) + } + async fn contains_batch(&self, batch_number: &u64) -> Result { Ok(self .inner()? diff --git a/crates/l2/storage/src/store_db/libmdbx.rs b/crates/l2/storage/src/store_db/libmdbx.rs index 7930d73b44..1a6d52730b 100644 --- a/crates/l2/storage/src/store_db/libmdbx.rs +++ b/crates/l2/storage/src/store_db/libmdbx.rs @@ -79,6 +79,8 @@ pub fn init_db(path: Option>) -> Result Result, RollupStoreError> { + Ok(self + .read::(batch_number) + .await? + .map(|tx| tx.to())) + } + + async fn store_commit_tx_by_batch( + &self, + batch_number: u64, + commit_tx: H256, + ) -> Result<(), RollupStoreError> { + self.write::(batch_number, commit_tx.into()) + .await + } + + async fn get_verify_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + Ok(self + .read::(batch_number) + .await? + .map(|tx| tx.to())) + } + + async fn store_verify_tx_by_batch( + &self, + batch_number: u64, + verify_tx: H256, + ) -> Result<(), RollupStoreError> { + self.write::(batch_number, verify_tx.into()) + .await + } + async fn contains_batch(&self, batch_number: &u64) -> Result { let exists = self .read::(*batch_number) @@ -406,3 +446,13 @@ table!( /// Value is the bincode-encoded BatchProof data. (BatchProofs) (u64, u32) => Vec ); + +table!( + /// Commit transaction by batch number + ( CommitTxByBatch ) u64 => Rlp +); + +table!( + /// Verify transaction by batch number + ( VerifyTxByBatch ) u64 => Rlp +); diff --git a/crates/l2/storage/src/store_db/redb.rs b/crates/l2/storage/src/store_db/redb.rs index 3a410b58dd..b08bbe945d 100644 --- a/crates/l2/storage/src/store_db/redb.rs +++ b/crates/l2/storage/src/store_db/redb.rs @@ -41,6 +41,10 @@ const ACCOUNT_UPDATES_BY_BLOCK_NUMBER: TableDefinition> = const BATCH_PROOF_BY_BATCH_AND_TYPE: TableDefinition<(u64, u32), Vec> = TableDefinition::new("BatchProofByBatchAndType"); +const COMMIT_TX_BY_BATCH: TableDefinition> = TableDefinition::new("CommitTxByBatch"); + +const VERIFY_TX_BY_BATCH: TableDefinition> = TableDefinition::new("VerifyTxByBatch"); + #[derive(Debug)] pub struct RedBStoreRollup { db: Arc, @@ -121,6 +125,8 @@ pub fn init_db() -> Result { table_creation_txn.open_table(LAST_SENT_BATCH_PROOF)?; table_creation_txn.open_table(ACCOUNT_UPDATES_BY_BLOCK_NUMBER)?; table_creation_txn.open_table(BATCH_PROOF_BY_BATCH_AND_TYPE)?; + table_creation_txn.open_table(COMMIT_TX_BY_BATCH)?; + table_creation_txn.open_table(VERIFY_TX_BY_BATCH)?; table_creation_txn.commit()?; Ok(db) @@ -258,6 +264,44 @@ impl StoreEngineRollup for RedBStoreRollup { .map(|rlp| rlp.value().to())) } + async fn get_commit_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + Ok(self + .read(COMMIT_TX_BY_BATCH, batch_number) + .await? + .map(|rlp| rlp.value().to())) + } + + async fn store_commit_tx_by_batch( + &self, + batch_number: u64, + commit_tx: H256, + ) -> Result<(), RollupStoreError> { + self.write(COMMIT_TX_BY_BATCH, batch_number, commit_tx.into()) + .await + } + + async fn get_verify_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + Ok(self + .read(VERIFY_TX_BY_BATCH, batch_number) + .await? + .map(|rlp| rlp.value().to())) + } + + async fn store_verify_tx_by_batch( + &self, + batch_number: u64, + verify_tx: H256, + ) -> Result<(), RollupStoreError> { + self.write(VERIFY_TX_BY_BATCH, batch_number, verify_tx.into()) + .await + } + async fn update_operations_count( &self, transaction_inc: u64, diff --git a/crates/l2/storage/src/store_db/sql.rs b/crates/l2/storage/src/store_db/sql.rs index 7406d26e00..a7a4d965b2 100644 --- a/crates/l2/storage/src/store_db/sql.rs +++ b/crates/l2/storage/src/store_db/sql.rs @@ -22,13 +22,15 @@ impl Debug for SQLStore { } } -const DB_SCHEMA: [&str; 11] = [ +const DB_SCHEMA: [&str; 13] = [ "CREATE TABLE blocks (block_number INT PRIMARY KEY, batch INT)", "CREATE TABLE messages (batch INT, idx INT, message_hash BLOB, PRIMARY KEY (batch, idx))", "CREATE TABLE deposits (batch INT PRIMARY KEY, deposit_hash BLOB)", "CREATE TABLE state_roots (batch INT PRIMARY KEY, state_root BLOB)", "CREATE TABLE blob_bundles (batch INT, idx INT, blob_bundle BLOB, PRIMARY KEY (batch, idx))", "CREATE TABLE account_updates (block_number INT PRIMARY KEY, updates BLOB)", + "CREATE TABLE commit_txs (batch INT PRIMARY KEY, commit_tx BLOB)", + "CREATE TABLE verify_txs (batch INT PRIMARY KEY, verify_tx BLOB)", "CREATE TABLE operation_count (_id INT PRIMARY KEY, transactions INT, deposits INT, messages INT)", "INSERT INTO operation_count VALUES (0, 0, 0, 0)", "CREATE TABLE latest_sent (_id INT PRIMARY KEY, batch INT)", @@ -331,6 +333,64 @@ impl StoreEngineRollup for SQLStore { } } + async fn store_commit_tx_by_batch( + &self, + batch_number: u64, + commit_tx: H256, + ) -> Result<(), RollupStoreError> { + self.execute_in_tx(vec![( + "INSERT OR REPLACE INTO commit_txs VALUES (?1, ?2)", + (batch_number, Vec::from(commit_tx.to_fixed_bytes())).into_params()?, + )]) + .await + } + + async fn get_commit_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + let mut rows = self + .query( + "SELECT commit_tx FROM commit_txs WHERE batch = ?1", + vec![batch_number], + ) + .await?; + if let Some(row) = rows.next().await? { + let vec = read_from_row_blob(&row, 0)?; + return Ok(Some(H256::from_slice(&vec))); + } + Ok(None) + } + + async fn store_verify_tx_by_batch( + &self, + batch_number: u64, + verify_tx: H256, + ) -> Result<(), RollupStoreError> { + self.execute_in_tx(vec![( + "INSERT OR REPLACE INTO verify_txs VALUES (?1, ?2)", + (batch_number, Vec::from(verify_tx.to_fixed_bytes())).into_params()?, + )]) + .await + } + + async fn get_verify_tx_by_batch( + &self, + batch_number: u64, + ) -> Result, RollupStoreError> { + let mut rows = self + .query( + "SELECT verify_tx FROM verify_txs WHERE batch = ?1", + vec![batch_number], + ) + .await?; + if let Some(row) = rows.next().await? { + let vec = read_from_row_blob(&row, 0)?; + return Ok(Some(H256::from_slice(&vec))); + } + Ok(None) + } + async fn update_operations_count( &self, transaction_inc: u64,