From fd86d4dea295e1762d551740856bb6454c7ee823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 19 Nov 2024 11:54:21 -0300 Subject: [PATCH 1/5] Fix nonce error on l1_watcher --- crates/l2/proposer/l1_watcher.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index ed3aaa8fe..121b86e58 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -20,8 +20,11 @@ use tracing::{debug, info, warn}; pub async fn start_l1_watcher(store: Store) { let eth_config = EthConfig::from_env().expect("EthConfig::from_env()"); let watcher_config = L1WatcherConfig::from_env().expect("L1WatcherConfig::from_env()"); + let sleep_duration = Duration::from_millis(watcher_config.check_interval_ms.clone()); let mut l1_watcher = L1Watcher::new_from_config(watcher_config, eth_config); loop { + sleep(sleep_duration).await; + let logs = l1_watcher.get_logs().await.expect("l1_watcher.get_logs()"); let _deposit_txs = l1_watcher .process_logs(logs, &store) @@ -34,7 +37,6 @@ pub struct L1Watcher { eth_client: EthClient, address: Address, topics: Vec, - check_interval: Duration, max_block_step: U256, last_block_fetched: U256, l2_proposer_pk: SecretKey, @@ -47,7 +49,6 @@ impl L1Watcher { eth_client: EthClient::new_from_config(eth_config), address: watcher_config.bridge_address, topics: watcher_config.topics, - check_interval: Duration::from_millis(watcher_config.check_interval_ms), max_block_step: watcher_config.max_block_step, last_block_fetched: U256::zero(), l2_proposer_pk: watcher_config.l2_proposer_private_key, @@ -91,8 +92,6 @@ impl L1Watcher { self.last_block_fetched = new_last_block; - sleep(self.check_interval).await; - Ok(logs) } @@ -108,8 +107,8 @@ impl L1Watcher { let mut deposit_txs = Vec::new(); let mut operator_nonce = store .get_account_info( - self.eth_client.get_block_number().await?.as_u64(), - self.l2_proposer_address, + store.get_latest_block_number().unwrap().unwrap(), + Address::zero(), ) .map_err(|e| L1WatcherError::FailedToRetrieveDepositorAccountInfo(e.to_string()))? .map(|info| info.nonce) From 989061ecd0fc7caad816ea9fd95036e8d7adc185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 19 Nov 2024 11:57:17 -0300 Subject: [PATCH 2/5] Check deposits are pending before adding to mempool --- crates/l2/contracts/src/l1/CommonBridge.sol | 5 +++ .../src/l1/interfaces/ICommonBridge.sol | 5 +++ crates/l2/proposer/l1_watcher.rs | 34 ++++++++++++++++++- crates/l2/proposer/prover_server.rs | 13 ++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/crates/l2/contracts/src/l1/CommonBridge.sol b/crates/l2/contracts/src/l1/CommonBridge.sol index 93afb58fb..59ef19e5f 100644 --- a/crates/l2/contracts/src/l1/CommonBridge.sol +++ b/crates/l2/contracts/src/l1/CommonBridge.sol @@ -55,6 +55,11 @@ contract CommonBridge is ICommonBridge, Ownable, ReentrancyGuard { ON_CHAIN_PROPOSER = onChainProposer; } + /// @inheritdoc ICommonBridge + function getDepositLogs() public view returns (bytes32[] memory) { + return depositLogs; + } + /// @inheritdoc ICommonBridge function deposit(address to) public payable { require(msg.value > 0, "CommonBridge: amount to deposit is zero"); diff --git a/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol b/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol index 92f6f6721..4958eafaa 100644 --- a/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol +++ b/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol @@ -41,6 +41,11 @@ interface ICommonBridge { uint256 indexed claimedAmount ); + /// @notice Method to retrieve all the deposit logs hashes. + /// @dev This method is used by the L2 L1_Watcher to get the remaining + /// deposit logs to be processed. + function getDepositLogs() external view returns (bytes32[] memory); + /// @notice Initializes the contract. /// @dev This method is called only once after the contract is deployed. /// @dev It sets the OnChainProposer address. diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index 121b86e58..bfd004b3f 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -12,6 +12,7 @@ use ethereum_rust_core::types::{Signable, Transaction}; use ethereum_rust_rpc::types::receipt::RpcLog; use ethereum_rust_storage::Store; use ethereum_types::{Address, BigEndianHash, H256, U256}; +use keccak_hash::keccak; use secp256k1::SecretKey; use std::{cmp::min, ops::Mul, time::Duration}; use tokio::time::sleep; @@ -25,9 +26,10 @@ pub async fn start_l1_watcher(store: Store) { loop { sleep(sleep_duration).await; + let l1_deposits_logs = l1_watcher.get_l1_deposit_logs().await.unwrap(); let logs = l1_watcher.get_logs().await.expect("l1_watcher.get_logs()"); let _deposit_txs = l1_watcher - .process_logs(logs, &store) + .process_logs(logs, &l1_deposits_logs, &store) .await .expect("l1_watcher.process_logs()"); } @@ -56,6 +58,28 @@ impl L1Watcher { } } + pub async fn get_l1_deposit_logs(&self) -> Result, L1WatcherError> { + Ok(hex::decode( + &self + .eth_client + .call( + self.address, + Bytes::copy_from_slice(&[0x35, 0x6d, 0xa2, 0x49]), + Overrides::default(), + ) + .await?[2..], + ) + .expect("Not a valid hex string") + .chunks(32) + .map(|chunk| H256::from_slice(chunk)) + .collect::>() + .split_at(2) // Two first words are index and length abi encode + .1 + .iter() + .map(|x| *x) + .collect()) + } + pub async fn get_logs(&mut self) -> Result, L1WatcherError> { let current_block = self.eth_client.get_block_number().await?; @@ -98,6 +122,7 @@ impl L1Watcher { pub async fn process_logs( &self, logs: Vec, + l1_deposit_logs: &Vec, store: &Store, ) -> Result, L1WatcherError> { if logs.is_empty() { @@ -130,6 +155,13 @@ impl L1Watcher { )) })?; + let mut value_bytes = [0u8; 32]; + mint_value.to_big_endian(&mut value_bytes); + if !l1_deposit_logs.contains(&keccak([beneficiary.as_bytes(), &value_bytes].concat())) { + warn!("Deposit already processed (to: {beneficiary:#x}, value: {mint_value}), skipping."); + continue; + } + info!("Initiating mint transaction for {beneficiary:#x} with value {mint_value:#x}",); let mut mint_transaction = self diff --git a/crates/l2/proposer/prover_server.rs b/crates/l2/proposer/prover_server.rs index 951e81924..eaff99aae 100644 --- a/crates/l2/proposer/prover_server.rs +++ b/crates/l2/proposer/prover_server.rs @@ -118,6 +118,9 @@ pub async fn start_prover_server(store: Store) { .await .unwrap(); + info!("Sending verify transaction with tx hash: {tx_hash:#x}"); + + let mut retries = 1; while eth_client .get_transaction_receipt(tx_hash) .await @@ -125,9 +128,17 @@ pub async fn start_prover_server(store: Store) { .is_none() { thread::sleep(Duration::from_secs(1)); + retries += 1; + if retries > 10 { + error!("Couldn't find receipt for transaction {tx_hash:#x}"); + panic!("Couldn't find receipt for transaction {tx_hash:#x}"); + } } - info!("Mocked verify transaction sent"); + info!( + "Mocked verify transaction sent for block {}", + last_verified_block + 1 + ); } } else { let mut prover_server = ProverServer::new_from_config( From 2ff7d0030287cb0518e51dbd46f135cd5d55d260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 19 Nov 2024 12:07:14 -0300 Subject: [PATCH 3/5] Improve error handling --- crates/l2/proposer/l1_watcher.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index bfd004b3f..5e91e1415 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -26,10 +26,22 @@ pub async fn start_l1_watcher(store: Store) { loop { sleep(sleep_duration).await; - let l1_deposits_logs = l1_watcher.get_l1_deposit_logs().await.unwrap(); - let logs = l1_watcher.get_logs().await.expect("l1_watcher.get_logs()"); + let pending_deposits_logs = match l1_watcher.get_pending_deposit_logs().await { + Ok(logs) => logs, + Err(error) => { + warn!("Error when getting L1 pending deposit logs: {}", error); + continue; + } + }; + let logs = match l1_watcher.get_logs().await { + Ok(logs) => logs, + Err(error) => { + warn!("Error when getting logs from L1: {}", error); + continue; + } + }; let _deposit_txs = l1_watcher - .process_logs(logs, &l1_deposits_logs, &store) + .process_logs(logs, &pending_deposits_logs, &store) .await .expect("l1_watcher.process_logs()"); } @@ -58,7 +70,7 @@ impl L1Watcher { } } - pub async fn get_l1_deposit_logs(&self) -> Result, L1WatcherError> { + pub async fn get_pending_deposit_logs(&self) -> Result, L1WatcherError> { Ok(hex::decode( &self .eth_client @@ -69,7 +81,7 @@ impl L1Watcher { ) .await?[2..], ) - .expect("Not a valid hex string") + .map_err(|_| L1WatcherError::FailedToDeserializeLog("Not a valid hex string".to_string()))? .chunks(32) .map(|chunk| H256::from_slice(chunk)) .collect::>() @@ -132,7 +144,12 @@ impl L1Watcher { let mut deposit_txs = Vec::new(); let mut operator_nonce = store .get_account_info( - store.get_latest_block_number().unwrap().unwrap(), + store + .get_latest_block_number() + .map_err(|e| L1WatcherError::FailedToRetrieveChainConfig(e.to_string()))? + .ok_or(L1WatcherError::FailedToRetrieveChainConfig( + "Last block is None".to_string(), + ))?, Address::zero(), ) .map_err(|e| L1WatcherError::FailedToRetrieveDepositorAccountInfo(e.to_string()))? From 33c87f46c7f14d85f800af2a9b931bda860d5dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 19 Nov 2024 12:13:08 -0300 Subject: [PATCH 4/5] Only check pending logs when new events are read --- crates/l2/proposer/l1_watcher.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index 5e91e1415..c59e1184d 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -26,17 +26,21 @@ pub async fn start_l1_watcher(store: Store) { loop { sleep(sleep_duration).await; - let pending_deposits_logs = match l1_watcher.get_pending_deposit_logs().await { + let logs = match l1_watcher.get_logs().await { Ok(logs) => logs, Err(error) => { - warn!("Error when getting L1 pending deposit logs: {}", error); + warn!("Error when getting logs from L1: {}", error); continue; } }; - let logs = match l1_watcher.get_logs().await { + if logs.is_empty() { + continue; + } + + let pending_deposits_logs = match l1_watcher.get_pending_deposit_logs().await { Ok(logs) => logs, Err(error) => { - warn!("Error when getting logs from L1: {}", error); + warn!("Error when getting L1 pending deposit logs: {}", error); continue; } }; @@ -137,10 +141,6 @@ impl L1Watcher { l1_deposit_logs: &Vec, store: &Store, ) -> Result, L1WatcherError> { - if logs.is_empty() { - return Ok(Vec::new()); - } - let mut deposit_txs = Vec::new(); let mut operator_nonce = store .get_account_info( From b9470326d60550bf25e398c9bd35b9eeef20bc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20I=C3=B1aki=20Bilbao?= Date: Tue, 19 Nov 2024 12:21:33 -0300 Subject: [PATCH 5/5] Fix clippy --- crates/l2/proposer/l1_watcher.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index c59e1184d..910cb8274 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -21,7 +21,7 @@ use tracing::{debug, info, warn}; pub async fn start_l1_watcher(store: Store) { let eth_config = EthConfig::from_env().expect("EthConfig::from_env()"); let watcher_config = L1WatcherConfig::from_env().expect("L1WatcherConfig::from_env()"); - let sleep_duration = Duration::from_millis(watcher_config.check_interval_ms.clone()); + let sleep_duration = Duration::from_millis(watcher_config.check_interval_ms); let mut l1_watcher = L1Watcher::new_from_config(watcher_config, eth_config); loop { sleep(sleep_duration).await; @@ -58,7 +58,6 @@ pub struct L1Watcher { max_block_step: U256, last_block_fetched: U256, l2_proposer_pk: SecretKey, - l2_proposer_address: Address, } impl L1Watcher { @@ -70,7 +69,6 @@ impl L1Watcher { max_block_step: watcher_config.max_block_step, last_block_fetched: U256::zero(), l2_proposer_pk: watcher_config.l2_proposer_private_key, - l2_proposer_address: watcher_config.l2_proposer_address, } } @@ -87,13 +85,11 @@ impl L1Watcher { ) .map_err(|_| L1WatcherError::FailedToDeserializeLog("Not a valid hex string".to_string()))? .chunks(32) - .map(|chunk| H256::from_slice(chunk)) + .map(H256::from_slice) .collect::>() .split_at(2) // Two first words are index and length abi encode .1 - .iter() - .map(|x| *x) - .collect()) + .to_vec()) } pub async fn get_logs(&mut self) -> Result, L1WatcherError> { @@ -138,7 +134,7 @@ impl L1Watcher { pub async fn process_logs( &self, logs: Vec, - l1_deposit_logs: &Vec, + l1_deposit_logs: &[H256], store: &Store, ) -> Result, L1WatcherError> { let mut deposit_txs = Vec::new();