Groundwork for dyn traits

This commit is contained in:
Omar Abdulla
2025-09-17 05:47:13 +03:00
parent 49cbc51546
commit 9fc74aeea0
9 changed files with 665 additions and 212 deletions
Generated
+1
View File
@@ -4584,6 +4584,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"alloy", "alloy",
"anyhow", "anyhow",
"revive-dt-format",
] ]
[[package]] [[package]]
+31
View File
@@ -564,3 +564,34 @@ pub enum TestingPlatform {
/// The kitchensink runtime provides the PolkaVM (PVM) based node implementation. /// The kitchensink runtime provides the PolkaVM (PVM) based node implementation.
Kitchensink, Kitchensink,
} }
/// An enum of the platform identifiers of all of the platforms supported by this framework.
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
ValueEnum,
EnumString,
Display,
AsRefStr,
IntoStaticStr,
)]
#[strum(serialize_all = "kebab-case")]
pub enum PlatformIdentifier {
/// The Go-ethereum reference full node EVM implementation.
GethEvm,
/// The kitchensink node with the PolkaVM backend.
KitchensinkPolkaVM,
/// The kitchensink node with the REVM backend.
KitchensinkREVM,
/// The revive dev node with the PolkaVM backend.
ReviveDevNodePolkaVM,
/// The revive dev node with the REVM backend.
ReviveDevNodeREVM,
}
+11 -1
View File
@@ -4,7 +4,7 @@
//! provides a helper utility to execute tests. //! provides a helper utility to execute tests.
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc}; use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
use revive_dt_config::TestingPlatform; use revive_dt_config::{PlatformIdentifier, TestingPlatform};
use revive_dt_format::traits::ResolverApi; use revive_dt_format::traits::ResolverApi;
use revive_dt_node::{Node, geth, kitchensink::KitchensinkNode}; use revive_dt_node::{Node, geth, kitchensink::KitchensinkNode};
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
@@ -45,3 +45,13 @@ impl Platform for Kitchensink {
&TestingPlatform::Kitchensink &TestingPlatform::Kitchensink
} }
} }
/// A trait that describes the interface for the platforms that are supported by the tool.
pub trait DynPlatform {
/// Returns the identifier of this platform.
fn platform_identifier(&self) -> PlatformIdentifier;
/// Creates a new node for the platform by spawning a new thread, creating the node object,
/// initializing it, spawning it, and waiting for it to start up.
fn new_node(&self) -> Box<dyn PlatformNode>;
}
+43 -20
View File
@@ -695,7 +695,7 @@ impl<T: AsRef<str>> CalldataToken<T> {
context context
.transaction_hash() .transaction_hash()
.context("No transaction hash provided to get the transaction gas price") .context("No transaction hash provided to get the transaction gas price")
.map(|tx_hash| resolver.transaction_gas_price(tx_hash))? .map(|tx_hash| resolver.transaction_gas_price(*tx_hash))?
.await .await
.map(U256::from) .map(U256::from)
} else if item == Self::GAS_LIMIT_VARIABLE { } else if item == Self::GAS_LIMIT_VARIABLE {
@@ -799,7 +799,7 @@ mod tests {
use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi}; use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi};
use alloy_primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address}; use alloy_primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address};
use alloy_sol_types::SolValue; use alloy_sol_types::SolValue;
use std::collections::HashMap; use std::{collections::HashMap, pin::Pin};
use super::*; use super::*;
use crate::metadata::ContractIdent; use crate::metadata::ContractIdent;
@@ -807,40 +807,63 @@ mod tests {
struct MockResolver; struct MockResolver;
impl ResolverApi for MockResolver { impl ResolverApi for MockResolver {
async fn chain_id(&self) -> anyhow::Result<ChainId> { fn chain_id(&self) -> Pin<Box<dyn Future<Output = anyhow::Result<ChainId>> + '_>> {
Ok(0x123) Box::pin(async move { Ok(0x123) })
} }
async fn block_gas_limit(&self, _: BlockNumberOrTag) -> anyhow::Result<u128> { fn block_gas_limit(
Ok(0x1234) &self,
_: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move { Ok(0x1234) })
} }
async fn block_coinbase(&self, _: BlockNumberOrTag) -> anyhow::Result<Address> { fn block_coinbase(
Ok(Address::ZERO) &self,
_: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
Box::pin(async move { Ok(Address::ZERO) })
} }
async fn block_difficulty(&self, _: BlockNumberOrTag) -> anyhow::Result<U256> { fn block_difficulty(
Ok(U256::from(0x12345u128)) &self,
_: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
Box::pin(async move { Ok(U256::from(0x12345u128)) })
} }
async fn block_base_fee(&self, _: BlockNumberOrTag) -> anyhow::Result<u64> { fn block_base_fee(
Ok(0x100) &self,
_: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
Box::pin(async move { Ok(0x100) })
} }
async fn block_hash(&self, _: BlockNumberOrTag) -> anyhow::Result<BlockHash> { fn block_hash(
Ok([0xEE; 32].into()) &self,
_: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
Box::pin(async move { Ok([0xEE; 32].into()) })
} }
async fn block_timestamp(&self, _: BlockNumberOrTag) -> anyhow::Result<BlockTimestamp> { fn block_timestamp(
Ok(0x123456) &self,
_: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
Box::pin(async move { Ok(0x123456) })
} }
async fn last_block_number(&self) -> anyhow::Result<BlockNumber> { fn last_block_number(
Ok(0x1234567) &self,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockNumber>> + '_>> {
Box::pin(async move { Ok(0x1234567) })
} }
async fn transaction_gas_price(&self, _: &TxHash) -> anyhow::Result<u128> { fn transaction_gas_price(
Ok(0x200) &self,
_: TxHash,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move { Ok(0x200) })
} }
} }
+28 -9
View File
@@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::pin::Pin;
use alloy::eips::BlockNumberOrTag; use alloy::eips::BlockNumberOrTag;
use alloy::json_abi::JsonAbi; use alloy::json_abi::JsonAbi;
@@ -12,36 +13,54 @@ use crate::metadata::{ContractIdent, ContractInstance};
/// crate implements to go from string calldata and into the bytes calldata. /// crate implements to go from string calldata and into the bytes calldata.
pub trait ResolverApi { pub trait ResolverApi {
/// Returns the ID of the chain that the node is on. /// Returns the ID of the chain that the node is on.
fn chain_id(&self) -> impl Future<Output = Result<ChainId>>; fn chain_id(&self) -> Pin<Box<dyn Future<Output = Result<ChainId>> + '_>>;
/// Returns the gas price for the specified transaction. /// Returns the gas price for the specified transaction.
fn transaction_gas_price(&self, tx_hash: &TxHash) -> impl Future<Output = Result<u128>>; fn transaction_gas_price(
&self,
tx_hash: TxHash,
) -> Pin<Box<dyn Future<Output = Result<u128>> + '_>>;
// TODO: This is currently a u128 due to Kitchensink needing more than 64 bits for its gas limit // TODO: This is currently a u128 due to Kitchensink needing more than 64 bits for its gas limit
// when we implement the changes to the gas we need to adjust this to be a u64. // when we implement the changes to the gas we need to adjust this to be a u64.
/// Returns the gas limit of the specified block. /// Returns the gas limit of the specified block.
fn block_gas_limit(&self, number: BlockNumberOrTag) -> impl Future<Output = Result<u128>>; fn block_gas_limit(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = Result<u128>> + '_>>;
/// Returns the coinbase of the specified block. /// Returns the coinbase of the specified block.
fn block_coinbase(&self, number: BlockNumberOrTag) -> impl Future<Output = Result<Address>>; fn block_coinbase(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = Result<Address>> + '_>>;
/// Returns the difficulty of the specified block. /// Returns the difficulty of the specified block.
fn block_difficulty(&self, number: BlockNumberOrTag) -> impl Future<Output = Result<U256>>; fn block_difficulty(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = Result<U256>> + '_>>;
/// Returns the base fee of the specified block. /// Returns the base fee of the specified block.
fn block_base_fee(&self, number: BlockNumberOrTag) -> impl Future<Output = Result<u64>>; fn block_base_fee(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = Result<u64>> + '_>>;
/// Returns the hash of the specified block. /// Returns the hash of the specified block.
fn block_hash(&self, number: BlockNumberOrTag) -> impl Future<Output = Result<BlockHash>>; fn block_hash(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = Result<BlockHash>> + '_>>;
/// Returns the timestamp of the specified block, /// Returns the timestamp of the specified block,
fn block_timestamp( fn block_timestamp(
&self, &self,
number: BlockNumberOrTag, number: BlockNumberOrTag,
) -> impl Future<Output = Result<BlockTimestamp>>; ) -> Pin<Box<dyn Future<Output = Result<BlockTimestamp>> + '_>>;
/// Returns the number of the last block. /// Returns the number of the last block.
fn last_block_number(&self) -> impl Future<Output = Result<BlockNumber>>; fn last_block_number(&self) -> Pin<Box<dyn Future<Output = Result<BlockNumber>> + '_>>;
} }
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
+2
View File
@@ -9,6 +9,8 @@ repository.workspace = true
rust-version.workspace = true rust-version.workspace = true
[dependencies] [dependencies]
revive-dt-format = { workspace = true }
alloy = { workspace = true } alloy = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
+4
View File
@@ -4,6 +4,7 @@ use alloy::primitives::{Address, StorageKey, U256};
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace}; use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace};
use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest}; use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest};
use anyhow::Result; use anyhow::Result;
use revive_dt_format::traits::ResolverApi;
/// An interface for all interactions with Ethereum compatible nodes. /// An interface for all interactions with Ethereum compatible nodes.
pub trait EthereumNode { pub trait EthereumNode {
@@ -32,4 +33,7 @@ pub trait EthereumNode {
address: Address, address: Address,
keys: Vec<StorageKey>, keys: Vec<StorageKey>,
) -> impl Future<Output = Result<EIP1186AccountProofResponse>>; ) -> impl Future<Output = Result<EIP1186AccountProofResponse>>;
/// Returns the resolver that is to use with this ethereum node.
fn resolver(&self) -> impl Future<Output = Result<Box<dyn ResolverApi + '_>>>;
} }
+190 -10
View File
@@ -5,6 +5,7 @@ use std::{
io::{BufRead, BufReader, Read, Write}, io::{BufRead, BufReader, Read, Write},
ops::ControlFlow, ops::ControlFlow,
path::PathBuf, path::PathBuf,
pin::Pin,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
sync::{ sync::{
Arc, Arc,
@@ -438,32 +439,183 @@ impl EthereumNode for GethNode {
.await .await
.map_err(Into::into) .map_err(Into::into)
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn resolver(&self) -> impl Future<Output = anyhow::Result<Box<dyn ResolverApi + '_>>> {
Box::pin(async move {
let id = self.id;
let provider = self.provider().await?;
Ok(Box::new(GethNodeResolver { id, provider }) as Box<dyn ResolverApi>)
})
}
} }
pub struct GethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
id: u32,
provider: FillProvider<F, P, Ethereum>,
}
impl<F: TxFiller<Ethereum>, P: Provider<Ethereum>> ResolverApi for GethNodeResolver<F, P> {
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn chain_id(
&self,
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::primitives::ChainId>> + '_>> {
Box::pin(async move { self.provider.get_chain_id().await.map_err(Into::into) })
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn transaction_gas_price(
&self,
tx_hash: TxHash,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider
.get_transaction_receipt(tx_hash)
.await?
.context("Failed to get the transaction receipt")
.map(|receipt| receipt.effective_gas_price)
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn block_gas_limit(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.gas_limit as _)
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn block_coinbase(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.beneficiary)
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn block_difficulty(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| U256::from_be_bytes(block.header.mix_hash.0))
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn block_base_fee(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.and_then(|block| {
block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
})
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn block_hash(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.hash)
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn block_timestamp(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.timestamp)
})
}
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
fn last_block_number(&self) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockNumber>> + '_>> {
Box::pin(async move { self.provider.get_block_number().await.map_err(Into::into) })
}
}
// TODO: Remove
impl ResolverApi for GethNode { impl ResolverApi for GethNode {
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> { fn chain_id(
&self,
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::primitives::ChainId>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
.get_chain_id() .get_chain_id()
.await .await
.map_err(Into::into) .map_err(Into::into)
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn transaction_gas_price(&self, tx_hash: &TxHash) -> anyhow::Result<u128> { fn transaction_gas_price(
&self,
tx_hash: TxHash,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
.get_transaction_receipt(*tx_hash) .get_transaction_receipt(tx_hash)
.await? .await?
.context("Failed to get the transaction receipt") .context("Failed to get the transaction receipt")
.map(|receipt| receipt.effective_gas_price) .map(|receipt| receipt.effective_gas_price)
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn block_gas_limit(&self, number: BlockNumberOrTag) -> anyhow::Result<u128> { fn block_gas_limit(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
@@ -472,10 +624,15 @@ impl ResolverApi for GethNode {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.gas_limit as _) .map(|block| block.header.gas_limit as _)
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn block_coinbase(&self, number: BlockNumberOrTag) -> anyhow::Result<Address> { fn block_coinbase(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
@@ -484,10 +641,15 @@ impl ResolverApi for GethNode {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.beneficiary) .map(|block| block.header.beneficiary)
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn block_difficulty(&self, number: BlockNumberOrTag) -> anyhow::Result<U256> { fn block_difficulty(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
@@ -496,10 +658,15 @@ impl ResolverApi for GethNode {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| U256::from_be_bytes(block.header.mix_hash.0)) .map(|block| U256::from_be_bytes(block.header.mix_hash.0))
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn block_base_fee(&self, number: BlockNumberOrTag) -> anyhow::Result<u64> { fn block_base_fee(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
@@ -513,10 +680,15 @@ impl ResolverApi for GethNode {
.base_fee_per_gas .base_fee_per_gas
.context("Failed to get the base fee per gas") .context("Failed to get the base fee per gas")
}) })
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn block_hash(&self, number: BlockNumberOrTag) -> anyhow::Result<BlockHash> { fn block_hash(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
@@ -525,10 +697,15 @@ impl ResolverApi for GethNode {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.hash) .map(|block| block.header.hash)
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn block_timestamp(&self, number: BlockNumberOrTag) -> anyhow::Result<BlockTimestamp> { fn block_timestamp(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
@@ -537,16 +714,19 @@ impl ResolverApi for GethNode {
.context("Failed to get the geth block")? .context("Failed to get the geth block")?
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth block, perhaps there are no blocks?")
.map(|block| block.header.timestamp) .map(|block| block.header.timestamp)
})
} }
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
async fn last_block_number(&self) -> anyhow::Result<BlockNumber> { fn last_block_number(&self) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockNumber>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Geth provider")? .context("Failed to get the Geth provider")?
.get_block_number() .get_block_number()
.await .await
.map_err(Into::into) .map_err(Into::into)
})
} }
} }
+213 -30
View File
@@ -2,6 +2,7 @@ use std::{
fs::{File, OpenOptions, create_dir_all, remove_dir_all}, fs::{File, OpenOptions, create_dir_all, remove_dir_all},
io::{BufRead, Write}, io::{BufRead, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
pin::Pin,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
sync::{ sync::{
Arc, Arc,
@@ -44,6 +45,7 @@ use sp_runtime::AccountId32;
use revive_dt_config::*; use revive_dt_config::*;
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use tracing::instrument;
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE}; use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
@@ -387,14 +389,14 @@ impl KitchensinkNode {
&self, &self,
) -> anyhow::Result< ) -> anyhow::Result<
FillProvider< FillProvider<
impl TxFiller<KitchenSinkNetwork>, impl TxFiller<KitchensinkNetwork>,
impl Provider<KitchenSinkNetwork>, impl Provider<KitchensinkNetwork>,
KitchenSinkNetwork, KitchensinkNetwork,
>, >,
> { > {
ProviderBuilder::new() ProviderBuilder::new()
.disable_recommended_fillers() .disable_recommended_fillers()
.network::<KitchenSinkNetwork>() .network::<KitchensinkNetwork>()
.filler(FallbackGasFiller::new( .filler(FallbackGasFiller::new(
25_000_000, 25_000_000,
1_000_000_000, 1_000_000_000,
@@ -479,29 +481,182 @@ impl EthereumNode for KitchensinkNode {
.await .await
.map_err(Into::into) .map_err(Into::into)
} }
fn resolver(&self) -> impl Future<Output = anyhow::Result<Box<dyn ResolverApi + '_>>> {
Box::pin(async move {
let id = self.id;
let provider = self.provider().await?;
Ok(Box::new(KitchensinkNodeResolver { id, provider }) as Box<dyn ResolverApi>)
})
}
} }
pub struct KitchensinkNodeResolver<F: TxFiller<KitchensinkNetwork>, P: Provider<KitchensinkNetwork>>
{
id: u32,
provider: FillProvider<F, P, KitchensinkNetwork>,
}
impl<F: TxFiller<KitchensinkNetwork>, P: Provider<KitchensinkNetwork>> ResolverApi
for KitchensinkNodeResolver<F, P>
{
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn chain_id(
&self,
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::primitives::ChainId>> + '_>> {
Box::pin(async move { self.provider.get_chain_id().await.map_err(Into::into) })
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn transaction_gas_price(
&self,
tx_hash: TxHash,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider
.get_transaction_receipt(tx_hash)
.await?
.context("Failed to get the transaction receipt")
.map(|receipt| receipt.effective_gas_price)
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn block_gas_limit(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.gas_limit as _)
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn block_coinbase(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.beneficiary)
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn block_difficulty(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| U256::from_be_bytes(block.header.mix_hash.0))
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn block_base_fee(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.and_then(|block| {
block
.header
.base_fee_per_gas
.context("Failed to get the base fee per gas")
})
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn block_hash(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.hash)
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn block_timestamp(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
Box::pin(async move {
self.provider
.get_block_by_number(number)
.await
.context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.timestamp)
})
}
#[instrument(level = "info", skip_all, fields(kitchensink_node_id = self.id))]
fn last_block_number(&self) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockNumber>> + '_>> {
Box::pin(async move { self.provider.get_block_number().await.map_err(Into::into) })
}
}
// TODO: Remove
impl ResolverApi for KitchensinkNode { impl ResolverApi for KitchensinkNode {
async fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> { fn chain_id(
&self,
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::primitives::ChainId>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
.get_chain_id() .get_chain_id()
.await .await
.map_err(Into::into) .map_err(Into::into)
})
} }
async fn transaction_gas_price(&self, tx_hash: &TxHash) -> anyhow::Result<u128> { fn transaction_gas_price(
&self,
tx_hash: TxHash,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
.get_transaction_receipt(*tx_hash) .get_transaction_receipt(tx_hash)
.await? .await?
.context("Failed to get the transaction receipt") .context("Failed to get the transaction receipt")
.map(|receipt| receipt.effective_gas_price) .map(|receipt| receipt.effective_gas_price)
})
} }
async fn block_gas_limit(&self, number: BlockNumberOrTag) -> anyhow::Result<u128> { fn block_gas_limit(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
@@ -510,9 +665,14 @@ impl ResolverApi for KitchensinkNode {
.context("Failed to get the kitchensink block")? .context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.gas_limit as _) .map(|block| block.header.gas_limit as _)
})
} }
async fn block_coinbase(&self, number: BlockNumberOrTag) -> anyhow::Result<Address> { fn block_coinbase(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
@@ -521,9 +681,14 @@ impl ResolverApi for KitchensinkNode {
.context("Failed to get the kitchensink block")? .context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.beneficiary) .map(|block| block.header.beneficiary)
})
} }
async fn block_difficulty(&self, number: BlockNumberOrTag) -> anyhow::Result<U256> { fn block_difficulty(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
@@ -532,9 +697,14 @@ impl ResolverApi for KitchensinkNode {
.context("Failed to get the kitchensink block")? .context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| U256::from_be_bytes(block.header.mix_hash.0)) .map(|block| U256::from_be_bytes(block.header.mix_hash.0))
})
} }
async fn block_base_fee(&self, number: BlockNumberOrTag) -> anyhow::Result<u64> { fn block_base_fee(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
@@ -548,9 +718,14 @@ impl ResolverApi for KitchensinkNode {
.base_fee_per_gas .base_fee_per_gas
.context("Failed to get the base fee per gas") .context("Failed to get the base fee per gas")
}) })
})
} }
async fn block_hash(&self, number: BlockNumberOrTag) -> anyhow::Result<BlockHash> { fn block_hash(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
@@ -559,9 +734,14 @@ impl ResolverApi for KitchensinkNode {
.context("Failed to get the kitchensink block")? .context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.hash) .map(|block| block.header.hash)
})
} }
async fn block_timestamp(&self, number: BlockNumberOrTag) -> anyhow::Result<BlockTimestamp> { fn block_timestamp(
&self,
number: BlockNumberOrTag,
) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
@@ -570,15 +750,18 @@ impl ResolverApi for KitchensinkNode {
.context("Failed to get the kitchensink block")? .context("Failed to get the kitchensink block")?
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.map(|block| block.header.timestamp) .map(|block| block.header.timestamp)
})
} }
async fn last_block_number(&self) -> anyhow::Result<BlockNumber> { fn last_block_number(&self) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockNumber>> + '_>> {
Box::pin(async move {
self.provider() self.provider()
.await .await
.context("Failed to get the Kitchensink provider")? .context("Failed to get the Kitchensink provider")?
.get_block_number() .get_block_number()
.await .await
.map_err(Into::into) .map_err(Into::into)
})
} }
} }
@@ -701,9 +884,9 @@ impl Drop for KitchensinkNode {
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct KitchenSinkNetwork; pub struct KitchensinkNetwork;
impl Network for KitchenSinkNetwork { impl Network for KitchensinkNetwork {
type TxType = <Ethereum as Network>::TxType; type TxType = <Ethereum as Network>::TxType;
type TxEnvelope = <Ethereum as Network>::TxEnvelope; type TxEnvelope = <Ethereum as Network>::TxEnvelope;
@@ -712,7 +895,7 @@ impl Network for KitchenSinkNetwork {
type ReceiptEnvelope = <Ethereum as Network>::ReceiptEnvelope; type ReceiptEnvelope = <Ethereum as Network>::ReceiptEnvelope;
type Header = KitchenSinkHeader; type Header = KitchensinkHeader;
type TransactionRequest = <Ethereum as Network>::TransactionRequest; type TransactionRequest = <Ethereum as Network>::TransactionRequest;
@@ -720,12 +903,12 @@ impl Network for KitchenSinkNetwork {
type ReceiptResponse = <Ethereum as Network>::ReceiptResponse; type ReceiptResponse = <Ethereum as Network>::ReceiptResponse;
type HeaderResponse = Header<KitchenSinkHeader>; type HeaderResponse = Header<KitchensinkHeader>;
type BlockResponse = Block<Transaction<TxEnvelope>, Header<KitchenSinkHeader>>; type BlockResponse = Block<Transaction<TxEnvelope>, Header<KitchensinkHeader>>;
} }
impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::TransactionRequest { impl TransactionBuilder<KitchensinkNetwork> for <Ethereum as Network>::TransactionRequest {
fn chain_id(&self) -> Option<alloy::primitives::ChainId> { fn chain_id(&self) -> Option<alloy::primitives::ChainId> {
<<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::chain_id(self) <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::chain_id(self)
} }
@@ -857,7 +1040,7 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
fn complete_type( fn complete_type(
&self, &self,
ty: <KitchenSinkNetwork as Network>::TxType, ty: <KitchensinkNetwork as Network>::TxType,
) -> Result<(), Vec<&'static str>> { ) -> Result<(), Vec<&'static str>> {
<<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::complete_type( <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::complete_type(
self, ty, self, ty,
@@ -874,13 +1057,13 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
<<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::can_build(self) <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::can_build(self)
} }
fn output_tx_type(&self) -> <KitchenSinkNetwork as Network>::TxType { fn output_tx_type(&self) -> <KitchensinkNetwork as Network>::TxType {
<<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::output_tx_type( <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::output_tx_type(
self, self,
) )
} }
fn output_tx_type_checked(&self) -> Option<<KitchenSinkNetwork as Network>::TxType> { fn output_tx_type_checked(&self) -> Option<<KitchensinkNetwork as Network>::TxType> {
<<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::output_tx_type_checked( <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::output_tx_type_checked(
self, self,
) )
@@ -894,7 +1077,7 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
fn build_unsigned( fn build_unsigned(
self, self,
) -> alloy::network::BuildResult<<KitchenSinkNetwork as Network>::UnsignedTx, KitchenSinkNetwork> ) -> alloy::network::BuildResult<<KitchensinkNetwork as Network>::UnsignedTx, KitchensinkNetwork>
{ {
let result = <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::build_unsigned( let result = <<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::build_unsigned(
self, self,
@@ -902,7 +1085,7 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
match result { match result {
Ok(unsigned_tx) => Ok(unsigned_tx), Ok(unsigned_tx) => Ok(unsigned_tx),
Err(UnbuiltTransactionError { request, error }) => { Err(UnbuiltTransactionError { request, error }) => {
Err(UnbuiltTransactionError::<KitchenSinkNetwork> { Err(UnbuiltTransactionError::<KitchensinkNetwork> {
request, request,
error: match error { error: match error {
TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => { TransactionBuilderError::InvalidTransactionRequest(tx_type, items) => {
@@ -923,12 +1106,12 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
} }
} }
async fn build<W: alloy::network::NetworkWallet<KitchenSinkNetwork>>( async fn build<W: alloy::network::NetworkWallet<KitchensinkNetwork>>(
self, self,
wallet: &W, wallet: &W,
) -> Result< ) -> Result<
<KitchenSinkNetwork as Network>::TxEnvelope, <KitchensinkNetwork as Network>::TxEnvelope,
TransactionBuilderError<KitchenSinkNetwork>, TransactionBuilderError<KitchensinkNetwork>,
> { > {
Ok(wallet.sign_request(self).await?) Ok(wallet.sign_request(self).await?)
} }
@@ -936,7 +1119,7 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct KitchenSinkHeader { pub struct KitchensinkHeader {
/// The Keccak 256-bit hash of the parent /// The Keccak 256-bit hash of the parent
/// blocks header, in its entirety; formally Hp. /// blocks header, in its entirety; formally Hp.
pub parent_hash: B256, pub parent_hash: B256,
@@ -1039,7 +1222,7 @@ pub struct KitchenSinkHeader {
pub requests_hash: Option<B256>, pub requests_hash: Option<B256>,
} }
impl BlockHeader for KitchenSinkHeader { impl BlockHeader for KitchensinkHeader {
fn parent_hash(&self) -> B256 { fn parent_hash(&self) -> B256 {
self.parent_hash self.parent_hash
} }