Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: network-parameterized block responses #1106

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/network-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

mod traits;
pub use traits::{ReceiptResponse, TransactionResponse};
pub use traits::{BlockResponse, HeaderResponse, ReceiptResponse, TransactionResponse};

mod block;
pub use block::{BlockTransactionHashes, BlockTransactions, BlockTransactionsKind};
45 changes: 45 additions & 0 deletions crates/network-primitives/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256};
use alloy_serde::WithOtherFields;

use crate::BlockTransactions;

/// Receipt JSON-RPC response.
pub trait ReceiptResponse {
/// Address of the created contract, or `None` if the transaction was not a deployment.
Expand Down Expand Up @@ -46,6 +48,32 @@ pub trait TransactionResponse {
fn input(&self) -> &Bytes;
}

/// Header JSON-RPC response.
pub trait HeaderResponse {
/// Base fee per unit of gas (If EIP-1559 is supported)
fn base_fee_per_gas(&self) -> Option<u128>;

/// Blob fee for the next block (if EIP-4844 is supported)
fn next_block_blob_fee(&self) -> Option<u128>;
Comment on lines +62 to +66
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good start, we can add more functions based on the spec:

https://ethereum.github.io/execution-apis/api-documentation/

}

/// Block JSON-RPC response.
pub trait BlockResponse {
/// Header type
type Header;
/// Transaction type
type Transaction;

/// Block header
fn header(&self) -> &Self::Header;

/// Block transactions
fn transactions(&self) -> &BlockTransactions<Self::Transaction>;

/// Mutable reference to block transactions
fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction>;
}

impl<T: TransactionResponse> TransactionResponse for WithOtherFields<T> {
fn tx_hash(&self) -> TxHash {
self.inner.tx_hash()
Expand Down Expand Up @@ -89,3 +117,20 @@ impl<T: ReceiptResponse> ReceiptResponse for WithOtherFields<T> {
self.inner.block_number()
}
}

impl<T: BlockResponse> BlockResponse for WithOtherFields<T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we do the same for HeaderResponse, in case the header is WithOtherFields

type Header = T::Header;
type Transaction = T::Transaction;

fn header(&self) -> &Self::Header {
self.inner.header()
}

fn transactions(&self) -> &BlockTransactions<Self::Transaction> {
self.inner.transactions()
}

fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction> {
self.inner.transactions_mut()
}
}
6 changes: 4 additions & 2 deletions crates/network/src/any/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::Network;
use alloy_consensus::TxType;
use alloy_eips::eip2718::Eip2718Error;
use alloy_rpc_types_eth::{AnyTransactionReceipt, Header, Transaction, TransactionRequest};
use alloy_rpc_types_eth::{AnyTransactionReceipt, Block, Header, Transaction, TransactionRequest};
use alloy_serde::WithOtherFields;
use core::fmt;

Expand Down Expand Up @@ -73,5 +73,7 @@ impl Network for AnyNetwork {

type ReceiptResponse = AnyTransactionReceipt;

type HeaderResponse = WithOtherFields<Header>;
type HeaderResponse = Header;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this remain WithOtherFields<Header> ?

Copy link
Member Author

@klkvr klkvr Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Header is getting flattened into block in RPC, so if block has additional fields those will get consumed by header's others. There's no concept of body/header separation in RPC, so we can't really tell which field belongs to a header or to a body of the block

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, that makes sense


type BlockResponse = WithOtherFields<Block<Self::TransactionResponse>>;
}
2 changes: 2 additions & 0 deletions crates/network/src/ethereum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ impl Network for Ethereum {
type ReceiptResponse = alloy_rpc_types_eth::TransactionReceipt;

type HeaderResponse = alloy_rpc_types_eth::Header;

type BlockResponse = alloy_rpc_types_eth::Block;
}
7 changes: 6 additions & 1 deletion crates/network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use alloy_consensus::TxReceipt;
use alloy_eips::eip2718::{Eip2718Envelope, Eip2718Error};
use alloy_json_rpc::RpcObject;
use alloy_network_primitives::{BlockResponse, HeaderResponse};
use core::fmt::{Debug, Display};

mod transaction;
Expand Down Expand Up @@ -85,5 +86,9 @@ pub trait Network: Debug + Clone + Copy + Sized + Send + Sync + 'static {
type ReceiptResponse: RpcObject + ReceiptResponse;

/// The JSON body of a header response.
type HeaderResponse: RpcObject;
type HeaderResponse: RpcObject + HeaderResponse;

/// The JSON body of a block response.
type BlockResponse: RpcObject
+ BlockResponse<Transaction = Self::TransactionResponse, Header = Self::HeaderResponse>;
}
3 changes: 2 additions & 1 deletion crates/provider/src/fillers/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
};
use alloy_json_rpc::RpcError;
use alloy_network::{Network, TransactionBuilder};
use alloy_network_primitives::{BlockResponse, HeaderResponse};
use alloy_rpc_types_eth::BlockNumberOrTag;
use alloy_transport::{Transport, TransportResult};
use futures::FutureExt;
Expand Down Expand Up @@ -150,7 +151,7 @@ impl GasFiller {
.get_block_by_number(BlockNumberOrTag::Latest, false)
.await?
.ok_or(RpcError::NullResp)?
.header
.header()
.next_block_blob_fee()
.ok_or(RpcError::UnsupportedFeature("eip4844"))
}
Expand Down
34 changes: 19 additions & 15 deletions crates/provider/src/provider/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ use crate::{
use alloy_eips::eip2718::Encodable2718;
use alloy_json_rpc::{RpcError, RpcParam, RpcReturn};
use alloy_network::{Ethereum, Network};
use alloy_network_primitives::{BlockTransactionsKind, ReceiptResponse};
use alloy_network_primitives::{
BlockResponse, BlockTransactionsKind, HeaderResponse, 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, EIP1186AccountProofResponse,
FeeHistory, Filter, FilterChanges, Log, SyncStatus,
AccessListWithGasUsed, BlockId, BlockNumberOrTag, EIP1186AccountProofResponse, FeeHistory,
Filter, FilterChanges, Log, SyncStatus,
};
use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult};
use serde_json::value::RawValue;
Expand Down Expand Up @@ -209,8 +211,8 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
self.get_block_by_number(BlockNumberOrTag::Latest, false)
.await?
.ok_or(RpcError::NullResp)?
.header
.base_fee_per_gas
.header()
.base_fee_per_gas()
.ok_or(RpcError::UnsupportedFeature("eip1559"))?
}
};
Expand Down Expand Up @@ -258,7 +260,7 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
&self,
id: BlockId,
kind: BlockTransactionsKind,
) -> TransportResult<Option<Block>> {
) -> TransportResult<Option<N::BlockResponse>> {
match id {
BlockId::Hash(hash) => self.get_block_by_hash(hash.into(), kind).await,
BlockId::Number(number) => {
Expand All @@ -273,21 +275,21 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
&self,
hash: BlockHash,
kind: BlockTransactionsKind,
) -> TransportResult<Option<Block>> {
) -> TransportResult<Option<N::BlockResponse>> {
let full = match kind {
BlockTransactionsKind::Full => true,
BlockTransactionsKind::Hashes => false,
};

let block = self
.client()
.request::<_, Option<Block>>("eth_getBlockByHash", (hash, full))
.request::<_, Option<N::BlockResponse>>("eth_getBlockByHash", (hash, full))
.await?
.map(|mut block| {
if !full {
// this ensures an empty response for `Hashes` has the expected form
// this is required because deserializing [] is ambiguous
block.transactions.convert_to_hashes();
block.transactions_mut().convert_to_hashes();
}
block
});
Expand All @@ -301,16 +303,16 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
&self,
number: BlockNumberOrTag,
hydrate: bool,
) -> TransportResult<Option<Block>> {
) -> TransportResult<Option<N::BlockResponse>> {
let block = self
.client()
.request::<_, Option<Block>>("eth_getBlockByNumber", (number, hydrate))
.request::<_, Option<N::BlockResponse>>("eth_getBlockByNumber", (number, hydrate))
.await?
.map(|mut block| {
if !hydrate {
// this ensures an empty response for `Hashes` has the expected form
// this is required because deserializing [] is ambiguous
block.transactions.convert_to_hashes();
block.transactions_mut().convert_to_hashes();
}
block
});
Expand Down Expand Up @@ -532,7 +534,7 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
}

/// Gets an uncle block through the tag [BlockId] and index [u64].
async fn get_uncle(&self, tag: BlockId, idx: u64) -> TransportResult<Option<Block>> {
async fn get_uncle(&self, tag: BlockId, idx: u64) -> TransportResult<Option<N::BlockResponse>> {
let idx = U64::from(idx);
match tag {
BlockId::Hash(hash) => {
Expand Down Expand Up @@ -709,7 +711,9 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
/// # }
/// ```
#[cfg(feature = "pubsub")]
async fn subscribe_blocks(&self) -> TransportResult<alloy_pubsub::Subscription<Block>> {
async fn subscribe_blocks(
&self,
) -> TransportResult<alloy_pubsub::Subscription<N::BlockResponse>> {
self.root().pubsub_frontend()?;
let id = self.client().request("eth_subscribe", ("newHeads",)).await?;
self.root().get_subscription(id).await
Expand Down Expand Up @@ -989,7 +993,7 @@ mod tests {
use alloy_network::AnyNetwork;
use alloy_node_bindings::Anvil;
use alloy_primitives::{address, b256, bytes, keccak256};
use alloy_rpc_types_eth::request::TransactionRequest;
use alloy_rpc_types_eth::{request::TransactionRequest, Block};

fn init_tracing() {
let _ = tracing_subscriber::fmt::try_init();
Expand Down
29 changes: 28 additions & 1 deletion crates/rpc-types-eth/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Block RPC types.

use crate::{ConversionError, Transaction, Withdrawal};
use alloy_network_primitives::BlockTransactions;
use alloy_network_primitives::{BlockResponse, BlockTransactions, HeaderResponse};
use alloy_primitives::{Address, BlockHash, Bloom, Bytes, B256, B64, U256};
use alloy_serde::OtherFields;
use serde::{ser::Error, Deserialize, Serialize, Serializer};
Expand Down Expand Up @@ -205,6 +205,16 @@ impl TryFrom<Header> for alloy_consensus::Header {
}
}

impl HeaderResponse for Header {
fn base_fee_per_gas(&self) -> Option<u128> {
self.base_fee_per_gas
}

fn next_block_blob_fee(&self) -> Option<u128> {
self.next_block_blob_fee()
}
}

/// Error that can occur when converting other types to blocks
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum BlockError {
Expand Down Expand Up @@ -316,6 +326,23 @@ pub struct BlockOverrides {
pub block_hash: Option<BTreeMap<u64, B256>>,
}

impl<T> BlockResponse for Block<T> {
type Transaction = T;
type Header = Header;

fn header(&self) -> &Self::Header {
&self.header
}

fn transactions(&self) -> &BlockTransactions<T> {
&self.transactions
}

fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction> {
&mut self.transactions
}
}

#[cfg(test)]
mod tests {
use alloy_primitives::keccak256;
Expand Down