Skip to content

Commit 5eb3a0e

Browse files
authored
Fix for "transaction indexing is in progress" (#32)
* Retry getting transaction receipt * Small fix to logging consistency * Introduce a custom kitchensink network * Fix formtting and clippy
1 parent 772bd21 commit 5eb3a0e

File tree

7 files changed

+531
-19
lines changed

7 files changed

+531
-19
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
.DS_Store
44
node_modules
55
/*.json
6+
7+
# We do not want to commit any log files that we produce from running the code locally so this is
8+
# added to the .gitignore file.
9+
*.log

Cargo.lock

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ features = [
6868
"rpc-types",
6969
"signer-local",
7070
"std",
71+
"network",
72+
"serde",
73+
"rpc-types-eth",
7174
]
7275

7376
[profile.bench]

crates/config/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! The global configuration used accross all revive differential testing crates.
1+
//! The global configuration used across all revive differential testing crates.
22
33
use std::{
44
fmt::Display,

crates/node/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ rust-version.workspace = true
1212
anyhow = { workspace = true }
1313
alloy = { workspace = true }
1414
tracing = { workspace = true }
15+
tokio = { workspace = true }
1516

1617
revive-dt-node-interaction = { workspace = true }
1718
revive-dt-config = { workspace = true }
1819

20+
serde = { workspace = true }
1921
serde_json = { workspace = true }
2022

2123
sp-core = { workspace = true }

crates/node/src/geth.rs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,85 @@ impl EthereumNode for Instance {
159159
let connection_string = self.connection_string();
160160
let wallet = self.wallet.clone();
161161

162-
tracing::debug!("Submitting transaction: {transaction:#?}");
163-
164162
execute_transaction(Box::pin(async move {
165-
Ok(ProviderBuilder::new()
163+
let outer_span = tracing::debug_span!("Submitting transaction", ?transaction,);
164+
let _outer_guard = outer_span.enter();
165+
166+
let provider = ProviderBuilder::new()
166167
.wallet(wallet)
167168
.connect(&connection_string)
168-
.await?
169-
.send_transaction(transaction)
170-
.await?
171-
.get_receipt()
172-
.await?)
169+
.await?;
170+
171+
let pending_transaction = provider.send_transaction(transaction).await?;
172+
let transaction_hash = pending_transaction.tx_hash();
173+
174+
let span = tracing::info_span!("Awaiting transaction receipt", ?transaction_hash);
175+
let _guard = span.enter();
176+
177+
// The following is a fix for the "transaction indexing is in progress" error that we
178+
// used to get. You can find more information on this in the following GH issue in geth
179+
// https://github.com/ethereum/go-ethereum/issues/28877. To summarize what's going on,
180+
// before we can get the receipt of the transaction it needs to have been indexed by the
181+
// node's indexer. Just because the transaction has been confirmed it doesn't mean that
182+
// it has been indexed. When we call alloy's `get_receipt` it checks if the transaction
183+
// was confirmed. If it has been, then it will call `eth_getTransactionReceipt` method
184+
// which _might_ return the above error if the tx has not yet been indexed yet. So, we
185+
// need to implement a retry mechanism for the receipt to keep retrying to get it until
186+
// it eventually works, but we only do that if the error we get back is the "transaction
187+
// indexing is in progress" error or if the receipt is None.
188+
//
189+
// At the moment we do not allow for the 60 seconds to be modified and we take it as
190+
// being an implementation detail that's invisible to anything outside of this module.
191+
//
192+
// We allow a total of 60 retries for getting the receipt with one second between each
193+
// retry and the next which means that we allow for a total of 60 seconds of waiting
194+
// before we consider that we're unable to get the transaction receipt.
195+
let mut retries = 0;
196+
loop {
197+
match provider.get_transaction_receipt(*transaction_hash).await {
198+
Ok(Some(receipt)) => {
199+
tracing::info!("Obtained the transaction receipt");
200+
break Ok(receipt);
201+
}
202+
Ok(None) => {
203+
if retries == 60 {
204+
tracing::error!(
205+
"Polled for transaction receipt for 60 seconds but failed to get it"
206+
);
207+
break Err(anyhow::anyhow!("Failed to get the transaction receipt"));
208+
} else {
209+
tracing::trace!(
210+
retries,
211+
"Sleeping for 1 second and trying to get the receipt again"
212+
);
213+
retries += 1;
214+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
215+
continue;
216+
}
217+
}
218+
Err(error) => {
219+
let error_string = error.to_string();
220+
if error_string.contains("transaction indexing is in progress") {
221+
if retries == 60 {
222+
tracing::error!(
223+
"Polled for transaction receipt for 60 seconds but failed to get it"
224+
);
225+
break Err(error.into());
226+
} else {
227+
tracing::trace!(
228+
retries,
229+
"Sleeping for 1 second and trying to get the receipt again"
230+
);
231+
retries += 1;
232+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
233+
continue;
234+
}
235+
} else {
236+
break Err(error.into());
237+
}
238+
}
239+
}
240+
}
173241
}))
174242
}
175243

@@ -270,6 +338,7 @@ impl Node for Instance {
270338

271339
impl Drop for Instance {
272340
fn drop(&mut self) {
341+
tracing::info!(id = self.id, "Dropping node");
273342
if let Some(child) = self.handle.as_mut() {
274343
let _ = child.kill();
275344
}

0 commit comments

Comments
 (0)