From 1d5ec90ea55409a692c6138b554dd842a4f992b0 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 19:07:32 +0800 Subject: [PATCH 1/5] refactor: add network-primitives --- Cargo.toml | 1 + crates/contract/Cargo.toml | 1 + crates/contract/src/call.rs | 3 +- crates/network-primitives/Cargo.toml | 28 ++ crates/network-primitives/README.md | 3 + crates/network-primitives/src/block.rs | 244 +++++++++++++ crates/network-primitives/src/lib.rs | 13 + crates/network-primitives/src/traits.rs | 104 ++++++ crates/network/Cargo.toml | 1 + crates/network/src/any/mod.rs | 48 +-- crates/network/src/ethereum/mod.rs | 48 +-- crates/network/src/lib.rs | 60 +--- crates/provider/Cargo.toml | 1 + crates/provider/src/heart.rs | 4 +- crates/provider/src/provider/trait.rs | 7 +- crates/rpc-types-eth/Cargo.toml | 2 + crates/rpc-types-eth/src/block.rs | 323 +----------------- crates/rpc-types-eth/src/transaction/mod.rs | 26 ++ .../rpc-types-eth/src/transaction/receipt.rs | 21 +- 19 files changed, 458 insertions(+), 480 deletions(-) create mode 100644 crates/network-primitives/Cargo.toml create mode 100644 crates/network-primitives/README.md create mode 100644 crates/network-primitives/src/block.rs create mode 100644 crates/network-primitives/src/lib.rs create mode 100644 crates/network-primitives/src/traits.rs diff --git a/Cargo.toml b/Cargo.toml index 9f17dd07bd8..c0930b65589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ alloy-eip7547 = { version = "0.2", path = "crates/eip7547", default-features = f alloy-genesis = { version = "0.2", path = "crates/genesis", default-features = false } alloy-json-rpc = { version = "0.2", path = "crates/json-rpc", default-features = false } alloy-network = { version = "0.2", path = "crates/network", default-features = false } +alloy-network-primitives = { version = "0.2", path = "crates/network-primitives", default-features = false } alloy-node-bindings = { version = "0.2", path = "crates/node-bindings", default-features = false } alloy-provider = { version = "0.2", path = "crates/provider", default-features = false } alloy-pubsub = { version = "0.2", path = "crates/pubsub", default-features = false } diff --git a/crates/contract/Cargo.toml b/crates/contract/Cargo.toml index 67610f3688a..b1862a2b99e 100644 --- a/crates/contract/Cargo.toml +++ b/crates/contract/Cargo.toml @@ -20,6 +20,7 @@ workspace = true [dependencies] alloy-network.workspace = true +alloy-network-primitives.workspace = true alloy-provider.workspace = true alloy-rpc-types-eth.workspace = true alloy-transport.workspace = true diff --git a/crates/contract/src/call.rs b/crates/contract/src/call.rs index 77ba97815a2..d9aaf5279ce 100644 --- a/crates/contract/src/call.rs +++ b/crates/contract/src/call.rs @@ -1,7 +1,8 @@ use crate::{CallDecoder, Error, EthCall, Result}; use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; use alloy_json_abi::Function; -use alloy_network::{Ethereum, Network, ReceiptResponse, TransactionBuilder}; +use alloy_network::{Ethereum, Network, TransactionBuilder}; +use alloy_network_primitives::ReceiptResponse; use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256}; use alloy_provider::{PendingTransactionBuilder, Provider}; use alloy_rpc_types_eth::{state::StateOverride, AccessList, BlobTransactionSidecar, BlockId}; diff --git a/crates/network-primitives/Cargo.toml b/crates/network-primitives/Cargo.toml new file mode 100644 index 00000000000..a56fb3e6239 --- /dev/null +++ b/crates/network-primitives/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "alloy-network-primitives" +description = "Primitive types for Alloy network abstraction" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +alloy-primitives.workspace = true +alloy-serde.workspace = true + +serde.workspace = true + +[dev-dependencies] +rand.workspace = true diff --git a/crates/network-primitives/README.md b/crates/network-primitives/README.md new file mode 100644 index 00000000000..d5ef0b8d631 --- /dev/null +++ b/crates/network-primitives/README.md @@ -0,0 +1,3 @@ +# alloy-network-primitives + +Primitive types for Alloy network abstraction. \ No newline at end of file diff --git a/crates/network-primitives/src/block.rs b/crates/network-primitives/src/block.rs new file mode 100644 index 00000000000..6cf27e1c97b --- /dev/null +++ b/crates/network-primitives/src/block.rs @@ -0,0 +1,244 @@ +use alloy_primitives::B256; +use serde::{Deserialize, Serialize}; + +use crate::TransactionResponse; + +/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`, +/// or if used by `eth_getUncle*` +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum BlockTransactions { + /// Full transactions + Full(Vec), + /// Only hashes + Hashes(Vec), + /// Special case for uncle response. + Uncle, +} + +impl Default for BlockTransactions { + fn default() -> Self { + Self::Hashes(Vec::default()) + } +} + +impl BlockTransactions { + /// Check if the enum variant is used for hashes. + #[inline] + pub const fn is_hashes(&self) -> bool { + matches!(self, Self::Hashes(_)) + } + + /// Fallibly cast to a slice of hashes. + pub fn as_hashes(&self) -> Option<&[B256]> { + match self { + Self::Hashes(hashes) => Some(hashes), + _ => None, + } + } + + /// Returns true if the enum variant is used for full transactions. + #[inline] + pub const fn is_full(&self) -> bool { + matches!(self, Self::Full(_)) + } + + /// Fallibly cast to a slice of transactions. + /// + /// Returns `None` if the enum variant is not `Full`. + pub fn as_transactions(&self) -> Option<&[T]> { + match self { + Self::Full(txs) => Some(txs), + _ => None, + } + } + + /// Returns true if the enum variant is used for an uncle response. + #[inline] + pub const fn is_uncle(&self) -> bool { + matches!(self, Self::Uncle) + } + + /// Returns an iterator over the transactions (if any). This will be empty + /// if the block is an uncle or if the transaction list contains only + /// hashes. + #[doc(alias = "transactions")] + pub fn txns(&self) -> impl Iterator { + self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter()) + } + + /// Returns an iterator over the transactions (if any). This will be empty if the block is not + /// full. + pub fn into_transactions(self) -> std::vec::IntoIter { + match self { + Self::Full(txs) => txs.into_iter(), + _ => std::vec::IntoIter::default(), + } + } + + /// Returns an instance of BlockTransactions with the Uncle special case. + #[inline] + pub const fn uncle() -> Self { + Self::Uncle + } + + /// Returns the number of transactions. + #[inline] + pub fn len(&self) -> usize { + match self { + Self::Hashes(h) => h.len(), + Self::Full(f) => f.len(), + Self::Uncle => 0, + } + } + + /// Whether the block has no transactions. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl BlockTransactions { + /// Converts `self` into `Hashes`. + #[inline] + pub fn convert_to_hashes(&mut self) { + if !self.is_hashes() { + *self = Self::Hashes(self.hashes().collect()); + } + } + + /// Converts `self` into `Hashes`. + #[inline] + pub fn into_hashes(mut self) -> Self { + self.convert_to_hashes(); + self + } + + /// Returns an iterator over the transaction hashes. + #[deprecated = "use `hashes` instead"] + #[inline] + pub fn iter(&self) -> BlockTransactionHashes<'_, T> { + self.hashes() + } + + /// Returns an iterator over references to the transaction hashes. + #[inline] + pub fn hashes(&self) -> BlockTransactionHashes<'_, T> { + BlockTransactionHashes::new(self) + } +} + +impl From> for BlockTransactions { + fn from(hashes: Vec) -> Self { + Self::Hashes(hashes) + } +} + +impl From> for BlockTransactions { + fn from(transactions: Vec) -> Self { + Self::Full(transactions) + } +} + +/// An iterator over the transaction hashes of a block. +/// +/// See [`BlockTransactions::hashes`]. +#[derive(Clone, Debug)] +pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>); + +#[derive(Clone, Debug)] +enum BlockTransactionHashesInner<'a, T> { + Hashes(std::slice::Iter<'a, B256>), + Full(std::slice::Iter<'a, T>), + Uncle, +} + +impl<'a, T> BlockTransactionHashes<'a, T> { + #[inline] + fn new(txs: &'a BlockTransactions) -> Self { + Self(match txs { + BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()), + BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()), + BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle, + }) + } +} + +impl<'a, T: TransactionResponse> Iterator for BlockTransactionHashes<'a, T> { + type Item = B256; + + #[inline] + fn next(&mut self) -> Option { + match &mut self.0 { + BlockTransactionHashesInner::Hashes(txs) => txs.next().copied(), + BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| tx.tx_hash()), + BlockTransactionHashesInner::Uncle => None, + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + match &self.0 { + BlockTransactionHashesInner::Full(txs) => txs.size_hint(), + BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(), + BlockTransactionHashesInner::Uncle => (0, Some(0)), + } + } +} + +impl ExactSizeIterator for BlockTransactionHashes<'_, T> { + #[inline] + fn len(&self) -> usize { + match &self.0 { + BlockTransactionHashesInner::Full(txs) => txs.len(), + BlockTransactionHashesInner::Hashes(txs) => txs.len(), + BlockTransactionHashesInner::Uncle => 0, + } + } +} + +impl DoubleEndedIterator for BlockTransactionHashes<'_, T> { + #[inline] + fn next_back(&mut self) -> Option { + match &mut self.0 { + BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| tx.tx_hash()), + BlockTransactionHashesInner::Hashes(txs) => txs.next_back().copied(), + BlockTransactionHashesInner::Uncle => None, + } + } +} + +impl<'a, T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'a, T> {} + +/// Determines how the `transactions` field of [Block] should be filled. +/// +/// This essentially represents the `full:bool` argument in RPC calls that determine whether the +/// response should include full transaction objects or just the hashes. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum BlockTransactionsKind { + /// Only include hashes: [BlockTransactions::Hashes] + #[default] + Hashes, + /// Include full transaction objects: [BlockTransactions::Full] + Full, +} + +impl From for BlockTransactionsKind { + fn from(is_full: bool) -> Self { + if is_full { + Self::Full + } else { + Self::Hashes + } + } +} + +impl From for bool { + fn from(kind: BlockTransactionsKind) -> Self { + match kind { + BlockTransactionsKind::Full => true, + BlockTransactionsKind::Hashes => false, + } + } +} diff --git a/crates/network-primitives/src/lib.rs b/crates/network-primitives/src/lib.rs new file mode 100644 index 00000000000..42f43f134ef --- /dev/null +++ b/crates/network-primitives/src/lib.rs @@ -0,0 +1,13 @@ +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", + html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod traits; +pub use traits::{ReceiptResponse, TransactionResponse}; + +mod block; +pub use block::{BlockTransactionHashes, BlockTransactions, BlockTransactionsKind}; diff --git a/crates/network-primitives/src/traits.rs b/crates/network-primitives/src/traits.rs new file mode 100644 index 00000000000..35a5ec0cfab --- /dev/null +++ b/crates/network-primitives/src/traits.rs @@ -0,0 +1,104 @@ +use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256}; +use alloy_serde::WithOtherFields; + +/// A receipt response. +/// +/// This is distinct from [`TxReceipt`], since this is for JSON-RPC receipts. +/// +/// [`TxReceipt`]: alloy_consensus::TxReceipt +pub trait ReceiptResponse { + /// Address of the created contract, or `None` if the transaction was not a deployment. + fn contract_address(&self) -> Option
; + + /// Status of the transaction. + /// + /// ## Note + /// + /// Caution must be taken when using this method for deep-historical + /// receipts, as it may not accurately reflect the status of the + /// transaction. The transaction status is not knowable from the receipt + /// for transactions before [EIP-658]. + /// + /// This can be handled using [`TxReceipt::status_or_post_state`]. + /// + /// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658 + /// [`TxReceipt::status_or_post_state`]: alloy_consensus::TxReceipt::status_or_post_state + fn status(&self) -> bool; + + /// Hash of the block this transaction was included within. + fn block_hash(&self) -> Option; + + /// Number of the block this transaction was included within. + fn block_number(&self) -> Option; +} + +/// Transaction Response +/// +/// This is distinct from [`Transaction`], since this is a JSON-RPC response. +/// +/// [`Transaction`]: alloy_consensus::Transaction +pub trait TransactionResponse { + /// Hash of the transaction + #[doc(alias = "transaction_hash")] + fn tx_hash(&self) -> TxHash; + + /// Sender of the transaction + fn from(&self) -> Address; + + /// Recipient of the transaction + fn to(&self) -> Option
; + + /// Transferred value + fn value(&self) -> U256; + + /// Gas limit + fn gas(&self) -> u128; + + /// Input data + #[doc(alias = "calldata")] + fn input(&self) -> &Bytes; +} + +impl TransactionResponse for WithOtherFields { + fn tx_hash(&self) -> TxHash { + self.inner.tx_hash() + } + + fn from(&self) -> Address { + self.inner.from() + } + + fn to(&self) -> Option
{ + self.inner.to() + } + + fn value(&self) -> U256 { + self.inner.value() + } + + fn gas(&self) -> u128 { + self.inner.gas() + } + + fn input(&self) -> &Bytes { + self.inner.input() + } +} + +impl ReceiptResponse for WithOtherFields { + fn contract_address(&self) -> Option
{ + self.inner.contract_address() + } + + fn status(&self) -> bool { + self.inner.status() + } + + fn block_hash(&self) -> Option { + self.inner.block_hash() + } + + fn block_number(&self) -> Option { + self.inner.block_number() + } +} diff --git a/crates/network/Cargo.toml b/crates/network/Cargo.toml index c84e8f6a706..9eda78c2426 100644 --- a/crates/network/Cargo.toml +++ b/crates/network/Cargo.toml @@ -22,6 +22,7 @@ workspace = true alloy-consensus = { workspace = true, features = ["std"] } alloy-eips = { workspace = true, features = ["serde"] } alloy-json-rpc.workspace = true +alloy-network-primitives.workspace = true alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-signer.workspace = true diff --git a/crates/network/src/any/mod.rs b/crates/network/src/any/mod.rs index 2acb0a1a5a1..8103b562ca2 100644 --- a/crates/network/src/any/mod.rs +++ b/crates/network/src/any/mod.rs @@ -1,7 +1,6 @@ -use crate::{Network, ReceiptResponse, TransactionResponse}; +use crate::Network; use alloy_consensus::TxType; use alloy_eips::eip2718::Eip2718Error; -use alloy_primitives::Bytes; use alloy_rpc_types_eth::{AnyTransactionReceipt, Header, Transaction, TransactionRequest}; use alloy_serde::WithOtherFields; use core::fmt; @@ -76,48 +75,3 @@ impl Network for AnyNetwork { type HeaderResponse = WithOtherFields
; } - -impl ReceiptResponse for AnyTransactionReceipt { - fn contract_address(&self) -> Option { - self.contract_address - } - - fn status(&self) -> bool { - self.inner.inner.status() - } - - fn block_hash(&self) -> Option { - self.inner.block_hash - } - - fn block_number(&self) -> Option { - self.inner.block_number - } -} - -impl TransactionResponse for WithOtherFields { - #[doc(alias = "transaction_hash")] - fn tx_hash(&self) -> alloy_primitives::B256 { - self.hash - } - - fn from(&self) -> alloy_primitives::Address { - self.from - } - - fn to(&self) -> Option { - self.to - } - - fn value(&self) -> alloy_primitives::U256 { - self.value - } - - fn gas(&self) -> u128 { - self.gas - } - - fn input(&self) -> &Bytes { - &self.input - } -} diff --git a/crates/network/src/ethereum/mod.rs b/crates/network/src/ethereum/mod.rs index 68729e3f0f9..0d2266874e4 100644 --- a/crates/network/src/ethereum/mod.rs +++ b/crates/network/src/ethereum/mod.rs @@ -1,5 +1,4 @@ -use crate::{Network, ReceiptResponse, TransactionResponse}; -use alloy_primitives::Bytes; +use crate::Network; mod builder; @@ -31,48 +30,3 @@ impl Network for Ethereum { type HeaderResponse = alloy_rpc_types_eth::Header; } - -impl ReceiptResponse for alloy_rpc_types_eth::TransactionReceipt { - fn contract_address(&self) -> Option { - self.contract_address - } - - fn status(&self) -> bool { - self.inner.status() - } - - fn block_hash(&self) -> Option { - self.block_hash - } - - fn block_number(&self) -> Option { - self.block_number - } -} - -impl TransactionResponse for alloy_rpc_types_eth::Transaction { - #[doc(alias = "transaction_hash")] - fn tx_hash(&self) -> alloy_primitives::B256 { - self.hash - } - - fn from(&self) -> alloy_primitives::Address { - self.from - } - - fn to(&self) -> Option { - self.to - } - - fn value(&self) -> alloy_primitives::U256 { - self.value - } - - fn gas(&self) -> u128 { - self.gas - } - - fn input(&self) -> &Bytes { - &self.input - } -} diff --git a/crates/network/src/lib.rs b/crates/network/src/lib.rs index 54431385f32..794cefb1a7b 100644 --- a/crates/network/src/lib.rs +++ b/crates/network/src/lib.rs @@ -9,7 +9,7 @@ use alloy_consensus::TxReceipt; use alloy_eips::eip2718::{Eip2718Envelope, Eip2718Error}; use alloy_json_rpc::RpcObject; -use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256}; +use alloy_network_primitives::{ReceiptResponse, TransactionResponse}; use core::fmt::{Debug, Display}; mod transaction; @@ -26,64 +26,6 @@ pub use any::{AnyNetwork, AnyTxType}; pub use alloy_eips::eip2718; -/// A receipt response. -/// -/// This is distinct from [`TxReceipt`], since this is for JSON-RPC receipts. -/// -/// [`TxReceipt`]: alloy_consensus::TxReceipt -pub trait ReceiptResponse { - /// Address of the created contract, or `None` if the transaction was not a deployment. - fn contract_address(&self) -> Option
; - - /// Status of the transaction. - /// - /// ## Note - /// - /// Caution must be taken when using this method for deep-historical - /// receipts, as it may not accurately reflect the status of the - /// transaction. The transaction status is not knowable from the receipt - /// for transactions before [EIP-658]. - /// - /// This can be handled using [`TxReceipt::status_or_post_state`]. - /// - /// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658 - /// [`TxReceipt::status_or_post_state`]: alloy_consensus::TxReceipt::status_or_post_state - fn status(&self) -> bool; - - /// Hash of the block this transaction was included within. - fn block_hash(&self) -> Option; - - /// Number of the block this transaction was included within. - fn block_number(&self) -> Option; -} - -/// Transaction Response -/// -/// This is distinct from [`Transaction`], since this is a JSON-RPC response. -/// -/// [`Transaction`]: alloy_consensus::Transaction -pub trait TransactionResponse { - /// Hash of the transaction - #[doc(alias = "transaction_hash")] - fn tx_hash(&self) -> TxHash; - - /// Sender of the transaction - fn from(&self) -> Address; - - /// Recipient of the transaction - fn to(&self) -> Option
; - - /// Transferred value - fn value(&self) -> U256; - - /// Gas limit - fn gas(&self) -> u128; - - /// Input data - #[doc(alias = "calldata")] - fn input(&self) -> &Bytes; -} - /// Captures type info for network-specific RPC requests/responses. /// /// Networks are only containers for types, so it is recommended to use ZSTs for their definition. diff --git a/crates/provider/Cargo.toml b/crates/provider/Cargo.toml index 3fda009b291..d7b692c83e2 100644 --- a/crates/provider/Cargo.toml +++ b/crates/provider/Cargo.toml @@ -23,6 +23,7 @@ alloy-eips.workspace = true alloy-consensus.workspace = true alloy-json-rpc.workspace = true alloy-network.workspace = true +alloy-network-primitives.workspace = true alloy-node-bindings = { workspace = true, optional = true } alloy-signer-local = { workspace = true, optional = true } alloy-rpc-client.workspace = true diff --git a/crates/provider/src/heart.rs b/crates/provider/src/heart.rs index db7d4b7af0c..58de345c6a2 100644 --- a/crates/provider/src/heart.rs +++ b/crates/provider/src/heart.rs @@ -555,13 +555,13 @@ impl Heartbeat { self.past_blocks.retain(|(h, _)| h < block_height); } } - self.past_blocks.push_back((*block_height, block.transactions.hashes().copied().collect())); + self.past_blocks.push_back((*block_height, block.transactions.hashes().collect())); // Check if we are watching for any of the transactions in this block. let to_check: Vec<_> = block .transactions .hashes() - .filter_map(|tx_hash| self.unconfirmed.remove(tx_hash)) + .filter_map(|tx_hash| self.unconfirmed.remove(&tx_hash)) .collect(); for mut watcher in to_check { // If `confirmations` is not more than 1 we can notify the watcher immediately. diff --git a/crates/provider/src/provider/trait.rs b/crates/provider/src/provider/trait.rs index 7d4cc1f889e..6af73c6744b 100644 --- a/crates/provider/src/provider/trait.rs +++ b/crates/provider/src/provider/trait.rs @@ -7,15 +7,16 @@ use crate::{ }; use alloy_eips::eip2718::Encodable2718; use alloy_json_rpc::{RpcError, RpcParam, RpcReturn}; -use alloy_network::{Ethereum, Network, ReceiptResponse as _}; +use alloy_network::{Ethereum, Network}; +use alloy_network_primitives::{BlockTransactionsKind, ReceiptResponse}; use alloy_primitives::{ hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U128, U256, U64, }; use alloy_rpc_client::{ClientRef, PollerBuilder, RpcCall, WeakClient}; use alloy_rpc_types_eth::{ - AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, BlockTransactionsKind, - EIP1186AccountProofResponse, FeeHistory, Filter, FilterChanges, Log, SyncStatus, + AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, EIP1186AccountProofResponse, + FeeHistory, Filter, FilterChanges, Log, SyncStatus, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; use serde_json::value::RawValue; diff --git a/crates/rpc-types-eth/Cargo.toml b/crates/rpc-types-eth/Cargo.toml index e1c998b73e0..60d2bc135ab 100644 --- a/crates/rpc-types-eth/Cargo.toml +++ b/crates/rpc-types-eth/Cargo.toml @@ -26,6 +26,8 @@ alloy-serde.workspace = true alloy-consensus = { workspace = true, features = ["std", "serde"] } alloy-eips = { workspace = true, features = ["std", "serde"] } +alloy-network-primitives.workspace = true + itertools.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/crates/rpc-types-eth/src/block.rs b/crates/rpc-types-eth/src/block.rs index 3a549f55c82..70fbf292b98 100644 --- a/crates/rpc-types-eth/src/block.rs +++ b/crates/rpc-types-eth/src/block.rs @@ -1,6 +1,7 @@ //! Block RPC types. use crate::{ConversionError, Transaction, Withdrawal}; +use alloy_network_primitives::BlockTransactions; use alloy_primitives::{Address, BlockHash, Bloom, Bytes, B256, B64, U256}; use alloy_serde::OtherFields; use serde::{ser::Error, Deserialize, Serialize, Serializer}; @@ -14,7 +15,7 @@ pub use alloy_eips::{ /// Block representation #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct Block { +pub struct Block { /// Header of the block. #[serde(flatten)] pub header: Header, @@ -27,7 +28,7 @@ pub struct Block { default = "BlockTransactions::uncle", skip_serializing_if = "BlockTransactions::is_uncle" )] - pub transactions: BlockTransactions, + pub transactions: BlockTransactions, /// Integer the size of this block in bytes. #[serde(default, skip_serializing_if = "Option::is_none")] pub size: Option, @@ -204,324 +205,6 @@ impl TryFrom
for alloy_consensus::Header { } } -/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`, -/// or if used by `eth_getUncle*` -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum BlockTransactions { - /// Full transactions - Full(Vec), - /// Only hashes - Hashes(Vec), - /// Special case for uncle response. - Uncle, -} - -impl Default for BlockTransactions { - fn default() -> Self { - Self::Hashes(Vec::default()) - } -} - -impl BlockTransactions { - /// Check if the enum variant is used for hashes. - #[inline] - pub const fn is_hashes(&self) -> bool { - matches!(self, Self::Hashes(_)) - } - - /// Fallibly cast to a slice of hashes. - pub fn as_hashes(&self) -> Option<&[B256]> { - match self { - Self::Hashes(hashes) => Some(hashes), - _ => None, - } - } - - /// Returns true if the enum variant is used for full transactions. - #[inline] - pub const fn is_full(&self) -> bool { - matches!(self, Self::Full(_)) - } - - /// Fallibly cast to a slice of transactions. - /// - /// Returns `None` if the enum variant is not `Full`. - pub fn as_transactions(&self) -> Option<&[T]> { - match self { - Self::Full(txs) => Some(txs), - _ => None, - } - } - - /// Returns true if the enum variant is used for an uncle response. - #[inline] - pub const fn is_uncle(&self) -> bool { - matches!(self, Self::Uncle) - } - - /// Returns an iterator over the transactions (if any). This will be empty - /// if the block is an uncle or if the transaction list contains only - /// hashes. - #[doc(alias = "transactions")] - pub fn txns(&self) -> impl Iterator { - self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter()) - } - - /// Returns an iterator over the transactions (if any). This will be empty if the block is not - /// full. - pub fn into_transactions(self) -> std::vec::IntoIter { - match self { - Self::Full(txs) => txs.into_iter(), - _ => std::vec::IntoIter::default(), - } - } - - /// Returns an instance of BlockTransactions with the Uncle special case. - #[inline] - pub const fn uncle() -> Self { - Self::Uncle - } - - /// Returns the number of transactions. - #[inline] - pub fn len(&self) -> usize { - match self { - Self::Hashes(h) => h.len(), - Self::Full(f) => f.len(), - Self::Uncle => 0, - } - } - - /// Whether the block has no transactions. - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl BlockTransactions { - /// Converts `self` into `Hashes`. - #[inline] - pub fn convert_to_hashes(&mut self) { - if !self.is_hashes() { - *self = Self::Hashes(self.hashes().copied().collect()); - } - } - - /// Converts `self` into `Hashes`. - #[inline] - pub fn into_hashes(mut self) -> Self { - self.convert_to_hashes(); - self - } - - /// Returns an iterator over the transaction hashes. - #[deprecated = "use `hashes` instead"] - #[inline] - pub fn iter(&self) -> BlockTransactionHashes<'_, Transaction> { - self.hashes() - } - - /// Returns an iterator over references to the transaction hashes. - #[inline] - pub fn hashes(&self) -> BlockTransactionHashes<'_, Transaction> { - BlockTransactionHashes::new(self) - } - - /// Returns an iterator over mutable references to the transaction hashes. - #[inline] - pub fn hashes_mut(&mut self) -> BlockTransactionHashesMut<'_, Transaction> { - BlockTransactionHashesMut::new(self) - } -} - -impl From> for BlockTransactions { - fn from(hashes: Vec) -> Self { - Self::Hashes(hashes) - } -} - -impl From> for BlockTransactions { - fn from(transactions: Vec) -> Self { - Self::Full(transactions) - } -} - -/// An iterator over the transaction hashes of a block. -/// -/// See [`BlockTransactions::hashes`]. -#[derive(Clone, Debug)] -pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>); - -#[derive(Clone, Debug)] -enum BlockTransactionHashesInner<'a, T = Transaction> { - Hashes(std::slice::Iter<'a, B256>), - Full(std::slice::Iter<'a, T>), - Uncle, -} - -impl<'a, T> BlockTransactionHashes<'a, T> { - #[inline] - fn new(txs: &'a BlockTransactions) -> Self { - Self(match txs { - BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()), - BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()), - BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle, - }) - } -} - -impl<'a> Iterator for BlockTransactionHashes<'a, Transaction> { - type Item = &'a B256; - - #[inline] - fn next(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInner::Hashes(txs) => txs.next(), - BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| &tx.hash), - BlockTransactionHashesInner::Uncle => None, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match &self.0 { - BlockTransactionHashesInner::Full(txs) => txs.size_hint(), - BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(), - BlockTransactionHashesInner::Uncle => (0, Some(0)), - } - } -} - -impl ExactSizeIterator for BlockTransactionHashes<'_, Transaction> { - #[inline] - fn len(&self) -> usize { - match &self.0 { - BlockTransactionHashesInner::Full(txs) => txs.len(), - BlockTransactionHashesInner::Hashes(txs) => txs.len(), - BlockTransactionHashesInner::Uncle => 0, - } - } -} - -impl DoubleEndedIterator for BlockTransactionHashes<'_, Transaction> { - #[inline] - fn next_back(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| &tx.hash), - BlockTransactionHashesInner::Hashes(txs) => txs.next_back(), - BlockTransactionHashesInner::Uncle => None, - } - } -} - -impl<'a> std::iter::FusedIterator for BlockTransactionHashes<'a, Transaction> {} - -/// An Iterator over the transaction hashes of a block. -/// -/// See [`BlockTransactions::hashes_mut`]. -#[derive(Debug)] -pub struct BlockTransactionHashesMut<'a, T = Transaction>(BlockTransactionHashesInnerMut<'a, T>); - -#[derive(Debug)] -enum BlockTransactionHashesInnerMut<'a, T = Transaction> { - Hashes(std::slice::IterMut<'a, B256>), - Full(std::slice::IterMut<'a, T>), - Uncle, -} - -impl<'a, T> BlockTransactionHashesMut<'a, T> { - #[inline] - fn new(txs: &'a mut BlockTransactions) -> Self { - Self(match txs { - BlockTransactions::Hashes(txs) => { - BlockTransactionHashesInnerMut::Hashes(txs.iter_mut()) - } - BlockTransactions::Full(txs) => BlockTransactionHashesInnerMut::Full(txs.iter_mut()), - BlockTransactions::Uncle => BlockTransactionHashesInnerMut::Uncle, - }) - } -} - -impl<'a> Iterator for BlockTransactionHashesMut<'a, Transaction> { - type Item = &'a mut B256; - - #[inline] - fn next(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.next().map(|tx| &mut tx.hash), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.next(), - BlockTransactionHashesInnerMut::Uncle => None, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match &self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.size_hint(), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.size_hint(), - BlockTransactionHashesInnerMut::Uncle => (0, Some(0)), - } - } -} - -impl ExactSizeIterator for BlockTransactionHashesMut<'_, Transaction> { - #[inline] - fn len(&self) -> usize { - match &self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.len(), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.len(), - BlockTransactionHashesInnerMut::Uncle => 0, - } - } -} - -impl DoubleEndedIterator for BlockTransactionHashesMut<'_, Transaction> { - #[inline] - fn next_back(&mut self) -> Option { - match &mut self.0 { - BlockTransactionHashesInnerMut::Full(txs) => txs.next_back().map(|tx| &mut tx.hash), - BlockTransactionHashesInnerMut::Hashes(txs) => txs.next_back(), - BlockTransactionHashesInnerMut::Uncle => None, - } - } -} - -impl<'a> std::iter::FusedIterator for BlockTransactionHashesMut<'a, Transaction> {} - -/// Determines how the `transactions` field of [Block] should be filled. -/// -/// This essentially represents the `full:bool` argument in RPC calls that determine whether the -/// response should include full transaction objects or just the hashes. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -pub enum BlockTransactionsKind { - /// Only include hashes: [BlockTransactions::Hashes] - #[default] - Hashes, - /// Include full transaction objects: [BlockTransactions::Full] - Full, -} - -impl From for BlockTransactionsKind { - fn from(is_full: bool) -> Self { - if is_full { - Self::Full - } else { - Self::Hashes - } - } -} - -impl From for bool { - fn from(kind: BlockTransactionsKind) -> Self { - match kind { - BlockTransactionsKind::Full => true, - BlockTransactionsKind::Hashes => false, - } - } -} - /// Error that can occur when converting other types to blocks #[derive(Clone, Copy, Debug, thiserror::Error)] pub enum BlockError { diff --git a/crates/rpc-types-eth/src/transaction/mod.rs b/crates/rpc-types-eth/src/transaction/mod.rs index 4d567e790d2..40f00c9f78d 100644 --- a/crates/rpc-types-eth/src/transaction/mod.rs +++ b/crates/rpc-types-eth/src/transaction/mod.rs @@ -5,6 +5,7 @@ use alloy_consensus::{ TxLegacy, TxType, }; use alloy_eips::eip7702::SignedAuthorization; +use alloy_network_primitives::TransactionResponse; use alloy_primitives::{Address, BlockHash, Bytes, ChainId, TxHash, TxKind, B256, U256}; use alloy_serde::OtherFields; use serde::{Deserialize, Serialize}; @@ -276,6 +277,31 @@ impl TryFrom for TxEnvelope { } } +impl TransactionResponse for Transaction { + fn tx_hash(&self) -> B256 { + self.hash + } + + fn from(&self) -> Address { + self.from + } + + fn to(&self) -> Option
{ + self.to + } + + fn value(&self) -> U256 { + self.value + } + + fn gas(&self) -> u128 { + self.gas + } + + fn input(&self) -> &Bytes { + &self.input + } +} #[cfg(test)] mod tests { use super::*; diff --git a/crates/rpc-types-eth/src/transaction/receipt.rs b/crates/rpc-types-eth/src/transaction/receipt.rs index 8d49e1488dc..a95e5dce66b 100644 --- a/crates/rpc-types-eth/src/transaction/receipt.rs +++ b/crates/rpc-types-eth/src/transaction/receipt.rs @@ -1,6 +1,7 @@ use crate::Log; -use alloy_consensus::{AnyReceiptEnvelope, ReceiptEnvelope, TxType}; +use alloy_consensus::{AnyReceiptEnvelope, ReceiptEnvelope, TxReceipt, TxType}; use alloy_eips::eip7702::SignedAuthorization; +use alloy_network_primitives::ReceiptResponse; use alloy_primitives::{Address, BlockHash, TxHash, B256}; use alloy_serde::WithOtherFields; use serde::{Deserialize, Serialize}; @@ -128,6 +129,24 @@ impl TransactionReceipt { #[doc(alias = "AnyTxReceipt")] pub type AnyTransactionReceipt = WithOtherFields>>; +impl> ReceiptResponse for TransactionReceipt { + fn contract_address(&self) -> Option { + self.contract_address + } + + fn status(&self) -> bool { + self.inner.status() + } + + fn block_hash(&self) -> Option { + self.block_hash + } + + fn block_number(&self) -> Option { + self.block_number + } +} + #[cfg(test)] mod test { use super::*; From 1bfda963a6eb2996bf5531772e051c52245c48c7 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 19:09:48 +0800 Subject: [PATCH 2/5] fix tests --- crates/network-primitives/src/block.rs | 14 ++++++++++++++ crates/rpc-types-eth/src/block.rs | 9 --------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/network-primitives/src/block.rs b/crates/network-primitives/src/block.rs index 6cf27e1c97b..88a0775c73c 100644 --- a/crates/network-primitives/src/block.rs +++ b/crates/network-primitives/src/block.rs @@ -242,3 +242,17 @@ impl From for bool { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_full_conversion() { + let full = true; + assert_eq!(BlockTransactionsKind::Full, full.into()); + + let full = false; + assert_eq!(BlockTransactionsKind::Hashes, full.into()); + } +} diff --git a/crates/rpc-types-eth/src/block.rs b/crates/rpc-types-eth/src/block.rs index 70fbf292b98..2f4ff183de1 100644 --- a/crates/rpc-types-eth/src/block.rs +++ b/crates/rpc-types-eth/src/block.rs @@ -331,15 +331,6 @@ mod tests { let _: Header = Header::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); } - #[test] - fn test_full_conversion() { - let full = true; - assert_eq!(BlockTransactionsKind::Full, full.into()); - - let full = false; - assert_eq!(BlockTransactionsKind::Hashes, full.into()); - } - #[test] #[cfg(feature = "jsonrpsee-types")] fn serde_json_header() { From 5bffebf704a63b212ac97da1ab3eccef84029b36 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 19:25:05 +0800 Subject: [PATCH 3/5] fix docs --- crates/network-primitives/src/block.rs | 2 +- crates/network-primitives/src/traits.rs | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/crates/network-primitives/src/block.rs b/crates/network-primitives/src/block.rs index 88a0775c73c..1ec52f338ba 100644 --- a/crates/network-primitives/src/block.rs +++ b/crates/network-primitives/src/block.rs @@ -211,7 +211,7 @@ impl DoubleEndedIterator for BlockTransactionHashes<'_, impl<'a, T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'a, T> {} -/// Determines how the `transactions` field of [Block] should be filled. +/// Determines how the `transactions` field of block should be filled. /// /// This essentially represents the `full:bool` argument in RPC calls that determine whether the /// response should include full transaction objects or just the hashes. diff --git a/crates/network-primitives/src/traits.rs b/crates/network-primitives/src/traits.rs index 35a5ec0cfab..9580f19b584 100644 --- a/crates/network-primitives/src/traits.rs +++ b/crates/network-primitives/src/traits.rs @@ -1,11 +1,7 @@ use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256}; use alloy_serde::WithOtherFields; -/// A receipt response. -/// -/// This is distinct from [`TxReceipt`], since this is for JSON-RPC receipts. -/// -/// [`TxReceipt`]: alloy_consensus::TxReceipt +/// Receipt JSON-RPC response. pub trait ReceiptResponse { /// Address of the created contract, or `None` if the transaction was not a deployment. fn contract_address(&self) -> Option
; @@ -18,11 +14,6 @@ pub trait ReceiptResponse { /// receipts, as it may not accurately reflect the status of the /// transaction. The transaction status is not knowable from the receipt /// for transactions before [EIP-658]. - /// - /// This can be handled using [`TxReceipt::status_or_post_state`]. - /// - /// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658 - /// [`TxReceipt::status_or_post_state`]: alloy_consensus::TxReceipt::status_or_post_state fn status(&self) -> bool; /// Hash of the block this transaction was included within. @@ -32,11 +23,7 @@ pub trait ReceiptResponse { fn block_number(&self) -> Option; } -/// Transaction Response -/// -/// This is distinct from [`Transaction`], since this is a JSON-RPC response. -/// -/// [`Transaction`]: alloy_consensus::Transaction +/// Transaction JSON-RPC response. pub trait TransactionResponse { /// Hash of the transaction #[doc(alias = "transaction_hash")] From 426c26f0ff0e85c7c4ad80ef6887087bacc85b13 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 20:59:53 +0800 Subject: [PATCH 4/5] add re-exports --- crates/network/src/lib.rs | 2 +- crates/rpc-types-eth/src/lib.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/network/src/lib.rs b/crates/network/src/lib.rs index 794cefb1a7b..0719e452c57 100644 --- a/crates/network/src/lib.rs +++ b/crates/network/src/lib.rs @@ -9,7 +9,6 @@ use alloy_consensus::TxReceipt; use alloy_eips::eip2718::{Eip2718Envelope, Eip2718Error}; use alloy_json_rpc::RpcObject; -use alloy_network_primitives::{ReceiptResponse, TransactionResponse}; use core::fmt::{Debug, Display}; mod transaction; @@ -25,6 +24,7 @@ mod any; pub use any::{AnyNetwork, AnyTxType}; pub use alloy_eips::eip2718; +pub use alloy_network_primitives::{ReceiptResponse, TransactionResponse}; /// Captures type info for network-specific RPC requests/responses. /// diff --git a/crates/rpc-types-eth/src/lib.rs b/crates/rpc-types-eth/src/lib.rs index 2219acba233..2f20ca8514b 100644 --- a/crates/rpc-types-eth/src/lib.rs +++ b/crates/rpc-types-eth/src/lib.rs @@ -14,6 +14,10 @@ pub use account::*; mod block; pub use block::*; +pub use alloy_network_primitives::{ + BlockTransactionHashes, BlockTransactions, BlockTransactionsKind, +}; + mod call; pub use call::{Bundle, EthCallResponse, StateContext, TransactionIndex}; From 50fab4299b8f546725a976c15f7db13f06475507 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 25 Jul 2024 21:57:34 +0800 Subject: [PATCH 5/5] Block --- crates/rpc-types-eth/src/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rpc-types-eth/src/block.rs b/crates/rpc-types-eth/src/block.rs index 2f4ff183de1..1622b3c74f0 100644 --- a/crates/rpc-types-eth/src/block.rs +++ b/crates/rpc-types-eth/src/block.rs @@ -15,7 +15,7 @@ pub use alloy_eips::{ /// Block representation #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct Block { +pub struct Block { /// Header of the block. #[serde(flatten)] pub header: Header, @@ -28,7 +28,7 @@ pub struct Block { default = "BlockTransactions::uncle", skip_serializing_if = "BlockTransactions::is_uncle" )] - pub transactions: BlockTransactions, + pub transactions: BlockTransactions, /// Integer the size of this block in bytes. #[serde(default, skip_serializing_if = "Option::is_none")] pub size: Option,