diff --git a/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign.rs b/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign.rs index dd5b80d77e8..31fc0b5fdbd 100644 --- a/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign.rs +++ b/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign.rs @@ -15,14 +15,17 @@ impl NonceManagerState { old_nonce: &Option, ) -> NonceResult { if let Some(nonce) = old_nonce { - // If the different nonce was assigned to the transaction, - // we clear the tracked nonce for the transaction first. - warn!( - ?nonce, - "Reassigning nonce to transaction, clearing currently tracked nonce" - ); - self.clear_tracked_tx_uuid(nonce).await?; - self.clear_tracked_tx_nonce(tx_uuid).await?; + // Only clear nonce and tx_uuid linkage if the old_nonce is indeed associated with the tx_uuid in question + if *tx_uuid == self.get_tracked_tx_uuid(nonce).await? { + // If the different nonce was assigned to the transaction, + // we clear the tracked nonce for the transaction first. + warn!( + ?nonce, + "Reassigning nonce to transaction, clearing currently tracked nonce" + ); + self.clear_tracked_tx_uuid(nonce).await?; + self.clear_tracked_tx_nonce(tx_uuid).await?; + } } let (finalized_nonce, upper_nonce) = self.get_boundary_nonces().await?; diff --git a/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign/tests/tests_assign.rs b/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign/tests/tests_assign.rs index 37d00552892..c3569ae34d0 100644 --- a/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign/tests/tests_assign.rs +++ b/rust/main/lander/src/adapter/chains/ethereum/nonce/state/assign/tests/tests_assign.rs @@ -15,6 +15,40 @@ fn create_tx(uuid: TransactionUuid, status: TransactionStatus) -> Transaction { make_tx(uuid, status, None, None) } +#[tokio::test] +async fn test_assign_next_nonce_and_keep_old_linkage() { + let (_, tx_db, nonce_db) = tmp_dbs(); + let address = Address::random(); + let metrics = EthereumAdapterMetrics::dummy_instance(); + let state = Arc::new(NonceManagerState::new(nonce_db, tx_db, address, metrics)); + + let other_tx_uuid = TransactionUuid::random(); + + // Assign nonce and build linkage for other tx + let nonce = state + .assign_next_nonce(&other_tx_uuid, &None) + .await + .unwrap(); + state + .set_tracked_tx_uuid(&nonce, &other_tx_uuid) + .await + .unwrap(); + + let tx_uuid = TransactionUuid::random(); + // Now assign a new nonce to a new tx but with an old nonce of the other tx + let new_nonce = state + .assign_next_nonce(&tx_uuid, &Some(nonce)) + .await + .unwrap(); + + let tracked_tx_uuid = state.get_tracked_tx_uuid(&nonce).await.unwrap(); + let tracked_nonce = state.get_tx_nonce(&other_tx_uuid).await.unwrap(); + // Assert that linkage of the old tx id & nonce was not removed + assert_eq!(tracked_tx_uuid, other_tx_uuid); + assert_eq!(tracked_nonce, Some(nonce)); + assert_eq!(nonce + 1, new_nonce); +} + #[tokio::test] async fn test_assign_next_nonce_no_previous_nonce() { let (_, tx_db, nonce_db) = tmp_dbs(); diff --git a/rust/main/lander/src/dispatcher/stages/building_stage/building.rs b/rust/main/lander/src/dispatcher/stages/building_stage/building.rs index 79a78f88d4e..3457df8efea 100644 --- a/rust/main/lander/src/dispatcher/stages/building_stage/building.rs +++ b/rust/main/lander/src/dispatcher/stages/building_stage/building.rs @@ -96,13 +96,15 @@ impl BuildingStage { return Ok(()); }; info!(?tx, "Transaction built successfully"); + self.state.store_tx(&tx).await; + // Only send tx to inclusion stage after we stored it + // This prevents TX from dropping in case the send operation fails call_until_success_or_nonretryable_error( || self.send_tx_to_inclusion_stage(tx.clone()), "Sending transaction to inclusion stage", &self.state, ) .await?; - self.state.store_tx(&tx).await; Ok(()) }