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 + '_>>>;
} }
+261 -81
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,115 +439,294 @@ 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.provider() &self,
.await ) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::primitives::ChainId>> + '_>> {
.context("Failed to get the Geth provider")? Box::pin(async move {
.get_chain_id() self.provider()
.await .await
.map_err(Into::into) .context("Failed to get the Geth provider")?
.get_chain_id()
.await
.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.provider() &self,
.await tx_hash: TxHash,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
.get_transaction_receipt(*tx_hash) Box::pin(async move {
.await? self.provider()
.context("Failed to get the transaction receipt") .await
.map(|receipt| receipt.effective_gas_price) .context("Failed to get the Geth 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))] #[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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the geth block")? .await
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth provider")?
.map(|block| block.header.gas_limit as _) .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))] #[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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the geth block")? .await
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth provider")?
.map(|block| block.header.beneficiary) .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))] #[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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the geth block")? .await
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth provider")?
.map(|block| U256::from_be_bytes(block.header.mix_hash.0)) .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))] #[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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the geth block")? .await
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth provider")?
.and_then(|block| { .get_block_by_number(number)
block .await
.header .context("Failed to get the geth block")?
.base_fee_per_gas .context("Failed to get the Geth block, perhaps there are no blocks?")
.context("Failed to get the base fee per gas") .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))] #[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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the geth block")? .await
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth provider")?
.map(|block| block.header.hash) .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))] #[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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Geth provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the geth block")? .await
.context("Failed to get the Geth block, perhaps there are no blocks?") .context("Failed to get the Geth provider")?
.map(|block| block.header.timestamp) .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))] #[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>> + '_>> {
self.provider() Box::pin(async move {
.await self.provider()
.context("Failed to get the Geth provider")? .await
.get_block_number() .context("Failed to get the Geth provider")?
.await .get_block_number()
.map_err(Into::into) .await
.map_err(Into::into)
})
} }
} }
+284 -101
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,106 +481,287 @@ 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.provider() &self,
.await ) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::primitives::ChainId>> + '_>> {
.context("Failed to get the Kitchensink provider")? Box::pin(async move {
.get_chain_id() self.provider()
.await .await
.map_err(Into::into) .context("Failed to get the Kitchensink provider")?
.get_chain_id()
.await
.map_err(Into::into)
})
} }
async fn transaction_gas_price(&self, tx_hash: &TxHash) -> anyhow::Result<u128> { fn transaction_gas_price(
self.provider() &self,
.await tx_hash: TxHash,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
.get_transaction_receipt(*tx_hash) Box::pin(async move {
.await? self.provider()
.context("Failed to get the transaction receipt") .await
.map(|receipt| receipt.effective_gas_price) .context("Failed to get the Kitchensink provider")?
.get_transaction_receipt(tx_hash)
.await?
.context("Failed to get the transaction receipt")
.map(|receipt| receipt.effective_gas_price)
})
} }
async fn block_gas_limit(&self, number: BlockNumberOrTag) -> anyhow::Result<u128> { fn block_gas_limit(
self.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<u128>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the kitchensink block")? .await
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink provider")?
.map(|block| block.header.gas_limit as _) .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 _)
})
} }
async fn block_coinbase(&self, number: BlockNumberOrTag) -> anyhow::Result<Address> { fn block_coinbase(
self.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<Address>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the kitchensink block")? .await
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink provider")?
.map(|block| block.header.beneficiary) .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)
})
} }
async fn block_difficulty(&self, number: BlockNumberOrTag) -> anyhow::Result<U256> { fn block_difficulty(
self.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<U256>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the kitchensink block")? .await
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink provider")?
.map(|block| U256::from_be_bytes(block.header.mix_hash.0)) .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))
})
} }
async fn block_base_fee(&self, number: BlockNumberOrTag) -> anyhow::Result<u64> { fn block_base_fee(
self.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<u64>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the kitchensink block")? .await
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink provider")?
.and_then(|block| { .get_block_by_number(number)
block .await
.header .context("Failed to get the kitchensink block")?
.base_fee_per_gas .context("Failed to get the Kitchensink block, perhaps the chain has no blocks?")
.context("Failed to get the base fee per gas") .and_then(|block| {
}) block
.header
.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.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockHash>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the kitchensink block")? .await
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink provider")?
.map(|block| block.header.hash) .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)
})
} }
async fn block_timestamp(&self, number: BlockNumberOrTag) -> anyhow::Result<BlockTimestamp> { fn block_timestamp(
self.provider() &self,
.await number: BlockNumberOrTag,
.context("Failed to get the Kitchensink provider")? ) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockTimestamp>> + '_>> {
.get_block_by_number(number) Box::pin(async move {
.await self.provider()
.context("Failed to get the kitchensink block")? .await
.context("Failed to get the Kitchensink block, perhaps the chain has no blocks?") .context("Failed to get the Kitchensink provider")?
.map(|block| block.header.timestamp) .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)
})
} }
async fn last_block_number(&self) -> anyhow::Result<BlockNumber> { fn last_block_number(&self) -> Pin<Box<dyn Future<Output = anyhow::Result<BlockNumber>> + '_>> {
self.provider() Box::pin(async move {
.await self.provider()
.context("Failed to get the Kitchensink provider")? .await
.get_block_number() .context("Failed to get the Kitchensink provider")?
.await .get_block_number()
.map_err(Into::into) .await
.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
} }