diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb4b7a1..0eeb93f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -99,9 +99,28 @@ jobs: - name: Install Geth on Ubuntu if: matrix.os == 'ubuntu-24.04' run: | - sudo add-apt-repository -y ppa:ethereum/ethereum sudo apt-get update - sudo apt-get install -y ethereum protobuf-compiler + sudo apt-get install -y protobuf-compiler + + # We were facing some issues in CI with the 1.16.* versions of geth, and specifically on + # Ubuntu. Eventually, we found out that the last version of geth that worked in our CI was + # version 1.15.11. Thus, this is the version that we want to use in CI. The PPA sadly does + # not have historic versions of Geth and therefore we need to resort to downloading pre + # built binaries for Geth and the surrounding tools which is what the following parts of + # the script do. + + sudo apt-get install -y wget ca-certificates tar + ARCH=$(uname -m) + if [ "$ARCH" = "x86_64" ]; then + URL="https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.15.11-36b2371c.tar.gz" + elif [ "$ARCH" = "aarch64" ]; then + URL="https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-arm64-1.15.11-36b2371c.tar.gz" + else + echo "Unsupported architecture: $ARCH" + exit 1 + fi + wget -qO- "$URL" | sudo tar xz -C /usr/local/bin --strip-components=1 + geth --version - name: Install Geth on macOS if: matrix.os == 'macos-14' diff --git a/Cargo.lock b/Cargo.lock index 0cff4c8..27c2f20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.9" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0093d23bf026b580c1f66ed3a053d8209c104a446c5264d3ad99587f6edef24e" +checksum = "ae58d888221eecf621595e2096836ce7cfc37be06bfa39d7f64aa6a3ea4c9e5b" dependencies = [ "alloy-consensus", "alloy-contract", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "alloy-core" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c5a28f166629752f2e7246b813cdea3243cca59aab2d4264b1fd68392c10eb" +checksum = "ad31216895d27d307369daa1393f5850b50bbbd372478a9fa951c095c210627e" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cc14d832bc3331ca22a1c7819de1ede99f58f61a7d123952af7dde8de124a6" +checksum = "7b95b3deca680efc7e9cba781f1a1db352fa1ea50e6384a514944dcf4419e652" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4003,6 +4003,7 @@ dependencies = [ "sp-core", "sp-runtime", "temp-dir", + "tokio", "tracing", ] diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index e72e8f6..7f1c92e 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1,4 +1,4 @@ -//! The global configuration used accross all revive differential testing crates. +//! The global configuration used across all revive differential testing crates. use std::{ fmt::Display, diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 1b54fc9..47b6d58 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true anyhow = { workspace = true } alloy = { workspace = true } tracing = { workspace = true } +tokio = { workspace = true } revive-dt-node-interaction = { workspace = true } revive-dt-config = { workspace = true } diff --git a/crates/node/src/geth.rs b/crates/node/src/geth.rs index c40b18f..066b376 100644 --- a/crates/node/src/geth.rs +++ b/crates/node/src/geth.rs @@ -159,17 +159,85 @@ impl EthereumNode for Instance { let connection_string = self.connection_string(); let wallet = self.wallet.clone(); - tracing::debug!("Submitting transaction: {transaction:#?}"); - execute_transaction(Box::pin(async move { - Ok(ProviderBuilder::new() + let outer_span = tracing::debug_span!("Submitting transaction", ?transaction,); + let _outer_guard = outer_span.enter(); + + let provider = ProviderBuilder::new() .wallet(wallet) .connect(&connection_string) - .await? - .send_transaction(transaction) - .await? - .get_receipt() - .await?) + .await?; + + let pending_transaction = provider.send_transaction(transaction).await?; + let transaction_hash = pending_transaction.tx_hash(); + + let span = tracing::info_span!("Awaiting transaction receipt", ?transaction_hash); + let _guard = span.enter(); + + // The following is a fix for the "transaction indexing is in progress" error that we + // used to get. You can find more information on this in the following GH issue in geth + // https://github.com/ethereum/go-ethereum/issues/28877. To summarize what's going on, + // before we can get the receipt of the transaction it needs to have been indexed by the + // node's indexer. Just because the transaction has been confirmed it doesn't mean that + // it has been indexed. When we call alloy's `get_receipt` it checks if the transaction + // was confirmed. If it has been, then it will call `eth_getTransactionReceipt` method + // which _might_ return the above error if the tx has not yet been indexed yet. So, we + // need to implement a retry mechanism for the receipt to keep retrying to get it until + // it eventually works, but we only do that if the error we get back is the "transaction + // indexing is in progress" error or if the receipt is None. + // + // At the moment we do not allow for the 60 seconds to be modified and we take it as + // being an implementation detail that's invisible to anything outside of this module. + // + // We allow a total of 60 retries for getting the receipt with one second between each + // retry and the next which means that we allow for a total of 60 seconds of waiting + // before we consider that we're unable to get the transaction receipt. + let mut retries = 0; + loop { + match provider.get_transaction_receipt(*transaction_hash).await { + Ok(Some(receipt)) => { + tracing::info!("Obtained the transaction receipt"); + break Ok(receipt); + } + Ok(None) => { + if retries == 60 { + tracing::error!( + "Polled for transaction receipt for 60 seconds but failed to get it" + ); + break Err(anyhow::anyhow!("Failed to get the transaction receipt")); + } else { + tracing::trace!( + retries, + "Sleeping for 1 second and trying to get the receipt again" + ); + retries += 1; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + continue; + } + } + Err(error) => { + let error_string = error.to_string(); + if error_string.contains("transaction indexing is in progress") { + if retries == 60 { + tracing::error!( + "Polled for transaction receipt for 60 seconds but failed to get it" + ); + break Err(error.into()); + } else { + tracing::trace!( + retries, + "Sleeping for 1 second and trying to get the receipt again" + ); + retries += 1; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + continue; + } + } else { + break Err(error.into()); + } + } + } + } })) } @@ -270,6 +338,7 @@ impl Node for Instance { impl Drop for Instance { fn drop(&mut self) { + tracing::info!(id = self.id, "Dropping node"); if let Some(child) = self.handle.as_mut() { let _ = child.kill(); }