From e6fa92dd1ed7a72c5e0711a35a64b4322fef8f2c Mon Sep 17 00:00:00 2001 From: erhant Date: Wed, 10 Jun 2026 14:51:01 +0300 Subject: [PATCH 1/6] initial hotfixes to bypass system account guards to allow deposit txes --- lez/common/src/transaction.rs | 57 +++++++++++++++++++++++------ lez/indexer/core/src/block_store.rs | 5 ++- lez/storage/src/indexer/mod.rs | 5 ++- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/lez/common/src/transaction.rs b/lez/common/src/transaction.rs index 42a7b761..60e3dcd9 100644 --- a/lez/common/src/transaction.rs +++ b/lez/common/src/transaction.rs @@ -78,18 +78,9 @@ impl LeeTransaction { block_id: BlockId, timestamp: Timestamp, ) -> Result { - let diff = match self { - Self::Public(tx) => { - ValidatedStateDiff::from_public_transaction(tx, state, block_id, timestamp) - } - Self::PrivacyPreserving(tx) => ValidatedStateDiff::from_privacy_preserving_transaction( - tx, state, block_id, timestamp, - ), - Self::ProgramDeployment(tx) => { - ValidatedStateDiff::from_program_deployment_transaction(tx, state) - } - }?; + let diff = self.compute_state_diff(state, block_id, timestamp)?; + // system accounts guard let system_accounts = lee::CLOCK_PROGRAM_ACCOUNT_IDS.iter().copied().chain([ lee::system_faucet_account_id(), lee::system_bridge_account_id(), @@ -101,6 +92,28 @@ impl LeeTransaction { Ok(diff) } + /// Computes the validated state diff without enforcing the system-account + /// restriction. Shared by [`Self::validate_on_state`] and + /// [`Self::execute_unchecked_on_state`]. + fn compute_state_diff( + &self, + state: &V03State, + block_id: BlockId, + timestamp: Timestamp, + ) -> Result { + match self { + Self::Public(tx) => { + ValidatedStateDiff::from_public_transaction(tx, state, block_id, timestamp) + } + Self::PrivacyPreserving(tx) => ValidatedStateDiff::from_privacy_preserving_transaction( + tx, state, block_id, timestamp, + ), + Self::ProgramDeployment(tx) => { + ValidatedStateDiff::from_program_deployment_transaction(tx, state) + } + } + } + /// Validates the transaction against the current state, rejects modifications to clock /// system accounts, and applies the resulting diff to the state. pub fn execute_check_on_state( @@ -115,6 +128,28 @@ impl LeeTransaction { state.apply_state_diff(diff); Ok(self) } + + /// Similar to [`Self::execute_check_on_state`], but skips the system-account guard. + /// + /// FIXME: HOT FIX (testnet v0.2): the indexer replays blocks the sequencer already + /// accepted, including sequencer-generated deposit transactions that + /// legitimately modify the bridge account. The `TransactionOrigin::Sequencer` + /// tag that lets the sequencer bypass the guard is not carried in the block, + /// so the indexer cannot yet distinguish deposit txs from user txs. + /// + /// REMOVE ME when the indexer can authenticate deposit transactions + pub fn execute_unchecked_on_state( + self, + state: &mut V03State, + block_id: BlockId, + timestamp: Timestamp, + ) -> Result { + let diff = self + .compute_state_diff(state, block_id, timestamp) + .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + state.apply_state_diff(diff); + Ok(self) + } } impl From for LeeTransaction { diff --git a/lez/indexer/core/src/block_store.rs b/lez/indexer/core/src/block_store.rs index d293637c..810cfa1b 100644 --- a/lez/indexer/core/src/block_store.rs +++ b/lez/indexer/core/src/block_store.rs @@ -171,7 +171,10 @@ impl IndexerStore { transaction .clone() .transaction_stateless_check()? - .execute_check_on_state( + // FIXME: HOT FIX (testnet v0.2): does not check for system account updates due to + // sequencer-generated deposit tx'es; + // CHANGE ME back to `execute_check_on_state` when the indexer can authenticate deposit transactions + .execute_unchecked_on_state( &mut state_guard, block.header.block_id, block.header.timestamp, diff --git a/lez/storage/src/indexer/mod.rs b/lez/storage/src/indexer/mod.rs index 51df8042..008958ed 100644 --- a/lez/storage/src/indexer/mod.rs +++ b/lez/storage/src/indexer/mod.rs @@ -208,7 +208,10 @@ impl RocksDBIO { "transaction pre check failed with err {err:?}" )) })? - .execute_check_on_state( + // FIXME: HOT FIX (testnet v0.2): does not check for system account updates due to + // sequencer-generated deposit tx'es; + // CHANGE ME back to `execute_check_on_state` when the indexer can authenticate deposit transactions + .execute_unchecked_on_state( &mut breakpoint, block.header.block_id, block.header.timestamp, From 12ee03027699667f58e022a199b741c2888f2b60 Mon Sep 17 00:00:00 2001 From: erhant Date: Wed, 10 Jun 2026 22:16:19 +0300 Subject: [PATCH 2/6] tiny lint err --- lez/common/src/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lez/common/src/transaction.rs b/lez/common/src/transaction.rs index 60e3dcd9..19b25b6d 100644 --- a/lez/common/src/transaction.rs +++ b/lez/common/src/transaction.rs @@ -137,7 +137,7 @@ impl LeeTransaction { /// tag that lets the sequencer bypass the guard is not carried in the block, /// so the indexer cannot yet distinguish deposit txs from user txs. /// - /// REMOVE ME when the indexer can authenticate deposit transactions + /// REMOVE ME when the indexer can authenticate deposit transactions. pub fn execute_unchecked_on_state( self, state: &mut V03State, From 29358d8664acb259f940c45b8c7f9ca2eb7c06ef Mon Sep 17 00:00:00 2001 From: erhant Date: Wed, 10 Jun 2026 22:47:59 +0300 Subject: [PATCH 3/6] simplify deref trick --- integration_tests/tests/bridge.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs index 81f62f2b..f7fd7bf6 100644 --- a/integration_tests/tests/bridge.rs +++ b/integration_tests/tests/bridge.rs @@ -4,12 +4,14 @@ reason = "We don't care about these in tests" )] -use std::time::Duration; +use std::{ops::Deref, time::Duration}; use anyhow::Context as _; use borsh::BorshSerialize; use common::transaction::LeeTransaction; -use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext}; +use integration_tests::{ + TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, wait_for_indexer_to_catch_up, +}; use lee::{ AccountId, execute_and_prove, privacy_preserving_transaction, program::Program, public_transaction, @@ -450,5 +452,25 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result< "Recipient balance should increase by claimed amount" ); + // The indexer must replay the deposit and claim blocks and reach the same + // state as the sequencer — including the bridge system account the deposit + // modifies, which is the case the hot fix unblocks. + wait_for_indexer_to_catch_up(&ctx).await?; + let bridge_account_id = lee::system_bridge_account_id(); + for account_id in [recipient_id, recipient_vault_id, bridge_account_id] { + let indexer_account = indexer_service_rpc::RpcClient::get_account( + // `deref` is needed for correct trait resolution of the async `get_account` method on `RpcClient` + ctx.indexer_client().deref(), + account_id.into(), + ) + .await?; + let sequencer_account = ctx.sequencer_client().get_account(account_id).await?; + assert_eq!( + indexer_account, + sequencer_account.into(), + "Indexer and sequencer diverged for account {account_id} after deposit" + ); + } + Ok(()) } From 551fa79ffd569fd58dc5e2fdb7e0a55c160a32b1 Mon Sep 17 00:00:00 2001 From: erhant Date: Wed, 10 Jun 2026 22:57:08 +0300 Subject: [PATCH 4/6] fmt fix --- integration_tests/tests/bridge.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs index f7fd7bf6..7e9351bb 100644 --- a/integration_tests/tests/bridge.rs +++ b/integration_tests/tests/bridge.rs @@ -459,7 +459,8 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result< let bridge_account_id = lee::system_bridge_account_id(); for account_id in [recipient_id, recipient_vault_id, bridge_account_id] { let indexer_account = indexer_service_rpc::RpcClient::get_account( - // `deref` is needed for correct trait resolution of the async `get_account` method on `RpcClient` + // `deref` is needed for correct trait resolution + // of the async `get_account` method on `RpcClient` ctx.indexer_client().deref(), account_id.into(), ) From 73f540fa0a662601613bacfa37ad9c5f394ed97a Mon Sep 17 00:00:00 2001 From: erhant Date: Wed, 10 Jun 2026 23:30:03 +0300 Subject: [PATCH 5/6] linter happy --- integration_tests/tests/bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/tests/bridge.rs b/integration_tests/tests/bridge.rs index 7e9351bb..8b9f02f2 100644 --- a/integration_tests/tests/bridge.rs +++ b/integration_tests/tests/bridge.rs @@ -4,7 +4,7 @@ reason = "We don't care about these in tests" )] -use std::{ops::Deref, time::Duration}; +use std::{ops::Deref as _, time::Duration}; use anyhow::Context as _; use borsh::BorshSerialize; From 4076de05c09b1c9803a9deba978792a3b3c1b3d8 Mon Sep 17 00:00:00 2001 From: erhant Date: Thu, 11 Jun 2026 12:44:16 +0300 Subject: [PATCH 6/6] rename `execute_unchecked_on_state` to `execute_without_system_accounts_check_on_state` --- lez/common/src/transaction.rs | 4 ++-- lez/indexer/core/src/block_store.rs | 2 +- lez/storage/src/indexer/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lez/common/src/transaction.rs b/lez/common/src/transaction.rs index 19b25b6d..7e7c46c4 100644 --- a/lez/common/src/transaction.rs +++ b/lez/common/src/transaction.rs @@ -94,7 +94,7 @@ impl LeeTransaction { /// Computes the validated state diff without enforcing the system-account /// restriction. Shared by [`Self::validate_on_state`] and - /// [`Self::execute_unchecked_on_state`]. + /// [`Self::execute_without_system_accounts_check_on_state`]. fn compute_state_diff( &self, state: &V03State, @@ -138,7 +138,7 @@ impl LeeTransaction { /// so the indexer cannot yet distinguish deposit txs from user txs. /// /// REMOVE ME when the indexer can authenticate deposit transactions. - pub fn execute_unchecked_on_state( + pub fn execute_without_system_accounts_check_on_state( self, state: &mut V03State, block_id: BlockId, diff --git a/lez/indexer/core/src/block_store.rs b/lez/indexer/core/src/block_store.rs index 810cfa1b..2ca90377 100644 --- a/lez/indexer/core/src/block_store.rs +++ b/lez/indexer/core/src/block_store.rs @@ -174,7 +174,7 @@ impl IndexerStore { // FIXME: HOT FIX (testnet v0.2): does not check for system account updates due to // sequencer-generated deposit tx'es; // CHANGE ME back to `execute_check_on_state` when the indexer can authenticate deposit transactions - .execute_unchecked_on_state( + .execute_without_system_accounts_check_on_state( &mut state_guard, block.header.block_id, block.header.timestamp, diff --git a/lez/storage/src/indexer/mod.rs b/lez/storage/src/indexer/mod.rs index 008958ed..b682a4d7 100644 --- a/lez/storage/src/indexer/mod.rs +++ b/lez/storage/src/indexer/mod.rs @@ -211,7 +211,7 @@ impl RocksDBIO { // FIXME: HOT FIX (testnet v0.2): does not check for system account updates due to // sequencer-generated deposit tx'es; // CHANGE ME back to `execute_check_on_state` when the indexer can authenticate deposit transactions - .execute_unchecked_on_state( + .execute_without_system_accounts_check_on_state( &mut breakpoint, block.header.block_id, block.header.timestamp,