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 all commits
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};
76 changes: 76 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,41 @@ pub trait TransactionResponse {
fn input(&self) -> &Bytes;
}

/// Header JSON-RPC response.
pub trait HeaderResponse {
/// Block number
fn number(&self) -> u64;

/// Block timestamp
fn timestamp(&self) -> u64;

/// Extra data
fn extra_data(&self) -> &Bytes;

/// 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 +126,42 @@ 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()
}
}

impl<T: HeaderResponse> HeaderResponse for WithOtherFields<T> {
fn number(&self) -> u64 {
self.inner.number()
}

fn timestamp(&self) -> u64 {
self.inner.timestamp()
}

fn extra_data(&self) -> &Bytes {
self.inner.extra_data()
}

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

fn next_block_blob_fee(&self) -> Option<u128> {
self.inner.next_block_blob_fee()
}
}
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>;
}
2 changes: 1 addition & 1 deletion crates/provider/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ mod tests {
provider.anvil_mine(Some(U256::from(1)), None).await.unwrap();

let block = with_timeout(stream.next()).await.expect("Block wasn't fetched");
assert_eq!(block.header.number, Some(1u64));
assert_eq!(block.header.number, 1);
}

#[tokio::test]
Expand Down
2 changes: 1 addition & 1 deletion crates/provider/src/ext/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ mod tests {

let node_info = provider.anvil_node_info().await.unwrap();

assert_eq!(node_info.current_block_number, latest_block.header.number.unwrap() + 1);
assert_eq!(node_info.current_block_number, latest_block.header.number + 1);
}

#[tokio::test]
Expand Down
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
2 changes: 1 addition & 1 deletion crates/provider/src/heart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ impl<S> Heartbeat<S> {
/// the latest block.
fn handle_new_block(&mut self, block: Block, latest: &watch::Sender<Option<Block>>) {
// Blocks without numbers are ignored, as they're not part of the chain.
let Some(block_height) = &block.header.number else { return };
let block_height = &block.header.number;

// Add the block the lookbehind.
// The value is chosen arbitrarily to not have a huge memory footprint but still
Expand Down
44 changes: 24 additions & 20 deletions crates/provider/src/provider/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,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::{
AccessListResult, Block, BlockId, BlockNumberOrTag, EIP1186AccountProofResponse, FeeHistory,
Filter, FilterChanges, Log, SyncStatus,
AccessListResult, BlockId, BlockNumberOrTag, EIP1186AccountProofResponse, FeeHistory, Filter,
FilterChanges, Log, SyncStatus,
};
use alloy_transport::{BoxTransport, Transport, TransportResult};
use serde_json::value::RawValue;
Expand Down Expand Up @@ -213,8 +215,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 @@ -262,7 +264,7 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
&self,
block: BlockId,
kind: BlockTransactionsKind,
) -> TransportResult<Option<Block>> {
) -> TransportResult<Option<N::BlockResponse>> {
match block {
BlockId::Hash(hash) => self.get_block_by_hash(hash.into(), kind).await,
BlockId::Number(number) => {
Expand All @@ -277,21 +279,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 @@ -305,16 +307,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 @@ -548,7 +550,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 @@ -725,7 +727,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 @@ -1007,7 +1011,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 Expand Up @@ -1119,7 +1123,7 @@ mod tests {
let mut stream = sub.into_stream().take(2);
let mut n = 1;
while let Some(block) = stream.next().await {
assert_eq!(block.header.number.unwrap(), n);
assert_eq!(block.header.number, n);
assert_eq!(block.transactions.hashes().len(), 0);
n += 1;
}
Expand All @@ -1141,7 +1145,7 @@ mod tests {
let mut stream = sub.into_stream().take(2);
let mut n = 1;
while let Some(block) = stream.next().await {
assert_eq!(block.header.number.unwrap(), n);
assert_eq!(block.header.number, n);
assert_eq!(block.transactions.hashes().len(), 0);
n += 1;
}
Expand All @@ -1161,7 +1165,7 @@ mod tests {
let mut stream = sub.into_stream().take(1);
while let Some(block) = stream.next().await {
println!("New block {:?}", block);
assert!(block.header.number.unwrap() > 0);
assert!(block.header.number > 0);
}
}

Expand Down Expand Up @@ -1299,7 +1303,7 @@ mod tests {
let num = 0;
let tag: BlockNumberOrTag = num.into();
let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap();
assert_eq!(block.header.number.unwrap(), num);
assert_eq!(block.header.number, num);
}

#[tokio::test]
Expand All @@ -1309,7 +1313,7 @@ mod tests {
let num = 0;
let tag: BlockNumberOrTag = num.into();
let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap();
assert_eq!(block.header.number.unwrap(), num);
assert_eq!(block.header.number, num);
}

#[tokio::test]
Expand Down
Loading