mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat: Completely decouple rpc-types to standalone crate (#5193)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -2618,6 +2618,7 @@ dependencies = [
|
|||||||
"reth-revm",
|
"reth-revm",
|
||||||
"reth-rpc-builder",
|
"reth-rpc-builder",
|
||||||
"reth-rpc-types",
|
"reth-rpc-types",
|
||||||
|
"reth-rpc-types-compat",
|
||||||
"reth-tasks",
|
"reth-tasks",
|
||||||
"reth-transaction-pool",
|
"reth-transaction-pool",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -6019,6 +6020,7 @@ dependencies = [
|
|||||||
"reth-nippy-jar",
|
"reth-nippy-jar",
|
||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
"reth-rpc-types",
|
"reth-rpc-types",
|
||||||
|
"reth-rpc-types-compat",
|
||||||
"revm-primitives",
|
"revm-primitives",
|
||||||
"secp256k1 0.27.0",
|
"secp256k1 0.27.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -6262,6 +6264,7 @@ dependencies = [
|
|||||||
"rayon",
|
"rayon",
|
||||||
"reth-codecs",
|
"reth-codecs",
|
||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
|
"reth-rpc-types",
|
||||||
"revm",
|
"revm",
|
||||||
"revm-primitives",
|
"revm-primitives",
|
||||||
"secp256k1 0.27.0",
|
"secp256k1 0.27.0",
|
||||||
@ -6498,15 +6501,21 @@ version = "0.1.0-alpha.10"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-rlp",
|
"alloy-rlp",
|
||||||
|
"arbitrary",
|
||||||
|
"bytes",
|
||||||
|
"c-kzg",
|
||||||
"itertools 0.11.0",
|
"itertools 0.11.0",
|
||||||
"jsonrpsee-types",
|
"jsonrpsee-types",
|
||||||
|
"proptest",
|
||||||
|
"proptest-derive",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"reth-primitives",
|
"secp256k1 0.27.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"similar-asserts",
|
"similar-asserts",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -12,6 +12,7 @@ reth-codecs = { path = "../storage/codecs" }
|
|||||||
reth-nippy-jar = { path = "../storage/nippy-jar" }
|
reth-nippy-jar = { path = "../storage/nippy-jar" }
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-rpc-types.workspace = true
|
reth-rpc-types.workspace = true
|
||||||
|
reth-rpc-types-compat.workspace = true
|
||||||
reth-network-api.workspace = true
|
reth-network-api.workspace = true
|
||||||
# TODO(onbjerg): We only need this for [BlockBody]
|
# TODO(onbjerg): We only need this for [BlockBody]
|
||||||
reth-eth-wire = { path = "../net/eth-wire" }
|
reth-eth-wire = { path = "../net/eth-wire" }
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use reth_rpc_types::engine::{
|
|||||||
};
|
};
|
||||||
use reth_rpc_types_compat::engine::payload::{
|
use reth_rpc_types_compat::engine::payload::{
|
||||||
block_to_payload_v3, convert_block_to_payload_field_v2,
|
block_to_payload_v3, convert_block_to_payload_field_v2,
|
||||||
convert_standalone_withdraw_to_withdrawal, try_block_to_payload_v1,
|
convert_standalone_withdraw_to_withdrawal, from_primitive_sidecar, try_block_to_payload_v1,
|
||||||
};
|
};
|
||||||
use revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, SpecId};
|
use revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, SpecId};
|
||||||
/// Contains the built payload.
|
/// Contains the built payload.
|
||||||
@ -111,7 +111,11 @@ impl From<BuiltPayload> for ExecutionPayloadEnvelopeV3 {
|
|||||||
// Spec:
|
// Spec:
|
||||||
// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification-2>
|
// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification-2>
|
||||||
should_override_builder: false,
|
should_override_builder: false,
|
||||||
blobs_bundle: sidecars.into(),
|
blobs_bundle: sidecars
|
||||||
|
.into_iter()
|
||||||
|
.map(from_primitive_sidecar)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ description = "Commonly used types in reth."
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# reth
|
# reth
|
||||||
reth-codecs = { path = "../storage/codecs" }
|
reth-codecs = { path = "../storage/codecs" }
|
||||||
|
reth-rpc-types.workspace = true
|
||||||
revm-primitives = { workspace = true, features = ["serde"] }
|
revm-primitives = { workspace = true, features = ["serde"] }
|
||||||
|
|
||||||
# ethereum
|
# ethereum
|
||||||
@ -89,7 +90,7 @@ pprof = { version = "0.12", features = ["flamegraph", "frame-pointer", "criterio
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
arbitrary = ["revm-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"]
|
arbitrary = ["revm-primitives/arbitrary", "reth-rpc-types/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"]
|
||||||
test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"]
|
test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"]
|
||||||
# value-256 controls whether transaction Value fields are DB-encoded as 256 bits instead of the
|
# value-256 controls whether transaction Value fields are DB-encoded as 256 bits instead of the
|
||||||
# default of 128 bits.
|
# default of 128 bits.
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
use crate::{
|
use crate::{Address, Header, SealedHeader, TransactionSigned, Withdrawal, B256};
|
||||||
Address, BlockHash, BlockNumber, Header, SealedHeader, TransactionSigned, Withdrawal, B256, U64,
|
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
||||||
};
|
|
||||||
use alloy_rlp::{Decodable, Encodable, Error as RlpError, RlpDecodable, RlpEncodable};
|
|
||||||
use reth_codecs::derive_arbitrary;
|
use reth_codecs::derive_arbitrary;
|
||||||
use serde::{
|
use serde::{Deserialize, Serialize};
|
||||||
de::{MapAccess, Visitor},
|
use std::ops::Deref;
|
||||||
ser::SerializeStruct,
|
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
pub use reth_rpc_types::{
|
||||||
|
BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, ForkBlock, RpcBlockHash,
|
||||||
};
|
};
|
||||||
use std::{fmt, fmt::Formatter, num::ParseIntError, ops::Deref, str::FromStr};
|
|
||||||
|
|
||||||
/// Ethereum full block.
|
/// Ethereum full block.
|
||||||
///
|
///
|
||||||
@ -269,520 +267,6 @@ impl std::ops::DerefMut for SealedBlockWithSenders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Either a block hash _or_ a block number
|
|
||||||
#[derive_arbitrary(rlp)]
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
pub enum BlockHashOrNumber {
|
|
||||||
/// A block hash
|
|
||||||
Hash(B256),
|
|
||||||
/// A block number
|
|
||||||
Number(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl BlockHashOrNumber ===
|
|
||||||
|
|
||||||
impl BlockHashOrNumber {
|
|
||||||
/// Returns the block number if it is a [`BlockHashOrNumber::Number`].
|
|
||||||
#[inline]
|
|
||||||
pub fn as_number(self) -> Option<u64> {
|
|
||||||
match self {
|
|
||||||
BlockHashOrNumber::Hash(_) => None,
|
|
||||||
BlockHashOrNumber::Number(num) => Some(num),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<B256> for BlockHashOrNumber {
|
|
||||||
fn from(value: B256) -> Self {
|
|
||||||
BlockHashOrNumber::Hash(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u64> for BlockHashOrNumber {
|
|
||||||
fn from(value: u64) -> Self {
|
|
||||||
BlockHashOrNumber::Number(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows for RLP encoding of either a block hash or block number
|
|
||||||
impl Encodable for BlockHashOrNumber {
|
|
||||||
fn encode(&self, out: &mut dyn bytes::BufMut) {
|
|
||||||
match self {
|
|
||||||
Self::Hash(block_hash) => block_hash.encode(out),
|
|
||||||
Self::Number(block_number) => block_number.encode(out),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn length(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Self::Hash(block_hash) => block_hash.length(),
|
|
||||||
Self::Number(block_number) => block_number.length(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows for RLP decoding of a block hash or block number
|
|
||||||
impl Decodable for BlockHashOrNumber {
|
|
||||||
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
|
||||||
let header: u8 = *buf.first().ok_or(RlpError::InputTooShort)?;
|
|
||||||
// if the byte string is exactly 32 bytes, decode it into a Hash
|
|
||||||
// 0xa0 = 0x80 (start of string) + 0x20 (32, length of string)
|
|
||||||
if header == 0xa0 {
|
|
||||||
// strip the first byte, parsing the rest of the string.
|
|
||||||
// If the rest of the string fails to decode into 32 bytes, we'll bubble up the
|
|
||||||
// decoding error.
|
|
||||||
let hash = B256::decode(buf)?;
|
|
||||||
Ok(Self::Hash(hash))
|
|
||||||
} else {
|
|
||||||
// a block number when encoded as bytes ranges from 0 to any number of bytes - we're
|
|
||||||
// going to accept numbers which fit in less than 64 bytes.
|
|
||||||
// Any data larger than this which is not caught by the Hash decoding should error and
|
|
||||||
// is considered an invalid block number.
|
|
||||||
Ok(Self::Number(u64::decode(buf)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("failed to parse {input:?} as a number: {parse_int_error} or hash: {hex_error}")]
|
|
||||||
pub struct ParseBlockHashOrNumberError {
|
|
||||||
input: String,
|
|
||||||
parse_int_error: ParseIntError,
|
|
||||||
hex_error: crate::hex::FromHexError,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for BlockHashOrNumber {
|
|
||||||
type Err = ParseBlockHashOrNumberError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match u64::from_str(s) {
|
|
||||||
Ok(val) => Ok(val.into()),
|
|
||||||
Err(pares_int_error) => match B256::from_str(s) {
|
|
||||||
Ok(val) => Ok(val.into()),
|
|
||||||
Err(hex_error) => Err(ParseBlockHashOrNumberError {
|
|
||||||
input: s.to_string(),
|
|
||||||
parse_int_error: pares_int_error,
|
|
||||||
hex_error,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Block Identifier
|
|
||||||
/// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md>
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum BlockId {
|
|
||||||
/// A block hash and an optional bool that defines if it's canonical
|
|
||||||
Hash(RpcBlockHash),
|
|
||||||
/// A block number
|
|
||||||
Number(BlockNumberOrTag),
|
|
||||||
}
|
|
||||||
|
|
||||||
// === impl BlockId ===
|
|
||||||
|
|
||||||
impl BlockId {
|
|
||||||
/// Returns the block hash if it is [BlockId::Hash]
|
|
||||||
pub fn as_block_hash(&self) -> Option<B256> {
|
|
||||||
match self {
|
|
||||||
BlockId::Hash(hash) => Some(hash.block_hash),
|
|
||||||
BlockId::Number(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this is [BlockNumberOrTag::Latest]
|
|
||||||
pub fn is_latest(&self) -> bool {
|
|
||||||
matches!(self, BlockId::Number(BlockNumberOrTag::Latest))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this is [BlockNumberOrTag::Pending]
|
|
||||||
pub fn is_pending(&self) -> bool {
|
|
||||||
matches!(self, BlockId::Number(BlockNumberOrTag::Pending))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u64> for BlockId {
|
|
||||||
fn from(num: u64) -> Self {
|
|
||||||
BlockNumberOrTag::Number(num).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BlockNumberOrTag> for BlockId {
|
|
||||||
fn from(num: BlockNumberOrTag) -> Self {
|
|
||||||
BlockId::Number(num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<B256> for BlockId {
|
|
||||||
fn from(block_hash: B256) -> Self {
|
|
||||||
BlockId::Hash(RpcBlockHash { block_hash, require_canonical: None })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(B256, Option<bool>)> for BlockId {
|
|
||||||
fn from(hash_can: (B256, Option<bool>)) -> Self {
|
|
||||||
BlockId::Hash(RpcBlockHash { block_hash: hash_can.0, require_canonical: hash_can.1 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RpcBlockHash> for BlockId {
|
|
||||||
fn from(hash_can: RpcBlockHash) -> Self {
|
|
||||||
BlockId::Hash(hash_can)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BlockHashOrNumber> for BlockId {
|
|
||||||
fn from(value: BlockHashOrNumber) -> Self {
|
|
||||||
match value {
|
|
||||||
BlockHashOrNumber::Hash(hash) => B256::from(hash.0).into(),
|
|
||||||
BlockHashOrNumber::Number(number) => number.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for BlockId {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
BlockId::Hash(RpcBlockHash { ref block_hash, ref require_canonical }) => {
|
|
||||||
let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?;
|
|
||||||
s.serialize_field("blockHash", block_hash)?;
|
|
||||||
if let Some(require_canonical) = require_canonical {
|
|
||||||
s.serialize_field("requireCanonical", require_canonical)?;
|
|
||||||
}
|
|
||||||
s.end()
|
|
||||||
}
|
|
||||||
BlockId::Number(ref num) => num.serialize(serializer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for BlockId {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct BlockIdVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for BlockIdVisitor {
|
|
||||||
type Value = BlockId;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
formatter.write_str("Block identifier following EIP-1898")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
// Since there is no way to clearly distinguish between a DATA parameter and a QUANTITY parameter. A str is therefor deserialized into a Block Number: <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md>
|
|
||||||
// However, since the hex string should be a QUANTITY, we can safely assume that if the len is 66 bytes, it is in fact a hash, ref <https://github.com/ethereum/go-ethereum/blob/ee530c0d5aa70d2c00ab5691a89ab431b73f8165/rpc/types.go#L184-L184>
|
|
||||||
if v.len() == 66 {
|
|
||||||
Ok(BlockId::Hash(v.parse::<B256>().map_err(serde::de::Error::custom)?.into()))
|
|
||||||
} else {
|
|
||||||
// quantity hex string or tag
|
|
||||||
Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
|
||||||
where
|
|
||||||
A: MapAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut number = None;
|
|
||||||
let mut block_hash = None;
|
|
||||||
let mut require_canonical = None;
|
|
||||||
while let Some(key) = map.next_key::<String>()? {
|
|
||||||
match key.as_str() {
|
|
||||||
"blockNumber" => {
|
|
||||||
if number.is_some() || block_hash.is_some() {
|
|
||||||
return Err(serde::de::Error::duplicate_field("blockNumber"))
|
|
||||||
}
|
|
||||||
if require_canonical.is_some() {
|
|
||||||
return Err(serde::de::Error::custom(
|
|
||||||
"Non-valid require_canonical field",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
number = Some(map.next_value::<BlockNumberOrTag>()?)
|
|
||||||
}
|
|
||||||
"blockHash" => {
|
|
||||||
if number.is_some() || block_hash.is_some() {
|
|
||||||
return Err(serde::de::Error::duplicate_field("blockHash"))
|
|
||||||
}
|
|
||||||
|
|
||||||
block_hash = Some(map.next_value::<B256>()?);
|
|
||||||
}
|
|
||||||
"requireCanonical" => {
|
|
||||||
if number.is_some() || require_canonical.is_some() {
|
|
||||||
return Err(serde::de::Error::duplicate_field("requireCanonical"))
|
|
||||||
}
|
|
||||||
|
|
||||||
require_canonical = Some(map.next_value::<bool>()?)
|
|
||||||
}
|
|
||||||
key => {
|
|
||||||
return Err(serde::de::Error::unknown_field(
|
|
||||||
key,
|
|
||||||
&["blockNumber", "blockHash", "requireCanonical"],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(number) = number {
|
|
||||||
Ok(BlockId::Number(number))
|
|
||||||
} else if let Some(block_hash) = block_hash {
|
|
||||||
Ok(BlockId::Hash(RpcBlockHash { block_hash, require_canonical }))
|
|
||||||
} else {
|
|
||||||
Err(serde::de::Error::custom(
|
|
||||||
"Expected `blockNumber` or `blockHash` with `requireCanonical` optionally",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_any(BlockIdVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A block Number (or tag - "latest", "earliest", "pending")
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
|
|
||||||
pub enum BlockNumberOrTag {
|
|
||||||
/// Latest block
|
|
||||||
#[default]
|
|
||||||
Latest,
|
|
||||||
/// Finalized block accepted as canonical
|
|
||||||
Finalized,
|
|
||||||
/// Safe head block
|
|
||||||
Safe,
|
|
||||||
/// Earliest block (genesis)
|
|
||||||
Earliest,
|
|
||||||
/// Pending block (not yet part of the blockchain)
|
|
||||||
Pending,
|
|
||||||
/// Block by number from canon chain
|
|
||||||
Number(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockNumberOrTag {
|
|
||||||
/// Returns the numeric block number if explicitly set
|
|
||||||
pub fn as_number(&self) -> Option<u64> {
|
|
||||||
match *self {
|
|
||||||
BlockNumberOrTag::Number(num) => Some(num),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if a numeric block number is set
|
|
||||||
pub fn is_number(&self) -> bool {
|
|
||||||
matches!(self, BlockNumberOrTag::Number(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if it's "latest"
|
|
||||||
pub fn is_latest(&self) -> bool {
|
|
||||||
matches!(self, BlockNumberOrTag::Latest)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if it's "finalized"
|
|
||||||
pub fn is_finalized(&self) -> bool {
|
|
||||||
matches!(self, BlockNumberOrTag::Finalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if it's "safe"
|
|
||||||
pub fn is_safe(&self) -> bool {
|
|
||||||
matches!(self, BlockNumberOrTag::Safe)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if it's "pending"
|
|
||||||
pub fn is_pending(&self) -> bool {
|
|
||||||
matches!(self, BlockNumberOrTag::Pending)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if it's "earliest"
|
|
||||||
pub fn is_earliest(&self) -> bool {
|
|
||||||
matches!(self, BlockNumberOrTag::Earliest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u64> for BlockNumberOrTag {
|
|
||||||
fn from(num: u64) -> Self {
|
|
||||||
BlockNumberOrTag::Number(num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<U64> for BlockNumberOrTag {
|
|
||||||
fn from(num: U64) -> Self {
|
|
||||||
num.into_limbs()[0].into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for BlockNumberOrTag {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
BlockNumberOrTag::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")),
|
|
||||||
BlockNumberOrTag::Latest => serializer.serialize_str("latest"),
|
|
||||||
BlockNumberOrTag::Finalized => serializer.serialize_str("finalized"),
|
|
||||||
BlockNumberOrTag::Safe => serializer.serialize_str("safe"),
|
|
||||||
BlockNumberOrTag::Earliest => serializer.serialize_str("earliest"),
|
|
||||||
BlockNumberOrTag::Pending => serializer.serialize_str("pending"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for BlockNumberOrTag {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let s = String::deserialize(deserializer)?.to_lowercase();
|
|
||||||
s.parse().map_err(serde::de::Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for BlockNumberOrTag {
|
|
||||||
type Err = ParseBlockNumberError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let block = match s {
|
|
||||||
"latest" => Self::Latest,
|
|
||||||
"finalized" => Self::Finalized,
|
|
||||||
"safe" => Self::Safe,
|
|
||||||
"earliest" => Self::Earliest,
|
|
||||||
"pending" => Self::Pending,
|
|
||||||
_number => {
|
|
||||||
if let Some(hex_val) = s.strip_prefix("0x") {
|
|
||||||
let number = u64::from_str_radix(hex_val, 16);
|
|
||||||
BlockNumberOrTag::Number(number?)
|
|
||||||
} else {
|
|
||||||
return Err(HexStringMissingPrefixError::default().into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for BlockNumberOrTag {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
BlockNumberOrTag::Number(ref x) => format!("0x{x:x}").fmt(f),
|
|
||||||
BlockNumberOrTag::Latest => f.write_str("latest"),
|
|
||||||
BlockNumberOrTag::Finalized => f.write_str("finalized"),
|
|
||||||
BlockNumberOrTag::Safe => f.write_str("safe"),
|
|
||||||
BlockNumberOrTag::Earliest => f.write_str("earliest"),
|
|
||||||
BlockNumberOrTag::Pending => f.write_str("pending"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error variants when parsing a [BlockNumberOrTag]
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum ParseBlockNumberError {
|
|
||||||
/// Failed to parse hex value
|
|
||||||
#[error(transparent)]
|
|
||||||
ParseIntErr(#[from] ParseIntError),
|
|
||||||
/// Block numbers should be 0x-prefixed
|
|
||||||
#[error(transparent)]
|
|
||||||
MissingPrefix(#[from] HexStringMissingPrefixError),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Thrown when a 0x-prefixed hex string was expected
|
|
||||||
#[derive(Debug, Default, thiserror::Error)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[error("hex string without 0x prefix")]
|
|
||||||
pub struct HexStringMissingPrefixError;
|
|
||||||
|
|
||||||
/// A block hash which may have
|
|
||||||
/// a boolean requireCanonical field.
|
|
||||||
/// If false, an RPC call should raise if a block
|
|
||||||
/// matching the hash is not found.
|
|
||||||
/// If true, an RPC call should additionaly raise if
|
|
||||||
/// the block is not in the canonical chain.
|
|
||||||
/// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md#specification>
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)]
|
|
||||||
pub struct RpcBlockHash {
|
|
||||||
/// A block hash
|
|
||||||
pub block_hash: B256,
|
|
||||||
/// Whether the block must be a canonical block
|
|
||||||
pub require_canonical: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RpcBlockHash {
|
|
||||||
pub fn from_hash(block_hash: B256, require_canonical: Option<bool>) -> Self {
|
|
||||||
RpcBlockHash { block_hash, require_canonical }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<B256> for RpcBlockHash {
|
|
||||||
fn from(value: B256) -> Self {
|
|
||||||
Self::from_hash(value, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RpcBlockHash> for B256 {
|
|
||||||
fn from(value: RpcBlockHash) -> Self {
|
|
||||||
value.block_hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<B256> for RpcBlockHash {
|
|
||||||
fn as_ref(&self) -> &B256 {
|
|
||||||
&self.block_hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Block number and hash.
|
|
||||||
#[derive(Clone, Copy, Hash, Default, PartialEq, Eq)]
|
|
||||||
pub struct BlockNumHash {
|
|
||||||
/// Block number
|
|
||||||
pub number: BlockNumber,
|
|
||||||
/// Block hash
|
|
||||||
pub hash: BlockHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Block number and hash of the forked block.
|
|
||||||
pub type ForkBlock = BlockNumHash;
|
|
||||||
|
|
||||||
impl std::fmt::Debug for BlockNumHash {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_tuple("").field(&self.number).field(&self.hash).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockNumHash {
|
|
||||||
/// Creates a new `BlockNumHash` from a block number and hash.
|
|
||||||
pub fn new(number: BlockNumber, hash: BlockHash) -> Self {
|
|
||||||
Self { number, hash }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes `Self` and returns [`BlockNumber`], [`BlockHash`]
|
|
||||||
pub fn into_components(self) -> (BlockNumber, BlockHash) {
|
|
||||||
(self.number, self.hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether or not the block matches the given [BlockHashOrNumber].
|
|
||||||
pub fn matches_block_or_num(&self, block: &BlockHashOrNumber) -> bool {
|
|
||||||
match block {
|
|
||||||
BlockHashOrNumber::Hash(hash) => self.hash == *hash,
|
|
||||||
BlockHashOrNumber::Number(number) => self.number == *number,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(BlockNumber, BlockHash)> for BlockNumHash {
|
|
||||||
fn from(val: (BlockNumber, BlockHash)) -> Self {
|
|
||||||
BlockNumHash { number: val.0, hash: val.1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(BlockHash, BlockNumber)> for BlockNumHash {
|
|
||||||
fn from(val: (BlockHash, BlockNumber)) -> Self {
|
|
||||||
BlockNumHash { hash: val.0, number: val.1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A response to `GetBlockBodies`, containing bodies if any bodies were found.
|
/// A response to `GetBlockBodies`, containing bodies if any bodies were found.
|
||||||
///
|
///
|
||||||
/// Withdrawals can be optionally included at the end of the RLP encoded message.
|
/// Withdrawals can be optionally included at the end of the RLP encoded message.
|
||||||
@ -887,6 +371,9 @@ pub struct BlockBodyRoots {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::{BlockId, BlockNumberOrTag::*, *};
|
use super::{BlockId, BlockNumberOrTag::*, *};
|
||||||
use crate::hex_literal::hex;
|
use crate::hex_literal::hex;
|
||||||
|
use alloy_rlp::{Decodable, Encodable};
|
||||||
|
use reth_rpc_types::HexStringMissingPrefixError;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// Check parsing according to EIP-1898.
|
/// Check parsing according to EIP-1898.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -51,7 +51,7 @@ mod withdrawal;
|
|||||||
pub use account::{Account, Bytecode};
|
pub use account::{Account, Bytecode};
|
||||||
pub use block::{
|
pub use block::{
|
||||||
Block, BlockBody, BlockBodyRoots, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag,
|
Block, BlockBody, BlockBodyRoots, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag,
|
||||||
BlockWithSenders, ForkBlock, SealedBlock, SealedBlockWithSenders,
|
BlockWithSenders, ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders,
|
||||||
};
|
};
|
||||||
pub use bytes::{Buf, BufMut, BytesMut};
|
pub use bytes::{Buf, BufMut, BytesMut};
|
||||||
pub use chain::{
|
pub use chain::{
|
||||||
|
|||||||
@ -1,118 +1,4 @@
|
|||||||
use crate::PeerId;
|
pub use reth_rpc_types::NodeRecord;
|
||||||
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
|
||||||
use secp256k1::{SecretKey, SECP256K1};
|
|
||||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
|
||||||
use std::{
|
|
||||||
fmt,
|
|
||||||
fmt::Write,
|
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
|
||||||
num::ParseIntError,
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
use url::{Host, Url};
|
|
||||||
|
|
||||||
/// Represents a ENR in discv4.
|
|
||||||
///
|
|
||||||
/// Note: this is only an excerpt of the [`NodeRecord`] data structure.
|
|
||||||
#[derive(
|
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
Debug,
|
|
||||||
Eq,
|
|
||||||
PartialEq,
|
|
||||||
Hash,
|
|
||||||
SerializeDisplay,
|
|
||||||
DeserializeFromStr,
|
|
||||||
RlpEncodable,
|
|
||||||
RlpDecodable,
|
|
||||||
)]
|
|
||||||
pub struct NodeRecord {
|
|
||||||
/// The Address of a node.
|
|
||||||
pub address: IpAddr,
|
|
||||||
/// TCP port of the port that accepts connections.
|
|
||||||
pub tcp_port: u16,
|
|
||||||
/// UDP discovery port.
|
|
||||||
pub udp_port: u16,
|
|
||||||
/// Public key of the discovery service
|
|
||||||
pub id: PeerId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeRecord {
|
|
||||||
/// Derive the [`NodeRecord`] from the secret key and addr
|
|
||||||
pub fn from_secret_key(addr: SocketAddr, sk: &SecretKey) -> Self {
|
|
||||||
let pk = secp256k1::PublicKey::from_secret_key(SECP256K1, sk);
|
|
||||||
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
|
|
||||||
Self::new(addr, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the `address` into an [`Ipv4Addr`] if the `address` is a mapped
|
|
||||||
/// [Ipv6Addr](std::net::Ipv6Addr).
|
|
||||||
///
|
|
||||||
/// Returns `true` if the address was converted.
|
|
||||||
///
|
|
||||||
/// See also [std::net::Ipv6Addr::to_ipv4_mapped]
|
|
||||||
pub fn convert_ipv4_mapped(&mut self) -> bool {
|
|
||||||
// convert IPv4 mapped IPv6 address
|
|
||||||
if let IpAddr::V6(v6) = self.address {
|
|
||||||
if let Some(v4) = v6.to_ipv4_mapped() {
|
|
||||||
self.address = v4.into();
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as [Self::convert_ipv4_mapped] but consumes the type
|
|
||||||
pub fn into_ipv4_mapped(mut self) -> Self {
|
|
||||||
self.convert_ipv4_mapped();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new record from a socket addr and peer id.
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn new(addr: SocketAddr, id: PeerId) -> Self {
|
|
||||||
Self { address: addr.ip(), tcp_port: addr.port(), udp_port: addr.port(), id }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The TCP socket address of this node
|
|
||||||
#[must_use]
|
|
||||||
pub fn tcp_addr(&self) -> SocketAddr {
|
|
||||||
SocketAddr::new(self.address, self.tcp_port)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The UDP socket address of this node
|
|
||||||
#[must_use]
|
|
||||||
pub fn udp_addr(&self) -> SocketAddr {
|
|
||||||
SocketAddr::new(self.address, self.udp_port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for NodeRecord {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("enode://")?;
|
|
||||||
crate::hex::encode(self.id.as_slice()).fmt(f)?;
|
|
||||||
f.write_char('@')?;
|
|
||||||
match self.address {
|
|
||||||
IpAddr::V4(ip) => {
|
|
||||||
ip.fmt(f)?;
|
|
||||||
}
|
|
||||||
IpAddr::V6(ip) => {
|
|
||||||
// encapsulate with brackets
|
|
||||||
f.write_char('[')?;
|
|
||||||
ip.fmt(f)?;
|
|
||||||
f.write_char(']')?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.write_char(':')?;
|
|
||||||
self.tcp_port.fmt(f)?;
|
|
||||||
if self.tcp_port != self.udp_port {
|
|
||||||
f.write_str("?discport=")?;
|
|
||||||
self.udp_port.fmt(f)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// <https://github.com/ledgerwatch/erigon/blob/610e648dc43ec8cd6563313e28f06f534a9091b3/params/bootnodes.go>
|
// <https://github.com/ledgerwatch/erigon/blob/610e648dc43ec8cd6563313e28f06f534a9091b3/params/bootnodes.go>
|
||||||
|
|
||||||
@ -180,60 +66,18 @@ fn parse_nodes(nodes: impl IntoIterator<Item = impl AsRef<str>>) -> Vec<NodeReco
|
|||||||
nodes.into_iter().map(|s| s.as_ref().parse().unwrap()).collect()
|
nodes.into_iter().map(|s| s.as_ref().parse().unwrap()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possible error types when parsing a `NodeRecord`
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum NodeRecordParseError {
|
|
||||||
#[error("failed to parse url: {0}")]
|
|
||||||
InvalidUrl(String),
|
|
||||||
#[error("failed to parse id")]
|
|
||||||
InvalidId(String),
|
|
||||||
#[error("failed to discport query: {0}")]
|
|
||||||
Discport(ParseIntError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for NodeRecord {
|
|
||||||
type Err = NodeRecordParseError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let url = Url::parse(s).map_err(|e| NodeRecordParseError::InvalidUrl(e.to_string()))?;
|
|
||||||
|
|
||||||
let address = match url.host() {
|
|
||||||
Some(Host::Ipv4(ip)) => IpAddr::V4(ip),
|
|
||||||
Some(Host::Ipv6(ip)) => IpAddr::V6(ip),
|
|
||||||
Some(Host::Domain(ip)) => IpAddr::V4(
|
|
||||||
Ipv4Addr::from_str(ip)
|
|
||||||
.map_err(|e| NodeRecordParseError::InvalidUrl(e.to_string()))?,
|
|
||||||
),
|
|
||||||
_ => return Err(NodeRecordParseError::InvalidUrl(format!("invalid host: {url:?}"))),
|
|
||||||
};
|
|
||||||
let port = url
|
|
||||||
.port()
|
|
||||||
.ok_or_else(|| NodeRecordParseError::InvalidUrl("no port specified".to_string()))?;
|
|
||||||
|
|
||||||
let udp_port = if let Some(discovery_port) = url
|
|
||||||
.query_pairs()
|
|
||||||
.find_map(|(maybe_disc, port)| (maybe_disc.as_ref() == "discport").then_some(port))
|
|
||||||
{
|
|
||||||
discovery_port.parse::<u16>().map_err(NodeRecordParseError::Discport)?
|
|
||||||
} else {
|
|
||||||
port
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = url
|
|
||||||
.username()
|
|
||||||
.parse::<PeerId>()
|
|
||||||
.map_err(|e| NodeRecordParseError::InvalidId(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(Self { address, id, tcp_port: port, udp_port })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::{
|
||||||
|
net::{IpAddr, Ipv4Addr},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use alloy_rlp::{Decodable, Encodable};
|
use alloy_rlp::{Decodable, Encodable};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use rand::{thread_rng, Rng, RngCore};
|
use rand::{thread_rng, Rng, RngCore};
|
||||||
|
use reth_rpc_types::PeerId;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mapped_ipv6() {
|
fn test_mapped_ipv6() {
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
use crate::B512;
|
// Re-export PeerId for ease of use.
|
||||||
|
pub use reth_rpc_types::PeerId;
|
||||||
// TODO: should we use `PublicKey` for this? Even when dealing with public keys we should try to
|
|
||||||
// prevent misuse
|
|
||||||
/// This represents an uncompressed secp256k1 public key.
|
|
||||||
/// This encodes the concatenation of the x and y components of the affine point in bytes.
|
|
||||||
pub type PeerId = B512;
|
|
||||||
|
|
||||||
/// Generic wrapper with peer id
|
/// Generic wrapper with peer id
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@ -805,6 +805,7 @@ mod tests {
|
|||||||
use reth_payload_builder::test_utils::spawn_test_payload_service;
|
use reth_payload_builder::test_utils::spawn_test_payload_service;
|
||||||
use reth_primitives::{SealedBlock, B256, MAINNET};
|
use reth_primitives::{SealedBlock, B256, MAINNET};
|
||||||
use reth_provider::test_utils::MockEthProvider;
|
use reth_provider::test_utils::MockEthProvider;
|
||||||
|
use reth_rpc_types_compat::engine::payload::execution_payload_from_sealed_block;
|
||||||
use reth_tasks::TokioTaskExecutor;
|
use reth_tasks::TokioTaskExecutor;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
||||||
@ -837,7 +838,9 @@ mod tests {
|
|||||||
let (mut handle, api) = setup_engine_api();
|
let (mut handle, api) = setup_engine_api();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
api.new_payload_v1(SealedBlock::default().into()).await.unwrap();
|
api.new_payload_v1(execution_payload_from_sealed_block(SealedBlock::default()))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
assert_matches!(handle.from_api.recv().await, Some(BeaconEngineMessage::NewPayload { .. }));
|
assert_matches!(handle.from_api.recv().await, Some(BeaconEngineMessage::NewPayload { .. }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::transaction::from_recovered_with_block_context;
|
use crate::transaction::from_recovered_with_block_context;
|
||||||
use alloy_rlp::Encodable;
|
use alloy_rlp::Encodable;
|
||||||
use reth_primitives::{Block as PrimitiveBlock, Header as PrimitiveHeader, B256, U256};
|
use reth_primitives::{Block as PrimitiveBlock, Header as PrimitiveHeader, B256, U256, U64};
|
||||||
use reth_rpc_types::{Block, BlockError, BlockTransactions, BlockTransactionsKind, Header};
|
use reth_rpc_types::{Block, BlockError, BlockTransactions, BlockTransactionsKind, Header};
|
||||||
|
|
||||||
/// Converts the given primitive block into a [Block] response with the given
|
/// Converts the given primitive block into a [Block] response with the given
|
||||||
@ -85,6 +85,71 @@ pub fn from_block_full(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts from a [reth_primitives::SealedHeader] to a [reth_rpc_types::BlockNumberOrTag]
|
||||||
|
pub fn from_primitive_with_hash(primitive_header: reth_primitives::SealedHeader) -> Header {
|
||||||
|
let reth_primitives::SealedHeader {
|
||||||
|
header:
|
||||||
|
PrimitiveHeader {
|
||||||
|
parent_hash,
|
||||||
|
ommers_hash,
|
||||||
|
beneficiary,
|
||||||
|
state_root,
|
||||||
|
transactions_root,
|
||||||
|
receipts_root,
|
||||||
|
logs_bloom,
|
||||||
|
difficulty,
|
||||||
|
number,
|
||||||
|
gas_limit,
|
||||||
|
gas_used,
|
||||||
|
timestamp,
|
||||||
|
mix_hash,
|
||||||
|
nonce,
|
||||||
|
base_fee_per_gas,
|
||||||
|
extra_data,
|
||||||
|
withdrawals_root,
|
||||||
|
blob_gas_used,
|
||||||
|
excess_blob_gas,
|
||||||
|
parent_beacon_block_root,
|
||||||
|
},
|
||||||
|
hash,
|
||||||
|
} = primitive_header;
|
||||||
|
|
||||||
|
Header {
|
||||||
|
hash: Some(hash),
|
||||||
|
parent_hash,
|
||||||
|
uncles_hash: ommers_hash,
|
||||||
|
miner: beneficiary,
|
||||||
|
state_root,
|
||||||
|
transactions_root,
|
||||||
|
receipts_root,
|
||||||
|
withdrawals_root,
|
||||||
|
number: Some(U256::from(number)),
|
||||||
|
gas_used: U256::from(gas_used),
|
||||||
|
gas_limit: U256::from(gas_limit),
|
||||||
|
extra_data,
|
||||||
|
logs_bloom,
|
||||||
|
timestamp: U256::from(timestamp),
|
||||||
|
difficulty,
|
||||||
|
mix_hash,
|
||||||
|
nonce: Some(nonce.to_be_bytes().into()),
|
||||||
|
base_fee_per_gas: base_fee_per_gas.map(U256::from),
|
||||||
|
blob_gas_used: blob_gas_used.map(U64::from),
|
||||||
|
excess_blob_gas: excess_blob_gas.map(U64::from),
|
||||||
|
parent_beacon_block_root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_primitive_withdrawal(
|
||||||
|
withdrawal: reth_primitives::Withdrawal,
|
||||||
|
) -> reth_rpc_types::Withdrawal {
|
||||||
|
reth_rpc_types::Withdrawal {
|
||||||
|
index: withdrawal.validator_index,
|
||||||
|
address: withdrawal.address,
|
||||||
|
validator_index: withdrawal.validator_index,
|
||||||
|
amount: withdrawal.amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_block_with_transactions(
|
fn from_block_with_transactions(
|
||||||
block_length: usize,
|
block_length: usize,
|
||||||
@ -94,8 +159,14 @@ fn from_block_with_transactions(
|
|||||||
transactions: BlockTransactions,
|
transactions: BlockTransactions,
|
||||||
) -> Block {
|
) -> Block {
|
||||||
let uncles = block.ommers.into_iter().map(|h| h.hash_slow()).collect();
|
let uncles = block.ommers.into_iter().map(|h| h.hash_slow()).collect();
|
||||||
let header = Header::from_primitive_with_hash(block.header.seal(block_hash));
|
let header = from_primitive_with_hash(block.header.seal(block_hash));
|
||||||
let withdrawals = if header.withdrawals_root.is_some() { block.withdrawals } else { None };
|
let withdrawals = if header.withdrawals_root.is_some() {
|
||||||
|
block
|
||||||
|
.withdrawals
|
||||||
|
.map(|withdrawals| withdrawals.into_iter().map(from_primitive_withdrawal).collect())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
Block {
|
Block {
|
||||||
header,
|
header,
|
||||||
uncles,
|
uncles,
|
||||||
@ -110,7 +181,7 @@ fn from_block_with_transactions(
|
|||||||
/// an Uncle from its header.
|
/// an Uncle from its header.
|
||||||
pub fn uncle_block_from_header(header: PrimitiveHeader) -> Block {
|
pub fn uncle_block_from_header(header: PrimitiveHeader) -> Block {
|
||||||
let hash = header.hash_slow();
|
let hash = header.hash_slow();
|
||||||
let rpc_header = Header::from_primitive_with_hash(header.clone().seal(hash));
|
let rpc_header = from_primitive_with_hash(header.clone().seal(hash));
|
||||||
let uncle_block = PrimitiveBlock { header, ..Default::default() };
|
let uncle_block = PrimitiveBlock { header, ..Default::default() };
|
||||||
let size = Some(U256::from(uncle_block.length()));
|
let size = Some(U256::from(uncle_block.length()));
|
||||||
Block {
|
Block {
|
||||||
|
|||||||
@ -360,6 +360,47 @@ pub fn convert_to_payload_body_v1(value: Block) -> ExecutionPayloadBodyV1 {
|
|||||||
ExecutionPayloadBodyV1 { transactions: transactions.collect(), withdrawals: withdraw }
|
ExecutionPayloadBodyV1 { transactions: transactions.collect(), withdrawals: withdraw }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transforms a [reth_primitives::BlobTransactionSidecar] into a
|
||||||
|
/// [reth_rpc_types::BlobTransactionSidecar]
|
||||||
|
pub fn from_primitive_sidecar(
|
||||||
|
sidecar: reth_primitives::BlobTransactionSidecar,
|
||||||
|
) -> reth_rpc_types::BlobTransactionSidecar {
|
||||||
|
reth_rpc_types::BlobTransactionSidecar {
|
||||||
|
blobs: sidecar.blobs,
|
||||||
|
commitments: sidecar.commitments,
|
||||||
|
proofs: sidecar.proofs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms a [SealedBlock] into a [ExecutionPayloadV1]
|
||||||
|
pub fn execution_payload_from_sealed_block(value: SealedBlock) -> ExecutionPayloadV1 {
|
||||||
|
let transactions = value
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.map(|tx| {
|
||||||
|
let mut encoded = Vec::new();
|
||||||
|
tx.encode_enveloped(&mut encoded);
|
||||||
|
encoded.into()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
ExecutionPayloadV1 {
|
||||||
|
parent_hash: value.parent_hash,
|
||||||
|
fee_recipient: value.beneficiary,
|
||||||
|
state_root: value.state_root,
|
||||||
|
receipts_root: value.receipts_root,
|
||||||
|
logs_bloom: value.logs_bloom,
|
||||||
|
prev_randao: value.mix_hash,
|
||||||
|
block_number: U64::from(value.number),
|
||||||
|
gas_limit: U64::from(value.gas_limit),
|
||||||
|
gas_used: U64::from(value.gas_used),
|
||||||
|
timestamp: U64::from(value.timestamp),
|
||||||
|
extra_data: value.extra_data.clone(),
|
||||||
|
base_fee_per_gas: U256::from(value.base_fee_per_gas.unwrap_or_default()),
|
||||||
|
block_hash: value.hash(),
|
||||||
|
transactions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use reth_primitives::{hex, Bytes, U256, U64};
|
use reth_primitives::{hex, Bytes, U256, U64};
|
||||||
|
|||||||
@ -15,6 +15,13 @@ pub fn from_primitive_log(log: reth_primitives::Log) -> reth_rpc_types::Log {
|
|||||||
removed: false,
|
removed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts from a [reth_rpc_types::Log] to a [reth_primitives::Log]
|
||||||
|
#[inline]
|
||||||
|
pub fn to_primitive_log(log: reth_rpc_types::Log) -> reth_primitives::Log {
|
||||||
|
reth_primitives::Log { address: log.address, topics: log.topics, data: log.data }
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a primitive `AccessList` structure from the `reth_primitives` module into the
|
/// Converts a primitive `AccessList` structure from the `reth_primitives` module into the
|
||||||
/// corresponding RPC type.
|
/// corresponding RPC type.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -30,3 +37,19 @@ pub fn from_primitive_access_list(list: reth_primitives::AccessList) -> reth_rpc
|
|||||||
|
|
||||||
reth_rpc_types::AccessList(converted_list)
|
reth_rpc_types::AccessList(converted_list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a primitive `AccessList` structure from the `reth_primitives` module into the
|
||||||
|
/// corresponding RPC type.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_primitive_access_list(list: reth_rpc_types::AccessList) -> reth_primitives::AccessList {
|
||||||
|
let converted_list: Vec<reth_primitives::AccessListItem> = list
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| reth_primitives::AccessListItem {
|
||||||
|
address: item.address,
|
||||||
|
storage_keys: item.storage_keys,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
reth_primitives::AccessList(converted_list)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
//! Compatibility functions for rpc proof related types.
|
//! Compatibility functions for rpc proof related types.
|
||||||
|
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
serde_helper::JsonStorageKey,
|
|
||||||
trie::{AccountProof, StorageProof},
|
trie::{AccountProof, StorageProof},
|
||||||
U64,
|
U64,
|
||||||
};
|
};
|
||||||
use reth_rpc_types::{EIP1186AccountProofResponse, EIP1186StorageProof};
|
use reth_rpc_types::{storage::JsonStorageKey, EIP1186AccountProofResponse, EIP1186StorageProof};
|
||||||
|
|
||||||
/// Creates a new rpc storage proof from a primitive storage proof type.
|
/// Creates a new rpc storage proof from a primitive storage proof type.
|
||||||
pub fn from_primitive_storage_proof(proof: StorageProof) -> EIP1186StorageProof {
|
pub fn from_primitive_storage_proof(proof: StorageProof) -> EIP1186StorageProof {
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
//! Compatibility functions for rpc `Transaction` type.
|
//! Compatibility functions for rpc `Transaction` type.
|
||||||
mod signature;
|
mod signature;
|
||||||
|
mod typed;
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
BlockNumber, Transaction as PrimitiveTransaction, TransactionKind as PrimitiveTransactionKind,
|
BlockNumber, Transaction as PrimitiveTransaction, TransactionKind as PrimitiveTransactionKind,
|
||||||
TransactionSignedEcRecovered, TxType, B256, U128, U256, U64,
|
TransactionSignedEcRecovered, TxType, B256, U128, U256, U64,
|
||||||
};
|
};
|
||||||
use reth_rpc_types::{AccessListItem, CallInput, CallRequest, Transaction};
|
use reth_rpc_types::{AccessListItem, CallInput, CallRequest, Transaction};
|
||||||
use signature::from_primitive_signature;
|
use signature::from_primitive_signature;
|
||||||
|
pub use typed::*;
|
||||||
/// Create a new rpc transaction result for a mined transaction, using the given block hash,
|
/// Create a new rpc transaction result for a mined transaction, using the given block hash,
|
||||||
/// number, and tx index fields to populate the corresponding fields in the rpc result.
|
/// number, and tx index fields to populate the corresponding fields in the rpc result.
|
||||||
///
|
///
|
||||||
@ -132,6 +134,38 @@ fn fill(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert [reth_primitives::AccessList] to [reth_rpc_types::AccessList]
|
||||||
|
pub fn from_primitive_access_list(
|
||||||
|
access_list: reth_primitives::AccessList,
|
||||||
|
) -> reth_rpc_types::AccessList {
|
||||||
|
reth_rpc_types::AccessList(
|
||||||
|
access_list
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| reth_rpc_types::AccessListItem {
|
||||||
|
address: item.address.0.into(),
|
||||||
|
storage_keys: item.storage_keys.into_iter().map(|key| key.0.into()).collect(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert [reth_rpc_types::AccessList] to [reth_primitives::AccessList]
|
||||||
|
pub fn to_primitive_access_list(
|
||||||
|
access_list: reth_rpc_types::AccessList,
|
||||||
|
) -> reth_primitives::AccessList {
|
||||||
|
reth_primitives::AccessList(
|
||||||
|
access_list
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| reth_primitives::AccessListItem {
|
||||||
|
address: item.address.0.into(),
|
||||||
|
storage_keys: item.storage_keys.into_iter().map(|key| key.0.into()).collect(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert [TransactionSignedEcRecovered] to [CallRequest]
|
/// Convert [TransactionSignedEcRecovered] to [CallRequest]
|
||||||
pub fn transaction_to_call_request(tx: TransactionSignedEcRecovered) -> CallRequest {
|
pub fn transaction_to_call_request(tx: TransactionSignedEcRecovered) -> CallRequest {
|
||||||
let from = tx.signer();
|
let from = tx.signer();
|
||||||
@ -141,7 +175,7 @@ pub fn transaction_to_call_request(tx: TransactionSignedEcRecovered) -> CallRequ
|
|||||||
let input = tx.transaction.input().clone();
|
let input = tx.transaction.input().clone();
|
||||||
let nonce = tx.transaction.nonce();
|
let nonce = tx.transaction.nonce();
|
||||||
let chain_id = tx.transaction.chain_id();
|
let chain_id = tx.transaction.chain_id();
|
||||||
let access_list = tx.transaction.access_list().cloned();
|
let access_list = tx.transaction.access_list().cloned().map(from_primitive_access_list);
|
||||||
let max_fee_per_blob_gas = tx.transaction.max_fee_per_blob_gas();
|
let max_fee_per_blob_gas = tx.transaction.max_fee_per_blob_gas();
|
||||||
let blob_versioned_hashes = tx.transaction.blob_versioned_hashes();
|
let blob_versioned_hashes = tx.transaction.blob_versioned_hashes();
|
||||||
let tx_type = tx.transaction.tx_type();
|
let tx_type = tx.transaction.tx_type();
|
||||||
|
|||||||
70
crates/rpc/rpc-types-compat/src/transaction/typed.rs
Normal file
70
crates/rpc/rpc-types-compat/src/transaction/typed.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use crate::log::to_primitive_access_list;
|
||||||
|
|
||||||
|
/// Converts a typed transaction request into a primitive transaction.
|
||||||
|
///
|
||||||
|
/// Returns `None` if any of the following are true:
|
||||||
|
/// - `nonce` is greater than [`u64::MAX`]
|
||||||
|
/// - `gas_limit` is greater than [`u64::MAX`]
|
||||||
|
/// - `value` is greater than [`u128::MAX`]
|
||||||
|
pub fn to_primitive_transaction(
|
||||||
|
tx_request: reth_rpc_types::TypedTransactionRequest,
|
||||||
|
) -> Option<reth_primitives::Transaction> {
|
||||||
|
use reth_primitives::{Transaction, TxEip1559, TxEip2930, TxEip4844, TxLegacy};
|
||||||
|
use reth_rpc_types::TypedTransactionRequest;
|
||||||
|
|
||||||
|
Some(match tx_request {
|
||||||
|
TypedTransactionRequest::Legacy(tx) => Transaction::Legacy(TxLegacy {
|
||||||
|
chain_id: tx.chain_id,
|
||||||
|
nonce: tx.nonce.to(),
|
||||||
|
gas_price: tx.gas_price.to(),
|
||||||
|
gas_limit: tx.gas_limit.try_into().ok()?,
|
||||||
|
to: to_primitive_transaction_kind(tx.kind),
|
||||||
|
value: tx.value.into(),
|
||||||
|
input: tx.input,
|
||||||
|
}),
|
||||||
|
TypedTransactionRequest::EIP2930(tx) => Transaction::Eip2930(TxEip2930 {
|
||||||
|
chain_id: tx.chain_id,
|
||||||
|
nonce: tx.nonce.to(),
|
||||||
|
gas_price: tx.gas_price.to(),
|
||||||
|
gas_limit: tx.gas_limit.try_into().ok()?,
|
||||||
|
to: to_primitive_transaction_kind(tx.kind),
|
||||||
|
value: tx.value.into(),
|
||||||
|
input: tx.input,
|
||||||
|
access_list: to_primitive_access_list(tx.access_list),
|
||||||
|
}),
|
||||||
|
TypedTransactionRequest::EIP1559(tx) => Transaction::Eip1559(TxEip1559 {
|
||||||
|
chain_id: tx.chain_id,
|
||||||
|
nonce: tx.nonce.to(),
|
||||||
|
max_fee_per_gas: tx.max_fee_per_gas.to(),
|
||||||
|
gas_limit: tx.gas_limit.try_into().ok()?,
|
||||||
|
to: to_primitive_transaction_kind(tx.kind),
|
||||||
|
value: tx.value.into(),
|
||||||
|
input: tx.input,
|
||||||
|
access_list: to_primitive_access_list(tx.access_list),
|
||||||
|
max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(),
|
||||||
|
}),
|
||||||
|
TypedTransactionRequest::EIP4844(tx) => Transaction::Eip4844(TxEip4844 {
|
||||||
|
chain_id: tx.chain_id,
|
||||||
|
nonce: tx.nonce.to(),
|
||||||
|
gas_limit: tx.gas_limit.to(),
|
||||||
|
max_fee_per_gas: tx.max_fee_per_gas.to(),
|
||||||
|
max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(),
|
||||||
|
to: to_primitive_transaction_kind(tx.kind),
|
||||||
|
value: tx.value.into(),
|
||||||
|
access_list: to_primitive_access_list(tx.access_list),
|
||||||
|
blob_versioned_hashes: tx.blob_versioned_hashes,
|
||||||
|
max_fee_per_blob_gas: tx.max_fee_per_blob_gas.to(),
|
||||||
|
input: tx.input,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms a [reth_rpc_types::TransactionKind] into a [reth_primitives::TransactionKind]
|
||||||
|
pub fn to_primitive_transaction_kind(
|
||||||
|
kind: reth_rpc_types::TransactionKind,
|
||||||
|
) -> reth_primitives::TransactionKind {
|
||||||
|
match kind {
|
||||||
|
reth_rpc_types::TransactionKind::Call(to) => reth_primitives::TransactionKind::Call(to),
|
||||||
|
reth_rpc_types::TransactionKind::Create => reth_primitives::TransactionKind::Create,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,11 +11,8 @@ Reth RPC types
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# reth
|
|
||||||
reth-primitives.workspace = true
|
|
||||||
|
|
||||||
# # ethereum
|
# # ethereum
|
||||||
alloy-rlp = { workspace = true, features = ["arrayvec"] }
|
alloy-rlp = { workspace = true, features = ["arrayvec", "derive"] }
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
@ -24,11 +21,21 @@ serde = { workspace = true, features = ["derive"] }
|
|||||||
serde_with = "3.3"
|
serde_with = "3.3"
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
jsonrpsee-types = { workspace = true, optional = true }
|
jsonrpsee-types = { workspace = true, optional = true }
|
||||||
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
|
alloy-primitives = { workspace = true, features = ["rand", "rlp", "serde"] }
|
||||||
|
c-kzg = { workspace = true, features = ["serde"] }
|
||||||
|
url = "2.3"
|
||||||
|
# necessary so we don't hit a "undeclared 'std'":
|
||||||
|
# https://github.com/paradigmxyz/reth/pull/177#discussion_r1021172198
|
||||||
|
secp256k1.workspace = true
|
||||||
|
bytes.workspace = true
|
||||||
|
|
||||||
|
arbitrary = { workspace = true, features = ["derive"], optional = true }
|
||||||
|
proptest = { workspace = true, optional = true }
|
||||||
|
proptest-derive = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["jsonrpsee-types"]
|
default = ["jsonrpsee-types"]
|
||||||
|
arbitrary = ["dep:arbitrary", "dep:proptest-derive", "dep:proptest", "alloy-primitives/arbitrary"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# misc
|
# misc
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
use crate::{NodeRecord, PeerId};
|
||||||
use alloy_primitives::{B256, U256};
|
use alloy_primitives::{B256, U256};
|
||||||
use reth_primitives::{NodeRecord, PeerId};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
@ -79,7 +79,7 @@ pub struct NetworkStatus {
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct EthProtocolInfo {
|
pub struct EthProtocolInfo {
|
||||||
/// The current difficulty at the head of the chain.
|
/// The current difficulty at the head of the chain.
|
||||||
#[serde(deserialize_with = "reth_primitives::serde_helper::deserialize_json_u256")]
|
#[serde(deserialize_with = "crate::serde_helpers::json_u256::deserialize_json_u256")]
|
||||||
pub difficulty: U256,
|
pub difficulty: U256,
|
||||||
/// The block hash of the head of the chain.
|
/// The block hash of the head of the chain.
|
||||||
pub head: B256,
|
pub head: B256,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
use crate::serde_helpers::storage::JsonStorageKey;
|
||||||
use alloy_primitives::{Address, Bytes, B256, B512, U256, U64};
|
use alloy_primitives::{Address, Bytes, B256, B512, U256, U64};
|
||||||
use reth_primitives::serde_helper::JsonStorageKey;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Account information.
|
/// Account information.
|
||||||
|
|||||||
@ -1,9 +1,19 @@
|
|||||||
//! Contains types that represent ethereum types in [reth_primitives] when used in RPC
|
//! Contains types that represent ethereum types when used in RPC
|
||||||
use crate::Transaction;
|
use crate::{Transaction, Withdrawal};
|
||||||
use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256, U64};
|
use alloy_primitives::{Address, BlockHash, BlockNumber, Bloom, Bytes, B256, B64, U256, U64};
|
||||||
use reth_primitives::{Header as PrimitiveHeader, SealedHeader, Withdrawal};
|
use alloy_rlp::{bytes, Decodable, Encodable, Error as RlpError};
|
||||||
use serde::{ser::Error, Deserialize, Serialize, Serializer};
|
use serde::{
|
||||||
use std::{collections::BTreeMap, ops::Deref};
|
de::{MapAccess, Visitor},
|
||||||
|
ser::{Error, SerializeStruct},
|
||||||
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
fmt::{self, Formatter},
|
||||||
|
num::ParseIntError,
|
||||||
|
ops::Deref,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`,
|
/// Block Transactions depending on the boolean attribute of `eth_getBlockBy*`,
|
||||||
/// or if used by `eth_getUncle*`
|
/// or if used by `eth_getUncle*`
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
@ -120,7 +130,7 @@ pub struct Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Block header representation.
|
/// Block header representation.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
/// Hash of the block
|
/// Hash of the block
|
||||||
@ -173,62 +183,506 @@ pub struct Header {
|
|||||||
pub parent_beacon_block_root: Option<B256>,
|
pub parent_beacon_block_root: Option<B256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// === impl Header ===
|
/// A block hash which may have
|
||||||
|
/// a boolean requireCanonical field.
|
||||||
|
/// If false, an RPC call should raise if a block
|
||||||
|
/// matching the hash is not found.
|
||||||
|
/// If true, an RPC call should additionaly raise if
|
||||||
|
/// the block is not in the canonical chain.
|
||||||
|
/// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md#specification>
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)]
|
||||||
|
pub struct RpcBlockHash {
|
||||||
|
/// A block hash
|
||||||
|
pub block_hash: B256,
|
||||||
|
/// Whether the block must be a canonical block
|
||||||
|
pub require_canonical: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Header {
|
impl RpcBlockHash {
|
||||||
/// Converts the primitive header type to this RPC type
|
/// Returns an [RpcBlockHash] from a [B256].
|
||||||
///
|
pub const fn from_hash(block_hash: B256, require_canonical: Option<bool>) -> Self {
|
||||||
/// CAUTION: this takes the header's hash as is and does _not_ calculate the hash.
|
RpcBlockHash { block_hash, require_canonical }
|
||||||
pub fn from_primitive_with_hash(primitive_header: SealedHeader) -> Self {
|
}
|
||||||
let SealedHeader {
|
}
|
||||||
header:
|
|
||||||
PrimitiveHeader {
|
|
||||||
parent_hash,
|
|
||||||
ommers_hash,
|
|
||||||
beneficiary,
|
|
||||||
state_root,
|
|
||||||
transactions_root,
|
|
||||||
receipts_root,
|
|
||||||
logs_bloom,
|
|
||||||
difficulty,
|
|
||||||
number,
|
|
||||||
gas_limit,
|
|
||||||
gas_used,
|
|
||||||
timestamp,
|
|
||||||
mix_hash,
|
|
||||||
nonce,
|
|
||||||
base_fee_per_gas,
|
|
||||||
extra_data,
|
|
||||||
withdrawals_root,
|
|
||||||
blob_gas_used,
|
|
||||||
excess_blob_gas,
|
|
||||||
parent_beacon_block_root,
|
|
||||||
},
|
|
||||||
hash,
|
|
||||||
} = primitive_header;
|
|
||||||
|
|
||||||
Header {
|
impl From<B256> for RpcBlockHash {
|
||||||
hash: Some(hash),
|
fn from(value: B256) -> Self {
|
||||||
parent_hash,
|
Self::from_hash(value, None)
|
||||||
uncles_hash: ommers_hash,
|
}
|
||||||
miner: beneficiary,
|
}
|
||||||
state_root,
|
|
||||||
transactions_root,
|
impl From<RpcBlockHash> for B256 {
|
||||||
receipts_root,
|
fn from(value: RpcBlockHash) -> Self {
|
||||||
withdrawals_root,
|
value.block_hash
|
||||||
number: Some(U256::from(number)),
|
}
|
||||||
gas_used: U256::from(gas_used),
|
}
|
||||||
gas_limit: U256::from(gas_limit),
|
|
||||||
extra_data,
|
impl AsRef<B256> for RpcBlockHash {
|
||||||
logs_bloom,
|
fn as_ref(&self) -> &B256 {
|
||||||
timestamp: U256::from(timestamp),
|
&self.block_hash
|
||||||
difficulty,
|
}
|
||||||
mix_hash,
|
}
|
||||||
nonce: Some(nonce.to_be_bytes().into()),
|
|
||||||
base_fee_per_gas: base_fee_per_gas.map(U256::from),
|
/// A block Number (or tag - "latest", "earliest", "pending")
|
||||||
blob_gas_used: blob_gas_used.map(U64::from),
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
excess_blob_gas: excess_blob_gas.map(U64::from),
|
pub enum BlockNumberOrTag {
|
||||||
parent_beacon_block_root,
|
/// Latest block
|
||||||
|
#[default]
|
||||||
|
Latest,
|
||||||
|
/// Finalized block accepted as canonical
|
||||||
|
Finalized,
|
||||||
|
/// Safe head block
|
||||||
|
Safe,
|
||||||
|
/// Earliest block (genesis)
|
||||||
|
Earliest,
|
||||||
|
/// Pending block (not yet part of the blockchain)
|
||||||
|
Pending,
|
||||||
|
/// Block by number from canon chain
|
||||||
|
Number(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockNumberOrTag {
|
||||||
|
/// Returns the numeric block number if explicitly set
|
||||||
|
pub const fn as_number(&self) -> Option<u64> {
|
||||||
|
match *self {
|
||||||
|
BlockNumberOrTag::Number(num) => Some(num),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if a numeric block number is set
|
||||||
|
pub const fn is_number(&self) -> bool {
|
||||||
|
matches!(self, BlockNumberOrTag::Number(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if it's "latest"
|
||||||
|
pub const fn is_latest(&self) -> bool {
|
||||||
|
matches!(self, BlockNumberOrTag::Latest)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if it's "finalized"
|
||||||
|
pub const fn is_finalized(&self) -> bool {
|
||||||
|
matches!(self, BlockNumberOrTag::Finalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if it's "safe"
|
||||||
|
pub const fn is_safe(&self) -> bool {
|
||||||
|
matches!(self, BlockNumberOrTag::Safe)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if it's "pending"
|
||||||
|
pub const fn is_pending(&self) -> bool {
|
||||||
|
matches!(self, BlockNumberOrTag::Pending)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if it's "earliest"
|
||||||
|
pub const fn is_earliest(&self) -> bool {
|
||||||
|
matches!(self, BlockNumberOrTag::Earliest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for BlockNumberOrTag {
|
||||||
|
fn from(num: u64) -> Self {
|
||||||
|
BlockNumberOrTag::Number(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<U64> for BlockNumberOrTag {
|
||||||
|
fn from(num: U64) -> Self {
|
||||||
|
num.into_limbs()[0].into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for BlockNumberOrTag {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
BlockNumberOrTag::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")),
|
||||||
|
BlockNumberOrTag::Latest => serializer.serialize_str("latest"),
|
||||||
|
BlockNumberOrTag::Finalized => serializer.serialize_str("finalized"),
|
||||||
|
BlockNumberOrTag::Safe => serializer.serialize_str("safe"),
|
||||||
|
BlockNumberOrTag::Earliest => serializer.serialize_str("earliest"),
|
||||||
|
BlockNumberOrTag::Pending => serializer.serialize_str("pending"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for BlockNumberOrTag {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?.to_lowercase();
|
||||||
|
s.parse().map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BlockNumberOrTag {
|
||||||
|
type Err = ParseBlockNumberError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let block = match s {
|
||||||
|
"latest" => Self::Latest,
|
||||||
|
"finalized" => Self::Finalized,
|
||||||
|
"safe" => Self::Safe,
|
||||||
|
"earliest" => Self::Earliest,
|
||||||
|
"pending" => Self::Pending,
|
||||||
|
_number => {
|
||||||
|
if let Some(hex_val) = s.strip_prefix("0x") {
|
||||||
|
let number = u64::from_str_radix(hex_val, 16);
|
||||||
|
BlockNumberOrTag::Number(number?)
|
||||||
|
} else {
|
||||||
|
return Err(HexStringMissingPrefixError::default().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BlockNumberOrTag {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
BlockNumberOrTag::Number(ref x) => format!("0x{x:x}").fmt(f),
|
||||||
|
BlockNumberOrTag::Latest => f.write_str("latest"),
|
||||||
|
BlockNumberOrTag::Finalized => f.write_str("finalized"),
|
||||||
|
BlockNumberOrTag::Safe => f.write_str("safe"),
|
||||||
|
BlockNumberOrTag::Earliest => f.write_str("earliest"),
|
||||||
|
BlockNumberOrTag::Pending => f.write_str("pending"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error variants when parsing a [BlockNumberOrTag]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ParseBlockNumberError {
|
||||||
|
/// Failed to parse hex value
|
||||||
|
#[error(transparent)]
|
||||||
|
ParseIntErr(#[from] ParseIntError),
|
||||||
|
/// Block numbers should be 0x-prefixed
|
||||||
|
#[error(transparent)]
|
||||||
|
MissingPrefix(#[from] HexStringMissingPrefixError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Thrown when a 0x-prefixed hex string was expected
|
||||||
|
#[derive(Copy, Clone, Debug, Default, thiserror::Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[error("hex string without 0x prefix")]
|
||||||
|
pub struct HexStringMissingPrefixError;
|
||||||
|
|
||||||
|
/// A Block Identifier
|
||||||
|
/// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md>
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum BlockId {
|
||||||
|
/// A block hash and an optional bool that defines if it's canonical
|
||||||
|
Hash(RpcBlockHash),
|
||||||
|
/// A block number
|
||||||
|
Number(BlockNumberOrTag),
|
||||||
|
}
|
||||||
|
|
||||||
|
// === impl BlockId ===
|
||||||
|
|
||||||
|
impl BlockId {
|
||||||
|
/// Returns the block hash if it is [BlockId::Hash]
|
||||||
|
pub const fn as_block_hash(&self) -> Option<B256> {
|
||||||
|
match self {
|
||||||
|
BlockId::Hash(hash) => Some(hash.block_hash),
|
||||||
|
BlockId::Number(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this is [BlockNumberOrTag::Latest]
|
||||||
|
pub const fn is_latest(&self) -> bool {
|
||||||
|
matches!(self, BlockId::Number(BlockNumberOrTag::Latest))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this is [BlockNumberOrTag::Pending]
|
||||||
|
pub const fn is_pending(&self) -> bool {
|
||||||
|
matches!(self, BlockId::Number(BlockNumberOrTag::Pending))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for BlockId {
|
||||||
|
fn from(num: u64) -> Self {
|
||||||
|
BlockNumberOrTag::Number(num).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlockNumberOrTag> for BlockId {
|
||||||
|
fn from(num: BlockNumberOrTag) -> Self {
|
||||||
|
BlockId::Number(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<B256> for BlockId {
|
||||||
|
fn from(block_hash: B256) -> Self {
|
||||||
|
BlockId::Hash(RpcBlockHash { block_hash, require_canonical: None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(B256, Option<bool>)> for BlockId {
|
||||||
|
fn from(hash_can: (B256, Option<bool>)) -> Self {
|
||||||
|
BlockId::Hash(RpcBlockHash { block_hash: hash_can.0, require_canonical: hash_can.1 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for BlockId {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
BlockId::Hash(RpcBlockHash { ref block_hash, ref require_canonical }) => {
|
||||||
|
let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?;
|
||||||
|
s.serialize_field("blockHash", block_hash)?;
|
||||||
|
if let Some(require_canonical) = require_canonical {
|
||||||
|
s.serialize_field("requireCanonical", require_canonical)?;
|
||||||
|
}
|
||||||
|
s.end()
|
||||||
|
}
|
||||||
|
BlockId::Number(ref num) => num.serialize(serializer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for BlockId {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct BlockIdVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for BlockIdVisitor {
|
||||||
|
type Value = BlockId;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("Block identifier following EIP-1898")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
// Since there is no way to clearly distinguish between a DATA parameter and a QUANTITY parameter. A str is therefor deserialized into a Block Number: <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md>
|
||||||
|
// However, since the hex string should be a QUANTITY, we can safely assume that if the len is 66 bytes, it is in fact a hash, ref <https://github.com/ethereum/go-ethereum/blob/ee530c0d5aa70d2c00ab5691a89ab431b73f8165/rpc/types.go#L184-L184>
|
||||||
|
if v.len() == 66 {
|
||||||
|
Ok(BlockId::Hash(v.parse::<B256>().map_err(serde::de::Error::custom)?.into()))
|
||||||
|
} else {
|
||||||
|
// quantity hex string or tag
|
||||||
|
Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut number = None;
|
||||||
|
let mut block_hash = None;
|
||||||
|
let mut require_canonical = None;
|
||||||
|
while let Some(key) = map.next_key::<String>()? {
|
||||||
|
match key.as_str() {
|
||||||
|
"blockNumber" => {
|
||||||
|
if number.is_some() || block_hash.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("blockNumber"));
|
||||||
|
}
|
||||||
|
if require_canonical.is_some() {
|
||||||
|
return Err(serde::de::Error::custom(
|
||||||
|
"Non-valid require_canonical field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
number = Some(map.next_value::<BlockNumberOrTag>()?)
|
||||||
|
}
|
||||||
|
"blockHash" => {
|
||||||
|
if number.is_some() || block_hash.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("blockHash"));
|
||||||
|
}
|
||||||
|
|
||||||
|
block_hash = Some(map.next_value::<B256>()?);
|
||||||
|
}
|
||||||
|
"requireCanonical" => {
|
||||||
|
if number.is_some() || require_canonical.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("requireCanonical"));
|
||||||
|
}
|
||||||
|
|
||||||
|
require_canonical = Some(map.next_value::<bool>()?)
|
||||||
|
}
|
||||||
|
key => {
|
||||||
|
return Err(serde::de::Error::unknown_field(
|
||||||
|
key,
|
||||||
|
&["blockNumber", "blockHash", "requireCanonical"],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(number) = number {
|
||||||
|
Ok(BlockId::Number(number))
|
||||||
|
} else if let Some(block_hash) = block_hash {
|
||||||
|
Ok(BlockId::Hash(RpcBlockHash { block_hash, require_canonical }))
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::custom(
|
||||||
|
"Expected `blockNumber` or `blockHash` with `requireCanonical` optionally",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(BlockIdVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block number and hash.
|
||||||
|
#[derive(Clone, Copy, Hash, Default, PartialEq, Eq)]
|
||||||
|
pub struct BlockNumHash {
|
||||||
|
/// Block number
|
||||||
|
pub number: BlockNumber,
|
||||||
|
/// Block hash
|
||||||
|
pub hash: BlockHash,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block number and hash of the forked block.
|
||||||
|
pub type ForkBlock = BlockNumHash;
|
||||||
|
|
||||||
|
impl std::fmt::Debug for BlockNumHash {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("").field(&self.number).field(&self.hash).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockNumHash {
|
||||||
|
/// Creates a new `BlockNumHash` from a block number and hash.
|
||||||
|
pub fn new(number: BlockNumber, hash: BlockHash) -> Self {
|
||||||
|
Self { number, hash }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes `Self` and returns [`BlockNumber`], [`BlockHash`]
|
||||||
|
pub fn into_components(self) -> (BlockNumber, BlockHash) {
|
||||||
|
(self.number, self.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not the block matches the given [BlockHashOrNumber].
|
||||||
|
pub fn matches_block_or_num(&self, block: &BlockHashOrNumber) -> bool {
|
||||||
|
match block {
|
||||||
|
BlockHashOrNumber::Hash(hash) => self.hash == *hash,
|
||||||
|
BlockHashOrNumber::Number(number) => self.number == *number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(BlockNumber, BlockHash)> for BlockNumHash {
|
||||||
|
fn from(val: (BlockNumber, BlockHash)) -> Self {
|
||||||
|
BlockNumHash { number: val.0, hash: val.1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(BlockHash, BlockNumber)> for BlockNumHash {
|
||||||
|
fn from(val: (BlockHash, BlockNumber)) -> Self {
|
||||||
|
BlockNumHash { hash: val.0, number: val.1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Either a block hash _or_ a block number
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(
|
||||||
|
any(test, feature = "arbitrary"),
|
||||||
|
derive(proptest_derive::Arbitrary, arbitrary::Arbitrary)
|
||||||
|
)]
|
||||||
|
pub enum BlockHashOrNumber {
|
||||||
|
/// A block hash
|
||||||
|
Hash(B256),
|
||||||
|
/// A block number
|
||||||
|
Number(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
// === impl BlockHashOrNumber ===
|
||||||
|
|
||||||
|
impl BlockHashOrNumber {
|
||||||
|
/// Returns the block number if it is a [`BlockHashOrNumber::Number`].
|
||||||
|
#[inline]
|
||||||
|
pub fn as_number(self) -> Option<u64> {
|
||||||
|
match self {
|
||||||
|
BlockHashOrNumber::Hash(_) => None,
|
||||||
|
BlockHashOrNumber::Number(num) => Some(num),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<B256> for BlockHashOrNumber {
|
||||||
|
fn from(value: B256) -> Self {
|
||||||
|
BlockHashOrNumber::Hash(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for BlockHashOrNumber {
|
||||||
|
fn from(value: u64) -> Self {
|
||||||
|
BlockHashOrNumber::Number(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows for RLP encoding of either a block hash or block number
|
||||||
|
impl Encodable for BlockHashOrNumber {
|
||||||
|
fn encode(&self, out: &mut dyn bytes::BufMut) {
|
||||||
|
match self {
|
||||||
|
Self::Hash(block_hash) => block_hash.encode(out),
|
||||||
|
Self::Number(block_number) => block_number.encode(out),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn length(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Hash(block_hash) => block_hash.length(),
|
||||||
|
Self::Number(block_number) => block_number.length(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows for RLP decoding of a block hash or block number
|
||||||
|
impl Decodable for BlockHashOrNumber {
|
||||||
|
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||||
|
let header: u8 = *buf.first().ok_or(RlpError::InputTooShort)?;
|
||||||
|
// if the byte string is exactly 32 bytes, decode it into a Hash
|
||||||
|
// 0xa0 = 0x80 (start of string) + 0x20 (32, length of string)
|
||||||
|
if header == 0xa0 {
|
||||||
|
// strip the first byte, parsing the rest of the string.
|
||||||
|
// If the rest of the string fails to decode into 32 bytes, we'll bubble up the
|
||||||
|
// decoding error.
|
||||||
|
let hash = B256::decode(buf)?;
|
||||||
|
Ok(Self::Hash(hash))
|
||||||
|
} else {
|
||||||
|
// a block number when encoded as bytes ranges from 0 to any number of bytes - we're
|
||||||
|
// going to accept numbers which fit in less than 64 bytes.
|
||||||
|
// Any data larger than this which is not caught by the Hash decoding should error and
|
||||||
|
// is considered an invalid block number.
|
||||||
|
Ok(Self::Number(u64::decode(buf)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error thrown when parsing a [BlockHashOrNumber] from a string.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("failed to parse {input:?} as a number: {parse_int_error} or hash: {hex_error}")]
|
||||||
|
pub struct ParseBlockHashOrNumberError {
|
||||||
|
input: String,
|
||||||
|
parse_int_error: ParseIntError,
|
||||||
|
hex_error: alloy_primitives::hex::FromHexError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BlockHashOrNumber {
|
||||||
|
type Err = ParseBlockHashOrNumberError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match u64::from_str(s) {
|
||||||
|
Ok(val) => Ok(val.into()),
|
||||||
|
Err(pares_int_error) => match B256::from_str(s) {
|
||||||
|
Ok(val) => Ok(val.into()),
|
||||||
|
Err(hex_error) => Err(ParseBlockHashOrNumberError {
|
||||||
|
input: s.to_string(),
|
||||||
|
parse_int_error: pares_int_error,
|
||||||
|
hex_error,
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
//use crate::access_list::AccessList;
|
//use crate::access_list::AccessList;
|
||||||
use crate::BlockOverrides;
|
use crate::{AccessList, BlockId, BlockOverrides};
|
||||||
use alloy_primitives::{Address, Bytes, B256, U256, U64, U8};
|
use alloy_primitives::{Address, Bytes, B256, U256, U64, U8};
|
||||||
use reth_primitives::{AccessList, BlockId};
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
/// Bundle of transactions
|
/// Bundle of transactions
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
use crate::eth::withdrawal::BeaconAPIWithdrawal;
|
use crate::eth::{transaction::BlobTransactionSidecar, withdrawal::BeaconAPIWithdrawal};
|
||||||
pub use crate::Withdrawal;
|
pub use crate::Withdrawal;
|
||||||
use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256, U64};
|
use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256, U64};
|
||||||
use reth_primitives::{
|
use c_kzg::{Blob, Bytes48};
|
||||||
kzg::{Blob, Bytes48},
|
|
||||||
BlobTransactionSidecar, SealedBlock,
|
|
||||||
};
|
|
||||||
use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer};
|
use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer};
|
||||||
use serde_with::{serde_as, DisplayFromStr};
|
use serde_with::{serde_as, DisplayFromStr};
|
||||||
|
|
||||||
@ -140,36 +137,6 @@ pub struct ExecutionPayloadV1 {
|
|||||||
pub transactions: Vec<Bytes>,
|
pub transactions: Vec<Bytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SealedBlock> for ExecutionPayloadV1 {
|
|
||||||
fn from(value: SealedBlock) -> Self {
|
|
||||||
let transactions = value
|
|
||||||
.body
|
|
||||||
.iter()
|
|
||||||
.map(|tx| {
|
|
||||||
let mut encoded = Vec::new();
|
|
||||||
tx.encode_enveloped(&mut encoded);
|
|
||||||
encoded.into()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
ExecutionPayloadV1 {
|
|
||||||
parent_hash: value.parent_hash,
|
|
||||||
fee_recipient: value.beneficiary,
|
|
||||||
state_root: value.state_root,
|
|
||||||
receipts_root: value.receipts_root,
|
|
||||||
logs_bloom: value.logs_bloom,
|
|
||||||
prev_randao: value.mix_hash,
|
|
||||||
block_number: U64::from(value.number),
|
|
||||||
gas_limit: U64::from(value.gas_limit),
|
|
||||||
gas_used: U64::from(value.gas_used),
|
|
||||||
timestamp: U64::from(value.timestamp),
|
|
||||||
extra_data: value.extra_data.clone(),
|
|
||||||
base_fee_per_gas: U256::from(value.base_fee_per_gas.unwrap_or_default()),
|
|
||||||
block_hash: value.hash(),
|
|
||||||
transactions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This structure maps on the ExecutionPayloadV2 structure of the beacon chain spec.
|
/// This structure maps on the ExecutionPayloadV2 structure of the beacon chain spec.
|
||||||
///
|
///
|
||||||
/// See also: <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#executionpayloadv2>
|
/// See also: <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#executionpayloadv2>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use crate::Log as RpcLog;
|
use crate::{eth::log::Log as RpcLog, BlockNumberOrTag, Log};
|
||||||
use alloy_primitives::{keccak256, Address, Bloom, BloomInput, B256, U256, U64};
|
use alloy_primitives::{keccak256, Address, Bloom, BloomInput, B256, U256, U64};
|
||||||
use itertools::{EitherOrBoth::*, Itertools};
|
use itertools::{EitherOrBoth::*, Itertools};
|
||||||
use reth_primitives::{BlockNumberOrTag, Log};
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{DeserializeOwned, MapAccess, Visitor},
|
de::{DeserializeOwned, MapAccess, Visitor},
|
||||||
ser::SerializeStruct,
|
ser::SerializeStruct,
|
||||||
@ -283,7 +282,7 @@ impl Filter {
|
|||||||
/// Match the latest block only
|
/// Match the latest block only
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use reth_primitives::BlockNumberOrTag;
|
/// # use reth_rpc_types::BlockNumberOrTag;
|
||||||
/// # use reth_rpc_types::Filter;
|
/// # use reth_rpc_types::Filter;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// let filter = Filter::new().select(BlockNumberOrTag::Latest);
|
/// let filter = Filter::new().select(BlockNumberOrTag::Latest);
|
||||||
|
|||||||
@ -10,6 +10,7 @@ mod filter;
|
|||||||
mod index;
|
mod index;
|
||||||
mod log;
|
mod log;
|
||||||
pub mod pubsub;
|
pub mod pubsub;
|
||||||
|
pub mod raw_log;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
mod syncing;
|
mod syncing;
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
@ -26,6 +27,7 @@ pub use fee::{FeeHistory, TxGasAndReward};
|
|||||||
pub use filter::*;
|
pub use filter::*;
|
||||||
pub use index::Index;
|
pub use index::Index;
|
||||||
pub use log::Log;
|
pub use log::Log;
|
||||||
|
pub use raw_log::{logs_bloom, Log as RawLog};
|
||||||
pub use syncing::*;
|
pub use syncing::*;
|
||||||
pub use transaction::*;
|
pub use transaction::*;
|
||||||
pub use withdrawal::Withdrawal;
|
pub use withdrawal::Withdrawal;
|
||||||
|
|||||||
30
crates/rpc/rpc-types/src/eth/raw_log.rs
Normal file
30
crates/rpc/rpc-types/src/eth/raw_log.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! Ethereum log object.
|
||||||
|
|
||||||
|
use alloy_primitives::{Address, Bloom, Bytes, B256};
|
||||||
|
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
||||||
|
|
||||||
|
/// Ethereum Log
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable, Default)]
|
||||||
|
pub struct Log {
|
||||||
|
/// Contract that emitted this log.
|
||||||
|
pub address: Address,
|
||||||
|
/// Topics of the log. The number of logs depend on what `LOG` opcode is used.
|
||||||
|
pub topics: Vec<B256>,
|
||||||
|
/// Arbitrary length data.
|
||||||
|
pub data: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate receipt logs bloom.
|
||||||
|
pub fn logs_bloom<'a, It>(logs: It) -> Bloom
|
||||||
|
where
|
||||||
|
It: IntoIterator<Item = &'a Log>,
|
||||||
|
{
|
||||||
|
let mut bloom = Bloom::ZERO;
|
||||||
|
for log in logs {
|
||||||
|
bloom.m3_2048(log.address.as_slice());
|
||||||
|
for topic in &log.topics {
|
||||||
|
bloom.m3_2048(topic.as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bloom
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
//! `trace_filter` types and support
|
//! `trace_filter` types and support
|
||||||
|
use crate::serde_helpers::num::u64_hex_or_decimal_opt;
|
||||||
use alloy_primitives::Address;
|
use alloy_primitives::Address;
|
||||||
use reth_primitives::serde_helper::num::u64_hex_or_decimal_opt;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
use crate::serde_helpers::num::from_int_or_hex;
|
||||||
use alloy_primitives::{Address, Bytes, B256, U256};
|
use alloy_primitives::{Address, Bytes, B256, U256};
|
||||||
use reth_primitives::serde_helper::num::from_int_or_hex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// The response object for `debug_traceTransaction` with `"tracer": "callTracer"`
|
/// The response object for `debug_traceTransaction` with `"tracer": "callTracer"`
|
||||||
|
|||||||
@ -45,7 +45,7 @@ pub struct BlockTraceResult {
|
|||||||
pub struct DefaultFrame {
|
pub struct DefaultFrame {
|
||||||
pub failed: bool,
|
pub failed: bool,
|
||||||
pub gas: u64,
|
pub gas: u64,
|
||||||
#[serde(serialize_with = "reth_primitives::serde_helper::serialize_hex_string_no_prefix")]
|
#[serde(serialize_with = "crate::serde_helpers::serialize_hex_string_no_prefix")]
|
||||||
pub return_value: Bytes,
|
pub return_value: Bytes,
|
||||||
pub struct_logs: Vec<StructLog>,
|
pub struct_logs: Vec<StructLog>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
use crate::serde_helpers::num::from_int_or_hex_opt;
|
||||||
use alloy_primitives::{Address, Bytes, B256, U256};
|
use alloy_primitives::{Address, Bytes, B256, U256};
|
||||||
use reth_primitives::serde_helper::num::from_int_or_hex_opt;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{btree_map, BTreeMap};
|
use std::collections::{btree_map, BTreeMap};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
//! Builder style functions for `trace_call`
|
//! Builder style functions for `trace_call`
|
||||||
|
|
||||||
use crate::{state::StateOverride, trace::parity::TraceType, BlockOverrides, CallRequest};
|
use crate::{
|
||||||
use reth_primitives::BlockId;
|
eth::block::BlockId, state::StateOverride, trace::parity::TraceType, BlockOverrides,
|
||||||
|
CallRequest,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
use crate::{CallRequest,state::StateOverride,BlockOverrides};
|
|
||||||
use std::{collections::HashSet};
|
|
||||||
use reth_primitives::{BlockId};
|
|
||||||
use reth_rpc::{types::tracerequest::parity::TraceType};
|
|
||||||
|
|
||||||
pub struct TraceRequest{
|
|
||||||
call: CallRequest,
|
|
||||||
trace_types: HashSet<TraceType>,
|
|
||||||
block_id: Option<BlockId>,
|
|
||||||
state_overrides: Option<StateOverride>,
|
|
||||||
block_overrides: Option<Box<BlockOverrides>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TraceRequest{
|
|
||||||
|
|
||||||
pub fn new(call:CallRequest,trace_types:HashSet<TraceType>) -> Self{
|
|
||||||
|
|
||||||
Self { call,trace_types,block_id:None, state_overrides: None, block_overrides:None }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_block_id(mut self, block_id: BlockId) -> Self{
|
|
||||||
self.block_id = Some(block_id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_state_override(mut self, state_overrides:StateOverride) -> Self{
|
|
||||||
self.state_overrides = Some(state_overrides);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_block_overrides(mut self, block_overrides:Box<BlockOverrides>) -> Self{
|
|
||||||
self.block_overrides = Some(block_overrides);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
use reth_primitives::{Address, B256, U256};
|
use alloy_primitives::{Address, B256, U256};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// A list of addresses and storage keys that the transaction plans to access.
|
/// A list of addresses and storage keys that the transaction plans to access.
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
use crate::eth::transaction::typed::{
|
use crate::eth::transaction::{
|
||||||
EIP1559TransactionRequest, EIP2930TransactionRequest, LegacyTransactionRequest,
|
typed::{
|
||||||
TransactionKind, TypedTransactionRequest,
|
EIP1559TransactionRequest, EIP2930TransactionRequest, LegacyTransactionRequest,
|
||||||
|
TransactionKind, TypedTransactionRequest,
|
||||||
|
},
|
||||||
|
AccessList,
|
||||||
};
|
};
|
||||||
use alloy_primitives::{Address, Bytes, U128, U256, U64, U8};
|
use alloy_primitives::{Address, Bytes, U128, U256, U64, U8};
|
||||||
use reth_primitives::AccessList;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Represents _all_ transaction requests received from RPC
|
/// Represents _all_ transaction requests received from RPC
|
||||||
|
|||||||
@ -3,13 +3,12 @@
|
|||||||
//! transaction deserialized from the json input of an RPC call. Depending on what fields are set,
|
//! transaction deserialized from the json input of an RPC call. Depending on what fields are set,
|
||||||
//! it can be converted into the container type [`TypedTransactionRequest`].
|
//! it can be converted into the container type [`TypedTransactionRequest`].
|
||||||
|
|
||||||
|
use crate::eth::transaction::AccessList;
|
||||||
use alloy_primitives::{Address, Bytes, B256, U128, U256, U64};
|
use alloy_primitives::{Address, Bytes, B256, U128, U256, U64};
|
||||||
use alloy_rlp::{BufMut, Decodable, Encodable, Error as RlpError, RlpDecodable, RlpEncodable};
|
use alloy_rlp::{BufMut, Decodable, Encodable, Error as RlpError};
|
||||||
use reth_primitives::{
|
use c_kzg::{Blob, Bytes48};
|
||||||
kzg::{Blob, Bytes48},
|
|
||||||
AccessList, Transaction, TxEip1559, TxEip2930, TxEip4844, TxLegacy,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Container type for various Ethereum transaction requests
|
/// Container type for various Ethereum transaction requests
|
||||||
///
|
///
|
||||||
/// Its variants correspond to specific allowed transactions:
|
/// Its variants correspond to specific allowed transactions:
|
||||||
@ -24,62 +23,6 @@ pub enum TypedTransactionRequest {
|
|||||||
EIP4844(Eip4844TransactionRequest),
|
EIP4844(Eip4844TransactionRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedTransactionRequest {
|
|
||||||
/// Converts a typed transaction request into a primitive transaction.
|
|
||||||
///
|
|
||||||
/// Returns `None` if any of the following are true:
|
|
||||||
/// - `nonce` is greater than [`u64::MAX`]
|
|
||||||
/// - `gas_limit` is greater than [`u64::MAX`]
|
|
||||||
/// - `value` is greater than [`u128::MAX`]
|
|
||||||
pub fn into_transaction(self) -> Option<Transaction> {
|
|
||||||
Some(match self {
|
|
||||||
TypedTransactionRequest::Legacy(tx) => Transaction::Legacy(TxLegacy {
|
|
||||||
chain_id: tx.chain_id,
|
|
||||||
nonce: tx.nonce.to(),
|
|
||||||
gas_price: tx.gas_price.to(),
|
|
||||||
gas_limit: tx.gas_limit.try_into().ok()?,
|
|
||||||
to: tx.kind.into(),
|
|
||||||
value: tx.value.into(),
|
|
||||||
input: tx.input,
|
|
||||||
}),
|
|
||||||
TypedTransactionRequest::EIP2930(tx) => Transaction::Eip2930(TxEip2930 {
|
|
||||||
chain_id: tx.chain_id,
|
|
||||||
nonce: tx.nonce.to(),
|
|
||||||
gas_price: tx.gas_price.to(),
|
|
||||||
gas_limit: tx.gas_limit.try_into().ok()?,
|
|
||||||
to: tx.kind.into(),
|
|
||||||
value: tx.value.into(),
|
|
||||||
input: tx.input,
|
|
||||||
access_list: tx.access_list,
|
|
||||||
}),
|
|
||||||
TypedTransactionRequest::EIP1559(tx) => Transaction::Eip1559(TxEip1559 {
|
|
||||||
chain_id: tx.chain_id,
|
|
||||||
nonce: tx.nonce.to(),
|
|
||||||
max_fee_per_gas: tx.max_fee_per_gas.to(),
|
|
||||||
gas_limit: tx.gas_limit.try_into().ok()?,
|
|
||||||
to: tx.kind.into(),
|
|
||||||
value: tx.value.into(),
|
|
||||||
input: tx.input,
|
|
||||||
access_list: tx.access_list,
|
|
||||||
max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(),
|
|
||||||
}),
|
|
||||||
TypedTransactionRequest::EIP4844(tx) => Transaction::Eip4844(TxEip4844 {
|
|
||||||
chain_id: tx.chain_id,
|
|
||||||
nonce: tx.nonce.to(),
|
|
||||||
gas_limit: tx.gas_limit.to(),
|
|
||||||
max_fee_per_gas: tx.max_fee_per_gas.to(),
|
|
||||||
max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(),
|
|
||||||
to: tx.kind.into(),
|
|
||||||
value: tx.value.into(),
|
|
||||||
access_list: tx.access_list,
|
|
||||||
blob_versioned_hashes: tx.blob_versioned_hashes,
|
|
||||||
max_fee_per_blob_gas: tx.max_fee_per_blob_gas.to(),
|
|
||||||
input: tx.input,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a legacy transaction request
|
/// Represents a legacy transaction request
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct LegacyTransactionRequest {
|
pub struct LegacyTransactionRequest {
|
||||||
@ -93,7 +36,7 @@ pub struct LegacyTransactionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an EIP-2930 transaction request
|
/// Represents an EIP-2930 transaction request
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct EIP2930TransactionRequest {
|
pub struct EIP2930TransactionRequest {
|
||||||
pub chain_id: u64,
|
pub chain_id: u64,
|
||||||
pub nonce: U64,
|
pub nonce: U64,
|
||||||
@ -106,7 +49,7 @@ pub struct EIP2930TransactionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an EIP-1559 transaction request
|
/// Represents an EIP-1559 transaction request
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct EIP1559TransactionRequest {
|
pub struct EIP1559TransactionRequest {
|
||||||
pub chain_id: u64,
|
pub chain_id: u64,
|
||||||
pub nonce: U64,
|
pub nonce: U64,
|
||||||
@ -191,15 +134,6 @@ impl Decodable for TransactionKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TransactionKind> for reth_primitives::TransactionKind {
|
|
||||||
fn from(kind: TransactionKind) -> Self {
|
|
||||||
match kind {
|
|
||||||
TransactionKind::Call(to) => reth_primitives::TransactionKind::Call(to),
|
|
||||||
TransactionKind::Create => reth_primitives::TransactionKind::Create,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This represents a set of blobs, and its corresponding commitments and proofs.
|
/// This represents a set of blobs, and its corresponding commitments and proofs.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
pub struct BlobTransactionSidecar {
|
pub struct BlobTransactionSidecar {
|
||||||
|
|||||||
@ -1,13 +1,20 @@
|
|||||||
//! Withdrawal type and serde helpers.
|
//! Withdrawal type and serde helpers.
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use crate::serde_helpers::u64_hex;
|
||||||
use alloy_primitives::{Address, U256};
|
use alloy_primitives::{Address, U256};
|
||||||
use alloy_rlp::RlpEncodable;
|
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
||||||
use reth_primitives::{constants::GWEI_TO_WEI, serde_helper::u64_hex};
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_with::{serde_as, DeserializeAs, DisplayFromStr, SerializeAs};
|
use serde_with::{serde_as, DeserializeAs, DisplayFromStr, SerializeAs};
|
||||||
|
|
||||||
|
/// Multiplier for converting gwei to wei.
|
||||||
|
pub const GWEI_TO_WEI: u64 = 1_000_000_000;
|
||||||
|
|
||||||
/// Withdrawal represents a validator withdrawal from the consensus layer.
|
/// Withdrawal represents a validator withdrawal from the consensus layer.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, RlpEncodable, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Default, Hash, RlpEncodable, RlpDecodable, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
pub struct Withdrawal {
|
pub struct Withdrawal {
|
||||||
/// Monotonically increasing identifier issued by consensus layer.
|
/// Monotonically increasing identifier issued by consensus layer.
|
||||||
#[serde(with = "u64_hex")]
|
#[serde(with = "u64_hex")]
|
||||||
@ -27,6 +34,12 @@ impl Withdrawal {
|
|||||||
pub fn amount_wei(&self) -> U256 {
|
pub fn amount_wei(&self) -> U256 {
|
||||||
U256::from(self.amount) * U256::from(GWEI_TO_WEI)
|
U256::from(self.amount) * U256::from(GWEI_TO_WEI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate a heuristic for the in-memory size of the [Withdrawal].
|
||||||
|
#[inline]
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
mem::size_of::<Self>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [Withdrawal] but respects the Beacon API format which uses snake-case and quoted
|
/// Same as [Withdrawal] but respects the Beacon API format which uses snake-case and quoted
|
||||||
|
|||||||
@ -14,11 +14,17 @@
|
|||||||
mod admin;
|
mod admin;
|
||||||
mod eth;
|
mod eth;
|
||||||
mod mev;
|
mod mev;
|
||||||
|
mod net;
|
||||||
mod otterscan;
|
mod otterscan;
|
||||||
|
mod peer;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
mod serde_helpers;
|
||||||
|
|
||||||
pub use admin::*;
|
pub use admin::*;
|
||||||
pub use eth::*;
|
pub use eth::*;
|
||||||
pub use mev::*;
|
pub use mev::*;
|
||||||
|
pub use net::*;
|
||||||
pub use otterscan::*;
|
pub use otterscan::*;
|
||||||
|
pub use peer::*;
|
||||||
pub use rpc::*;
|
pub use rpc::*;
|
||||||
|
pub use serde_helpers::*;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
//! MEV bundle type bindings
|
//! MEV bundle type bindings
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
use crate::{BlockId, BlockNumberOrTag, Log};
|
||||||
use alloy_primitives::{Address, Bytes, TxHash, B256, U256, U64};
|
use alloy_primitives::{Address, Bytes, TxHash, B256, U256, U64};
|
||||||
use reth_primitives::{BlockId, BlockNumberOrTag, Log};
|
|
||||||
use serde::{
|
use serde::{
|
||||||
ser::{SerializeSeq, Serializer},
|
ser::{SerializeSeq, Serializer},
|
||||||
Deserialize, Deserializer, Serialize,
|
Deserialize, Deserializer, Serialize,
|
||||||
|
|||||||
300
crates/rpc/rpc-types/src/net.rs
Normal file
300
crates/rpc/rpc-types/src/net.rs
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
use crate::PeerId;
|
||||||
|
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
||||||
|
use secp256k1::{SecretKey, SECP256K1};
|
||||||
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
fmt::Write,
|
||||||
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
|
num::ParseIntError,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
use url::{Host, Url};
|
||||||
|
|
||||||
|
/// Represents a ENR in discovery.
|
||||||
|
///
|
||||||
|
/// Note: this is only an excerpt of the [`NodeRecord`] data structure.
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
SerializeDisplay,
|
||||||
|
DeserializeFromStr,
|
||||||
|
RlpEncodable,
|
||||||
|
RlpDecodable,
|
||||||
|
)]
|
||||||
|
pub struct NodeRecord {
|
||||||
|
/// The Address of a node.
|
||||||
|
pub address: IpAddr,
|
||||||
|
/// TCP port of the port that accepts connections.
|
||||||
|
pub tcp_port: u16,
|
||||||
|
/// UDP discovery port.
|
||||||
|
pub udp_port: u16,
|
||||||
|
/// Public key of the discovery service
|
||||||
|
pub id: PeerId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeRecord {
|
||||||
|
/// Derive the [`NodeRecord`] from the secret key and addr
|
||||||
|
pub fn from_secret_key(addr: SocketAddr, sk: &SecretKey) -> Self {
|
||||||
|
let pk = secp256k1::PublicKey::from_secret_key(SECP256K1, sk);
|
||||||
|
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
|
||||||
|
Self::new(addr, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the `address` into an [`Ipv4Addr`] if the `address` is a mapped
|
||||||
|
/// [Ipv6Addr](std::net::Ipv6Addr).
|
||||||
|
///
|
||||||
|
/// Returns `true` if the address was converted.
|
||||||
|
///
|
||||||
|
/// See also [std::net::Ipv6Addr::to_ipv4_mapped]
|
||||||
|
pub fn convert_ipv4_mapped(&mut self) -> bool {
|
||||||
|
// convert IPv4 mapped IPv6 address
|
||||||
|
if let IpAddr::V6(v6) = self.address {
|
||||||
|
if let Some(v4) = v6.to_ipv4_mapped() {
|
||||||
|
self.address = v4.into();
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as [Self::convert_ipv4_mapped] but consumes the type
|
||||||
|
pub fn into_ipv4_mapped(mut self) -> Self {
|
||||||
|
self.convert_ipv4_mapped();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new record from a socket addr and peer id.
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn new(addr: SocketAddr, id: PeerId) -> Self {
|
||||||
|
Self { address: addr.ip(), tcp_port: addr.port(), udp_port: addr.port(), id }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The TCP socket address of this node
|
||||||
|
#[must_use]
|
||||||
|
pub fn tcp_addr(&self) -> SocketAddr {
|
||||||
|
SocketAddr::new(self.address, self.tcp_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The UDP socket address of this node
|
||||||
|
#[must_use]
|
||||||
|
pub fn udp_addr(&self) -> SocketAddr {
|
||||||
|
SocketAddr::new(self.address, self.udp_port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NodeRecord {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("enode://")?;
|
||||||
|
alloy_primitives::hex::encode(self.id.as_slice()).fmt(f)?;
|
||||||
|
f.write_char('@')?;
|
||||||
|
match self.address {
|
||||||
|
IpAddr::V4(ip) => {
|
||||||
|
ip.fmt(f)?;
|
||||||
|
}
|
||||||
|
IpAddr::V6(ip) => {
|
||||||
|
// encapsulate with brackets
|
||||||
|
f.write_char('[')?;
|
||||||
|
ip.fmt(f)?;
|
||||||
|
f.write_char(']')?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write_char(':')?;
|
||||||
|
self.tcp_port.fmt(f)?;
|
||||||
|
if self.tcp_port != self.udp_port {
|
||||||
|
f.write_str("?discport=")?;
|
||||||
|
self.udp_port.fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible error types when parsing a `NodeRecord`
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum NodeRecordParseError {
|
||||||
|
/// Invalid url
|
||||||
|
#[error("Failed to parse url: {0}")]
|
||||||
|
InvalidUrl(String),
|
||||||
|
/// Invalid id
|
||||||
|
#[error("Failed to parse id")]
|
||||||
|
InvalidId(String),
|
||||||
|
/// Invalid discport
|
||||||
|
#[error("Failed to discport query: {0}")]
|
||||||
|
Discport(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for NodeRecord {
|
||||||
|
type Err = NodeRecordParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let url = Url::parse(s).map_err(|e| NodeRecordParseError::InvalidUrl(e.to_string()))?;
|
||||||
|
|
||||||
|
let address = match url.host() {
|
||||||
|
Some(Host::Ipv4(ip)) => IpAddr::V4(ip),
|
||||||
|
Some(Host::Ipv6(ip)) => IpAddr::V6(ip),
|
||||||
|
Some(Host::Domain(ip)) => IpAddr::V4(
|
||||||
|
Ipv4Addr::from_str(ip)
|
||||||
|
.map_err(|e| NodeRecordParseError::InvalidUrl(e.to_string()))?,
|
||||||
|
),
|
||||||
|
_ => return Err(NodeRecordParseError::InvalidUrl(format!("invalid host: {url:?}"))),
|
||||||
|
};
|
||||||
|
let port = url
|
||||||
|
.port()
|
||||||
|
.ok_or_else(|| NodeRecordParseError::InvalidUrl("no port specified".to_string()))?;
|
||||||
|
|
||||||
|
let udp_port = if let Some(discovery_port) = url
|
||||||
|
.query_pairs()
|
||||||
|
.find_map(|(maybe_disc, port)| (maybe_disc.as_ref() == "discport").then_some(port))
|
||||||
|
{
|
||||||
|
discovery_port.parse::<u16>().map_err(NodeRecordParseError::Discport)?
|
||||||
|
} else {
|
||||||
|
port
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = url
|
||||||
|
.username()
|
||||||
|
.parse::<PeerId>()
|
||||||
|
.map_err(|e| NodeRecordParseError::InvalidId(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(Self { address, id, tcp_port: port, udp_port })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use alloy_rlp::{Decodable, Encodable};
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use rand::{thread_rng, Rng, RngCore};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mapped_ipv6() {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
let v4: Ipv4Addr = "0.0.0.0".parse().unwrap();
|
||||||
|
let v6 = v4.to_ipv6_mapped();
|
||||||
|
|
||||||
|
let record = NodeRecord {
|
||||||
|
address: v6.into(),
|
||||||
|
tcp_port: rng.gen(),
|
||||||
|
udp_port: rng.gen(),
|
||||||
|
id: rng.gen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(record.clone().convert_ipv4_mapped());
|
||||||
|
assert_eq!(record.into_ipv4_mapped().address, IpAddr::from(v4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mapped_ipv4() {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let v4: Ipv4Addr = "0.0.0.0".parse().unwrap();
|
||||||
|
|
||||||
|
let record = NodeRecord {
|
||||||
|
address: v4.into(),
|
||||||
|
tcp_port: rng.gen(),
|
||||||
|
udp_port: rng.gen(),
|
||||||
|
id: rng.gen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(!record.clone().convert_ipv4_mapped());
|
||||||
|
assert_eq!(record.into_ipv4_mapped().address, IpAddr::from(v4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_noderecord_codec_ipv4() {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
for _ in 0..100 {
|
||||||
|
let mut ip = [0u8; 4];
|
||||||
|
rng.fill_bytes(&mut ip);
|
||||||
|
let record = NodeRecord {
|
||||||
|
address: IpAddr::V4(ip.into()),
|
||||||
|
tcp_port: rng.gen(),
|
||||||
|
udp_port: rng.gen(),
|
||||||
|
id: rng.gen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
record.encode(&mut buf);
|
||||||
|
|
||||||
|
let decoded = NodeRecord::decode(&mut buf.as_ref()).unwrap();
|
||||||
|
assert_eq!(record, decoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_noderecord_codec_ipv6() {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
for _ in 0..100 {
|
||||||
|
let mut ip = [0u8; 16];
|
||||||
|
rng.fill_bytes(&mut ip);
|
||||||
|
let record = NodeRecord {
|
||||||
|
address: IpAddr::V6(ip.into()),
|
||||||
|
tcp_port: rng.gen(),
|
||||||
|
udp_port: rng.gen(),
|
||||||
|
id: rng.gen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
record.encode(&mut buf);
|
||||||
|
|
||||||
|
let decoded = NodeRecord::decode(&mut buf.as_ref()).unwrap();
|
||||||
|
assert_eq!(record, decoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_url_parse() {
|
||||||
|
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
|
||||||
|
let node: NodeRecord = url.parse().unwrap();
|
||||||
|
assert_eq!(node, NodeRecord {
|
||||||
|
address: IpAddr::V4([10,3,58,6].into()),
|
||||||
|
tcp_port: 30303,
|
||||||
|
udp_port: 30301,
|
||||||
|
id: "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_node_display() {
|
||||||
|
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303";
|
||||||
|
let node: NodeRecord = url.parse().unwrap();
|
||||||
|
assert_eq!(url, &format!("{node}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_node_display_discport() {
|
||||||
|
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
|
||||||
|
let node: NodeRecord = url.parse().unwrap();
|
||||||
|
assert_eq!(url, &format!("{node}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_node_serialize() {
|
||||||
|
let node = NodeRecord{
|
||||||
|
address: IpAddr::V4([10, 3, 58, 6].into()),
|
||||||
|
tcp_port: 30303u16,
|
||||||
|
udp_port: 30301u16,
|
||||||
|
id: PeerId::from_str("6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0").unwrap(),
|
||||||
|
};
|
||||||
|
let ser = serde_json::to_string::<NodeRecord>(&node).expect("couldn't serialize");
|
||||||
|
assert_eq!(ser, "\"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_node_deserialize() {
|
||||||
|
let url = "\"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301\"";
|
||||||
|
let node: NodeRecord = serde_json::from_str(url).expect("couldn't deserialize");
|
||||||
|
assert_eq!(node, NodeRecord{
|
||||||
|
address: IpAddr::V4([10, 3, 58, 6].into()),
|
||||||
|
tcp_port: 30303u16,
|
||||||
|
udp_port: 30301u16,
|
||||||
|
id: PeerId::from_str("6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0").unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
4
crates/rpc/rpc-types/src/peer.rs
Normal file
4
crates/rpc/rpc-types/src/peer.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
use alloy_primitives::B512;
|
||||||
|
|
||||||
|
/// Alias for a peer identifier
|
||||||
|
pub type PeerId = B512;
|
||||||
93
crates/rpc/rpc-types/src/serde_helpers/json_u256.rs
Normal file
93
crates/rpc/rpc-types/src/serde_helpers/json_u256.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
//! Json U256 serde helpers.
|
||||||
|
|
||||||
|
use alloy_primitives::U256;
|
||||||
|
use serde::{
|
||||||
|
de::{Error, Visitor},
|
||||||
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
/// Wrapper around primitive U256 type that also supports deserializing numbers
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
pub struct JsonU256(pub U256);
|
||||||
|
|
||||||
|
impl From<JsonU256> for U256 {
|
||||||
|
fn from(value: JsonU256) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<U256> for JsonU256 {
|
||||||
|
fn from(value: U256) -> Self {
|
||||||
|
JsonU256(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for JsonU256 {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.0.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deserialize<'a> for JsonU256 {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'a>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_any(JsonU256Visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JsonU256Visitor;
|
||||||
|
|
||||||
|
impl<'a> Visitor<'a> for JsonU256Visitor {
|
||||||
|
type Value = JsonU256;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(formatter, "a hex encoding or decimal number")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
Ok(JsonU256(U256::from(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
let value = match value.len() {
|
||||||
|
0 => U256::ZERO,
|
||||||
|
2 if value.starts_with("0x") => U256::ZERO,
|
||||||
|
_ if value.starts_with("0x") => U256::from_str(value).map_err(|e| {
|
||||||
|
Error::custom(format!("Parsing JsonU256 as hex failed {value}: {e}"))
|
||||||
|
})?,
|
||||||
|
_ => U256::from_str_radix(value, 10).map_err(|e| {
|
||||||
|
Error::custom(format!("Parsing JsonU256 as decimal failed {value}: {e:?}"))
|
||||||
|
})?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(JsonU256(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: Error,
|
||||||
|
{
|
||||||
|
self.visit_str(value.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supports parsing `U256` numbers as strings via [JsonU256]
|
||||||
|
pub fn deserialize_json_u256<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let num = JsonU256::deserialize(deserializer)?;
|
||||||
|
Ok(num.into())
|
||||||
|
}
|
||||||
30
crates/rpc/rpc-types/src/serde_helpers/mod.rs
Normal file
30
crates/rpc/rpc-types/src/serde_helpers/mod.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! Serde helpers for primitive types.
|
||||||
|
|
||||||
|
use alloy_primitives::U256;
|
||||||
|
use serde::{Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
pub mod json_u256;
|
||||||
|
pub mod num;
|
||||||
|
/// Storage related helpers.
|
||||||
|
pub mod storage;
|
||||||
|
pub mod u64_hex;
|
||||||
|
|
||||||
|
/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with
|
||||||
|
/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number).
|
||||||
|
pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
num::NumberOrHexU256::deserialize(deserializer)?.try_into_u256()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a byte vec as a hex string _without_ the "0x" prefix.
|
||||||
|
///
|
||||||
|
/// This behaves the same as [`hex::encode`](alloy_primitives::hex::encode).
|
||||||
|
pub fn serialize_hex_string_no_prefix<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
s.serialize_str(&alloy_primitives::hex::encode(x.as_ref()))
|
||||||
|
}
|
||||||
139
crates/rpc/rpc-types/src/serde_helpers/num.rs
Normal file
139
crates/rpc/rpc-types/src/serde_helpers/num.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
//! Numeric serde helpers.
|
||||||
|
|
||||||
|
use alloy_primitives::{U256, U64};
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// A `u64` wrapper type that deserializes from hex or a u64 and serializes as hex.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use reth_rpc_types::num::U64HexOrNumber;
|
||||||
|
/// let number_json = "100";
|
||||||
|
/// let hex_json = "\"0x64\"";
|
||||||
|
///
|
||||||
|
/// let number: U64HexOrNumber = serde_json::from_str(number_json).unwrap();
|
||||||
|
/// let hex: U64HexOrNumber = serde_json::from_str(hex_json).unwrap();
|
||||||
|
/// assert_eq!(number, hex);
|
||||||
|
/// assert_eq!(hex.to(), 100);
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
|
||||||
|
pub struct U64HexOrNumber(U64);
|
||||||
|
|
||||||
|
impl U64HexOrNumber {
|
||||||
|
/// Returns the wrapped u64
|
||||||
|
pub fn to(self) -> u64 {
|
||||||
|
self.0.to()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for U64HexOrNumber {
|
||||||
|
fn from(value: u64) -> Self {
|
||||||
|
Self(U64::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<U64> for U64HexOrNumber {
|
||||||
|
fn from(value: U64) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<U64HexOrNumber> for u64 {
|
||||||
|
fn from(value: U64HexOrNumber) -> Self {
|
||||||
|
value.to()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<U64HexOrNumber> for U64 {
|
||||||
|
fn from(value: U64HexOrNumber) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for U64HexOrNumber {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum NumberOrHexU64 {
|
||||||
|
Hex(U64),
|
||||||
|
Int(u64),
|
||||||
|
}
|
||||||
|
match NumberOrHexU64::deserialize(deserializer)? {
|
||||||
|
NumberOrHexU64::Int(val) => Ok(val.into()),
|
||||||
|
NumberOrHexU64::Hex(val) => Ok(val.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// serde functions for handling primitive optional `u64` as [U64]
|
||||||
|
pub mod u64_hex_or_decimal_opt {
|
||||||
|
use crate::serde_helpers::num::U64HexOrNumber;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
/// Deserializes an `u64` accepting a hex quantity string with optional 0x prefix or
|
||||||
|
/// a number
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
match Option::<U64HexOrNumber>::deserialize(deserializer)? {
|
||||||
|
Some(val) => Ok(Some(val.into())),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializes u64 as hex string
|
||||||
|
pub fn serialize<S: Serializer>(value: &Option<u64>, s: S) -> Result<S::Ok, S::Error> {
|
||||||
|
match value {
|
||||||
|
Some(val) => U64HexOrNumber::from(*val).serialize(s),
|
||||||
|
None => s.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes the input into an `Option<U256>`, using [`from_int_or_hex`] to deserialize the
|
||||||
|
/// inner value.
|
||||||
|
pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result<Option<U256>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
match Option::<NumberOrHexU256>::deserialize(deserializer)? {
|
||||||
|
Some(val) => val.try_into_u256().map(Some),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An enum that represents either a [serde_json::Number] integer, or a hex [U256].
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum NumberOrHexU256 {
|
||||||
|
/// An integer
|
||||||
|
Int(serde_json::Number),
|
||||||
|
/// A hex U256
|
||||||
|
Hex(U256),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NumberOrHexU256 {
|
||||||
|
/// Tries to convert this into a [U256]].
|
||||||
|
pub fn try_into_u256<E: de::Error>(self) -> Result<U256, E> {
|
||||||
|
match self {
|
||||||
|
NumberOrHexU256::Int(num) => {
|
||||||
|
U256::from_str(num.to_string().as_str()).map_err(E::custom)
|
||||||
|
}
|
||||||
|
NumberOrHexU256::Hex(val) => Ok(val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with
|
||||||
|
/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number).
|
||||||
|
pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
NumberOrHexU256::deserialize(deserializer)?.try_into_u256()
|
||||||
|
}
|
||||||
102
crates/rpc/rpc-types/src/serde_helpers/storage.rs
Normal file
102
crates/rpc/rpc-types/src/serde_helpers/storage.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use alloy_primitives::{Bytes, B256, U256};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
use std::{collections::HashMap, fmt::Write};
|
||||||
|
|
||||||
|
/// A storage key type that can be serialized to and from a hex string up to 32 bytes. Used for
|
||||||
|
/// `eth_getStorageAt` and `eth_getProof` RPCs.
|
||||||
|
///
|
||||||
|
/// This is a wrapper type meant to mirror geth's serialization and deserialization behavior for
|
||||||
|
/// storage keys.
|
||||||
|
///
|
||||||
|
/// In `eth_getStorageAt`, this is used for deserialization of the `index` field. Internally, the
|
||||||
|
/// index is a [B256], but in `eth_getStorageAt` requests, its serialization can be _up to_ 32
|
||||||
|
/// bytes. To support this, the storage key is deserialized first as a U256, and converted to a
|
||||||
|
/// B256 for use internally.
|
||||||
|
///
|
||||||
|
/// `eth_getProof` also takes storage keys up to 32 bytes as input, so the `keys` field is
|
||||||
|
/// similarly deserialized. However, geth populates the storage proof `key` fields in the response
|
||||||
|
/// by mirroring the `key` field used in the input.
|
||||||
|
/// * See how `storageKey`s (the input) are populated in the `StorageResult` (the output):
|
||||||
|
/// <https://github.com/ethereum/go-ethereum/blob/00a73fbcce3250b87fc4160f3deddc44390848f4/internal/ethapi/api.go#L658-L690>
|
||||||
|
///
|
||||||
|
/// The contained [B256] and From implementation for String are used to preserve the input and
|
||||||
|
/// implement this behavior from geth.
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(from = "U256", into = "String")]
|
||||||
|
pub struct JsonStorageKey(pub B256);
|
||||||
|
|
||||||
|
impl From<U256> for JsonStorageKey {
|
||||||
|
fn from(value: U256) -> Self {
|
||||||
|
// SAFETY: Address (B256) and U256 have the same number of bytes
|
||||||
|
JsonStorageKey(B256::from(value.to_be_bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JsonStorageKey> for String {
|
||||||
|
fn from(value: JsonStorageKey) -> Self {
|
||||||
|
// SAFETY: Address (B256) and U256 have the same number of bytes
|
||||||
|
let uint = U256::from_be_bytes(value.0 .0);
|
||||||
|
|
||||||
|
// serialize byte by byte
|
||||||
|
//
|
||||||
|
// this is mainly so we can return an output that hive testing expects, because the
|
||||||
|
// `eth_getProof` implementation in geth simply mirrors the input
|
||||||
|
//
|
||||||
|
// see the use of `hexKey` in the `eth_getProof` response:
|
||||||
|
// <https://github.com/ethereum/go-ethereum/blob/00a73fbcce3250b87fc4160f3deddc44390848f4/internal/ethapi/api.go#L658-L690>
|
||||||
|
let bytes = uint.to_be_bytes_trimmed_vec();
|
||||||
|
let mut hex = String::with_capacity(2 + bytes.len() * 2);
|
||||||
|
hex.push_str("0x");
|
||||||
|
for byte in bytes {
|
||||||
|
write!(hex, "{:02x}", byte).unwrap();
|
||||||
|
}
|
||||||
|
hex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a Bytes value into a B256, accepting inputs that are less than 32 bytes long. These
|
||||||
|
/// inputs will be left padded with zeros.
|
||||||
|
pub fn from_bytes_to_b256<'de, D>(bytes: Bytes) -> Result<B256, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
if bytes.0.len() > 32 {
|
||||||
|
return Err(serde::de::Error::custom("input too long to be a B256"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// left pad with zeros to 32 bytes
|
||||||
|
let mut padded = [0u8; 32];
|
||||||
|
padded[32 - bytes.0.len()..].copy_from_slice(&bytes.0);
|
||||||
|
|
||||||
|
// then convert to B256 without a panic
|
||||||
|
Ok(B256::from_slice(&padded))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes the input into an Option<HashMap<B256, B256>>, using [from_bytes_to_b256] which
|
||||||
|
/// allows cropped values:
|
||||||
|
///
|
||||||
|
/// ```json
|
||||||
|
/// {
|
||||||
|
/// "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn deserialize_storage_map<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<Option<HashMap<B256, B256>>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let map = Option::<HashMap<Bytes, Bytes>>::deserialize(deserializer)?;
|
||||||
|
match map {
|
||||||
|
Some(mut map) => {
|
||||||
|
let mut res_map = HashMap::with_capacity(map.len());
|
||||||
|
for (k, v) in map.drain() {
|
||||||
|
let k_deserialized = from_bytes_to_b256::<'de, D>(k)?;
|
||||||
|
let v_deserialized = from_bytes_to_b256::<'de, D>(v)?;
|
||||||
|
res_map.insert(k_deserialized, v_deserialized);
|
||||||
|
}
|
||||||
|
Ok(Some(res_map))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
18
crates/rpc/rpc-types/src/serde_helpers/u64_hex.rs
Normal file
18
crates/rpc/rpc-types/src/serde_helpers/u64_hex.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//! Helper to deserialize an `u64` from [U64] accepting a hex quantity string with optional 0x
|
||||||
|
//! prefix
|
||||||
|
|
||||||
|
use alloy_primitives::U64;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
/// Deserializes an `u64` from [U64] accepting a hex quantity string with optional 0x prefix
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
U64::deserialize(deserializer).map(|val| val.to())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializes u64 as hex string
|
||||||
|
pub fn serialize<S: Serializer>(value: &u64, s: S) -> Result<S::Ok, S::Error> {
|
||||||
|
U64::from(*value).serialize(s)
|
||||||
|
}
|
||||||
@ -354,7 +354,8 @@ where
|
|||||||
let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
|
let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
|
||||||
let transaction_index = transaction_index.unwrap_or_default();
|
let transaction_index = transaction_index.unwrap_or_default();
|
||||||
|
|
||||||
let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
let target_block = block_number
|
||||||
|
.unwrap_or(reth_rpc_types::BlockId::Number(reth_rpc_types::BlockNumberOrTag::Latest));
|
||||||
let ((cfg, block_env, _), block) = futures::try_join!(
|
let ((cfg, block_env, _), block) = futures::try_join!(
|
||||||
self.inner.eth_api.evm_env_at(target_block),
|
self.inner.eth_api.evm_env_at(target_block),
|
||||||
self.inner.eth_api.block_by_id(target_block),
|
self.inner.eth_api.block_by_id(target_block),
|
||||||
|
|||||||
@ -21,7 +21,7 @@ use reth_rpc_types::{
|
|||||||
state::StateOverride, AccessListWithGasUsed, BlockError, Bundle, CallRequest, EthCallResponse,
|
state::StateOverride, AccessListWithGasUsed, BlockError, Bundle, CallRequest, EthCallResponse,
|
||||||
StateContext,
|
StateContext,
|
||||||
};
|
};
|
||||||
use reth_rpc_types_compat::log::from_primitive_access_list;
|
use reth_rpc_types_compat::log::{from_primitive_access_list, to_primitive_access_list};
|
||||||
use reth_transaction_pool::TransactionPool;
|
use reth_transaction_pool::TransactionPool;
|
||||||
use revm::{
|
use revm::{
|
||||||
db::{CacheDB, DatabaseRef},
|
db::{CacheDB, DatabaseRef},
|
||||||
@ -87,6 +87,7 @@ where
|
|||||||
let transaction_index = transaction_index.unwrap_or_default();
|
let transaction_index = transaction_index.unwrap_or_default();
|
||||||
|
|
||||||
let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
||||||
|
|
||||||
let ((cfg, block_env, _), block) =
|
let ((cfg, block_env, _), block) =
|
||||||
futures::try_join!(self.evm_env_at(target_block), self.block_by_id(target_block))?;
|
futures::try_join!(self.evm_env_at(target_block), self.block_by_id(target_block))?;
|
||||||
|
|
||||||
@ -386,7 +387,8 @@ where
|
|||||||
let initial = request.access_list.take().unwrap_or_default();
|
let initial = request.access_list.take().unwrap_or_default();
|
||||||
|
|
||||||
let precompiles = get_precompiles(env.cfg.spec_id);
|
let precompiles = get_precompiles(env.cfg.spec_id);
|
||||||
let mut inspector = AccessListInspector::new(initial, from, to, precompiles);
|
let mut inspector =
|
||||||
|
AccessListInspector::new(to_primitive_access_list(initial), from, to, precompiles);
|
||||||
let (result, env) = inspect(&mut db, env, &mut inspector)?;
|
let (result, env) = inspect(&mut db, env, &mut inspector)?;
|
||||||
|
|
||||||
match result.result {
|
match result.result {
|
||||||
@ -403,7 +405,7 @@ where
|
|||||||
let access_list = inspector.into_access_list();
|
let access_list = inspector.into_access_list();
|
||||||
|
|
||||||
// calculate the gas used using the access list
|
// calculate the gas used using the access list
|
||||||
request.access_list = Some(access_list.clone());
|
request.access_list = Some(from_primitive_access_list(access_list.clone()));
|
||||||
let gas_used = self.estimate_gas_with(env.cfg, env.block, request, db.db.state())?;
|
let gas_used = self.estimate_gas_with(env.cfg, env.block, request, db.db.state())?;
|
||||||
|
|
||||||
Ok(AccessListWithGasUsed { access_list: from_primitive_access_list(access_list), gas_used })
|
Ok(AccessListWithGasUsed { access_list: from_primitive_access_list(access_list), gas_used })
|
||||||
|
|||||||
@ -55,9 +55,8 @@ where
|
|||||||
|
|
||||||
let transactions =
|
let transactions =
|
||||||
txs.into_iter().map(recover_raw_transaction).collect::<Result<Vec<_>, _>>()?;
|
txs.into_iter().map(recover_raw_transaction).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let block_id: reth_rpc_types::BlockId = state_block_number.into();
|
||||||
let (cfg, mut block_env, at) =
|
let (cfg, mut block_env, at) = self.inner.eth_api.evm_env_at(block_id).await?;
|
||||||
self.inner.eth_api.evm_env_at(state_block_number.into()).await?;
|
|
||||||
|
|
||||||
// need to adjust the timestamp for the next block
|
// need to adjust the timestamp for the next block
|
||||||
if let Some(timestamp) = timestamp {
|
if let Some(timestamp) = timestamp {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use reth_primitives::{BlockNumHash, ChainInfo, Receipt, TxHash, U256};
|
use reth_primitives::{BlockNumHash, ChainInfo, Receipt, TxHash, U256};
|
||||||
use reth_rpc_types::{FilteredParams, Log};
|
use reth_rpc_types::{FilteredParams, Log};
|
||||||
|
use reth_rpc_types_compat::log::from_primitive_log;
|
||||||
|
|
||||||
/// Returns all matching logs of a block's receipts grouped with the hash of their transaction.
|
/// Returns all matching logs of a block's receipts grouped with the hash of their transaction.
|
||||||
pub(crate) fn matching_block_logs<I>(
|
pub(crate) fn matching_block_logs<I>(
|
||||||
@ -60,8 +61,8 @@ pub(crate) fn log_matches_filter(
|
|||||||
if params.filter.is_some() &&
|
if params.filter.is_some() &&
|
||||||
(!params.filter_block_range(block.number) ||
|
(!params.filter_block_range(block.number) ||
|
||||||
!params.filter_block_hash(block.hash) ||
|
!params.filter_block_hash(block.hash) ||
|
||||||
!params.filter_address(log) ||
|
!params.filter_address(&from_primitive_log(log.clone())) ||
|
||||||
!params.filter_topics(log))
|
!params.filter_topics(&from_primitive_log(log.clone())))
|
||||||
{
|
{
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -97,7 +98,7 @@ pub(crate) fn get_filter_block_range(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use reth_primitives::BlockNumberOrTag;
|
|
||||||
use reth_rpc_types::Filter;
|
use reth_rpc_types::Filter;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -159,8 +160,8 @@ mod tests {
|
|||||||
let start_block = info.best_number;
|
let start_block = info.best_number;
|
||||||
|
|
||||||
let (from_block_number, to_block_number) = get_filter_block_range(
|
let (from_block_number, to_block_number) = get_filter_block_range(
|
||||||
from_block.and_then(BlockNumberOrTag::as_number),
|
from_block.and_then(reth_rpc_types::BlockNumberOrTag::as_number),
|
||||||
to_block.and_then(BlockNumberOrTag::as_number),
|
to_block.and_then(reth_rpc_types::BlockNumberOrTag::as_number),
|
||||||
start_block,
|
start_block,
|
||||||
info,
|
info,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -294,7 +294,9 @@ where
|
|||||||
.committed()
|
.committed()
|
||||||
.map(|chain| chain.headers().collect::<Vec<_>>())
|
.map(|chain| chain.headers().collect::<Vec<_>>())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
futures::stream::iter(headers.into_iter().map(Header::from_primitive_with_hash))
|
futures::stream::iter(
|
||||||
|
headers.into_iter().map(reth_rpc_types_compat::block::from_primitive_with_hash),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
use crate::eth::error::{EthApiError, EthResult, RpcInvalidTransactionError};
|
use crate::eth::error::{EthApiError, EthResult, RpcInvalidTransactionError};
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
revm::env::{fill_tx_env, fill_tx_env_with_recovered},
|
revm::env::{fill_tx_env, fill_tx_env_with_recovered},
|
||||||
AccessList, Address, TransactionSigned, TransactionSignedEcRecovered, TxHash, B256, U256,
|
Address, TransactionSigned, TransactionSignedEcRecovered, TxHash, B256, U256,
|
||||||
};
|
};
|
||||||
use reth_rpc_types::{
|
use reth_rpc_types::{
|
||||||
state::{AccountOverride, StateOverride},
|
state::{AccountOverride, StateOverride},
|
||||||
@ -309,7 +309,9 @@ pub(crate) fn create_txn_env(block_env: &BlockEnv, request: CallRequest) -> EthR
|
|||||||
value: value.unwrap_or_default(),
|
value: value.unwrap_or_default(),
|
||||||
data: input.try_into_unique_input()?.unwrap_or_default(),
|
data: input.try_into_unique_input()?.unwrap_or_default(),
|
||||||
chain_id: chain_id.map(|c| c.to()),
|
chain_id: chain_id.map(|c| c.to()),
|
||||||
access_list: access_list.map(AccessList::into_flattened).unwrap_or_default(),
|
access_list: access_list
|
||||||
|
.map(reth_rpc_types::AccessList::into_flattened)
|
||||||
|
.unwrap_or_default(),
|
||||||
// EIP-4844 fields
|
// EIP-4844 fields
|
||||||
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
|
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
|
||||||
max_fee_per_blob_gas,
|
max_fee_per_blob_gas,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use reth_primitives::{
|
|||||||
};
|
};
|
||||||
use reth_rpc_types::TypedTransactionRequest;
|
use reth_rpc_types::TypedTransactionRequest;
|
||||||
|
|
||||||
|
use reth_rpc_types_compat::transaction::to_primitive_transaction;
|
||||||
use secp256k1::SecretKey;
|
use secp256k1::SecretKey;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -78,7 +79,8 @@ impl EthSigner for DevSigner {
|
|||||||
address: &Address,
|
address: &Address,
|
||||||
) -> Result<TransactionSigned> {
|
) -> Result<TransactionSigned> {
|
||||||
// convert to primitive transaction
|
// convert to primitive transaction
|
||||||
let transaction = request.into_transaction().ok_or(SignError::InvalidTransactionRequest)?;
|
let transaction =
|
||||||
|
to_primitive_transaction(request).ok_or(SignError::InvalidTransactionRequest)?;
|
||||||
let tx_signature_hash = transaction.signature_hash();
|
let tx_signature_hash = transaction.signature_hash();
|
||||||
let signature = self.sign_hash(tx_signature_hash, *address)?;
|
let signature = self.sign_hash(tx_signature_hash, *address)?;
|
||||||
|
|
||||||
|
|||||||
@ -66,7 +66,9 @@ where
|
|||||||
{
|
{
|
||||||
/// Executes the given call and returns a number of possible traces for it.
|
/// Executes the given call and returns a number of possible traces for it.
|
||||||
pub async fn trace_call(&self, trace_request: TraceRequest) -> EthResult<TraceResults> {
|
pub async fn trace_call(&self, trace_request: TraceRequest) -> EthResult<TraceResults> {
|
||||||
let at = trace_request.block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
let at = trace_request
|
||||||
|
.block_id
|
||||||
|
.unwrap_or(reth_rpc_types::BlockId::Number(reth_rpc_types::BlockNumberOrTag::Latest));
|
||||||
let config = tracing_config(&trace_request.trace_types);
|
let config = tracing_config(&trace_request.trace_types);
|
||||||
let overrides =
|
let overrides =
|
||||||
EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
|
EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
|
||||||
|
|||||||
@ -13,6 +13,7 @@ reth-provider.workspace = true
|
|||||||
|
|
||||||
reth-rpc-builder.workspace = true
|
reth-rpc-builder.workspace = true
|
||||||
reth-rpc-types.workspace = true
|
reth-rpc-types.workspace = true
|
||||||
|
reth-rpc-types-compat.workspace = true
|
||||||
|
|
||||||
reth-revm.workspace = true
|
reth-revm.workspace = true
|
||||||
reth-blockchain-tree.workspace = true
|
reth-blockchain-tree.workspace = true
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use reth_provider::{
|
|||||||
StateProvider, TransactionsProvider,
|
StateProvider, TransactionsProvider,
|
||||||
};
|
};
|
||||||
use reth_rpc_types::{Filter, FilteredParams};
|
use reth_rpc_types::{Filter, FilteredParams};
|
||||||
|
use reth_rpc_types_compat::log::from_primitive_log;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@ -197,7 +198,8 @@ fn receipts_provider_example<T: ReceiptProvider + TransactionsProvider + HeaderP
|
|||||||
{
|
{
|
||||||
let receipts = provider.receipt(header_num)?.ok_or(eyre::eyre!("receipt not found"))?;
|
let receipts = provider.receipt(header_num)?.ok_or(eyre::eyre!("receipt not found"))?;
|
||||||
for log in &receipts.logs {
|
for log in &receipts.logs {
|
||||||
if filter_params.filter_address(log) && filter_params.filter_topics(log) {
|
let log = from_primitive_log(log.clone());
|
||||||
|
if filter_params.filter_address(&log) && filter_params.filter_topics(&log) {
|
||||||
// Do something with the log e.g. decode it.
|
// Do something with the log e.g. decode it.
|
||||||
println!("Matching log found! {log:?}")
|
println!("Matching log found! {log:?}")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user