From 4a1ccc3f56802615e91b43c2e89dd4657b53e5bf Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 13:28:16 +0200 Subject: [PATCH 1/9] 243: Move BlockTag to request --- evm_rpc_types/src/lib.rs | 13 +------------ evm_rpc_types/src/request/mod.rs | 13 ++++++++++++- src/candid_rpc/cketh_conversion.rs | 3 ++- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/evm_rpc_types/src/lib.rs b/evm_rpc_types/src/lib.rs index 98064d1e..094c5122 100644 --- a/evm_rpc_types/src/lib.rs +++ b/evm_rpc_types/src/lib.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::Formatter; use std::str::FromStr; -pub use request::{FeeHistoryArgs, GetLogsArgs, GetTransactionCountArgs}; +pub use request::{BlockTag, FeeHistoryArgs, GetLogsArgs, GetTransactionCountArgs}; pub use response::{Block, FeeHistory, LogEntry, SendRawTransactionStatus, TransactionReceipt}; pub use result::{ HttpOutcallError, JsonRpcError, MultiRpcResult, ProviderError, RpcError, RpcResult, @@ -25,17 +25,6 @@ pub use rpc_client::{ RpcConfig, RpcService, RpcServices, }; -#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize, Default)] -pub enum BlockTag { - #[default] - Latest, - Finalized, - Safe, - Earliest, - Pending, - Number(Nat256), -} - /// A `Nat` that is guaranteed to fit in 256 bits. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(try_from = "candid::Nat", into = "candid::Nat")] diff --git a/evm_rpc_types/src/request/mod.rs b/evm_rpc_types/src/request/mod.rs index 15e655f7..3312215a 100644 --- a/evm_rpc_types/src/request/mod.rs +++ b/evm_rpc_types/src/request/mod.rs @@ -1,7 +1,18 @@ -use crate::{BlockTag, Hex20, Hex32, Nat256}; +use crate::{Hex20, Hex32, Nat256}; use candid::CandidType; use serde::Deserialize; +#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize, Default)] +pub enum BlockTag { + #[default] + Latest, + Finalized, + Safe, + Earliest, + Pending, + Number(Nat256), +} + #[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] pub struct FeeHistoryArgs { /// Number of blocks in the requested range. diff --git a/src/candid_rpc/cketh_conversion.rs b/src/candid_rpc/cketh_conversion.rs index 3bd2d79c..a3ad2fe9 100644 --- a/src/candid_rpc/cketh_conversion.rs +++ b/src/candid_rpc/cketh_conversion.rs @@ -4,7 +4,8 @@ use crate::rpc_client::json::requests::BlockSpec; use crate::rpc_client::json::Hash; -use evm_rpc_types::{BlockTag, Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; +use evm_rpc_types::{Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; +use evm_rpc_types::BlockTag; pub(super) fn into_block_spec(value: BlockTag) -> BlockSpec { use crate::rpc_client::json::requests; From 3b3a42efb1a9797667bf9ac17117bf189e93c0f1 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 13:40:33 +0200 Subject: [PATCH 2/9] 243: Strongly types for fields in TransactionReceipt started --- src/candid_rpc/cketh_conversion.rs | 10 ++++------ src/rpc_client/json/responses.rs | 19 ++++++++++++++++--- src/rpc_client/tests.rs | 6 +++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/candid_rpc/cketh_conversion.rs b/src/candid_rpc/cketh_conversion.rs index a3ad2fe9..85fd9fa4 100644 --- a/src/candid_rpc/cketh_conversion.rs +++ b/src/candid_rpc/cketh_conversion.rs @@ -4,8 +4,8 @@ use crate::rpc_client::json::requests::BlockSpec; use crate::rpc_client::json::Hash; -use evm_rpc_types::{Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; use evm_rpc_types::BlockTag; +use evm_rpc_types::{Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; pub(super) fn into_block_spec(value: BlockTag) -> BlockSpec { use crate::rpc_client::json::requests; @@ -115,17 +115,15 @@ pub(super) fn from_transaction_receipt( crate::rpc_client::json::responses::TransactionStatus::Failure => Nat256::from(0_u8), }), transaction_hash: Hex32::from(value.transaction_hash.0), - // TODO 243: responses types from querying JSON-RPC providers should be strongly typed - // for all the following fields: contract_address, from, logs_bloom, to, transaction_index, tx_type contract_address: value .contract_address .map(|address| Hex20::try_from(address).unwrap()), - from: Hex20::try_from(value.from).unwrap(), + from: from_address(value.from), logs: from_log_entries(value.logs), logs_bloom: Hex256::try_from(value.logs_bloom).unwrap(), - to: value.to.map(|v| Hex20::try_from(v).unwrap()), + to: value.to.map(from_address), transaction_index: value.transaction_index.into(), - tx_type: HexByte::try_from(value.r#type).unwrap(), + tx_type: HexByte::try_from(value.tx_type).unwrap(), } } diff --git a/src/rpc_client/json/responses.rs b/src/rpc_client/json/responses.rs index 2287db90..11f01cdf 100644 --- a/src/rpc_client/json/responses.rs +++ b/src/rpc_client/json/responses.rs @@ -35,17 +35,30 @@ pub struct TransactionReceipt { #[serde(rename = "transactionHash")] pub transaction_hash: Hash, + /// The contract address created, if the transaction was a contract creation, otherwise null. #[serde(rename = "contractAddress")] pub contract_address: Option, - pub from: String, + /// Address of the sender. + pub from: Address, + + /// An array of log objects that generated this transaction pub logs: Vec, + + /// The bloom filter which is used to retrive related logs #[serde(rename = "logsBloom")] pub logs_bloom: String, - pub to: Option, + + /// Address of the receiver or null in a contract creation transaction. + pub to: Option
, + + /// The transactions index position in the block #[serde(rename = "transactionIndex")] pub transaction_index: Amount<()>, - pub r#type: String, + + /// The type of the transaction (e.g. "0x0" for legacy transactions, "0x2" for EIP-1559 transactions) + #[serde(rename = "type")] + pub tx_type: String, } impl HttpResponsePayload for TransactionReceipt { diff --git a/src/rpc_client/tests.rs b/src/rpc_client/tests.rs index ca3a7fef..1041e78f 100644 --- a/src/rpc_client/tests.rs +++ b/src/rpc_client/tests.rs @@ -452,12 +452,12 @@ mod eth_get_transaction_receipt { ) .unwrap(), contract_address: None, - from: "0x1789f79e95324a47c5fd6693071188e82e9a3558".to_string(), + from: "0x1789f79e95324a47c5fd6693071188e82e9a3558".parse().unwrap(), logs: vec![], logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string(), - to: Some("0xdd2851cdd40ae6536831558dd46db62fac7a844d".to_string()), + to: Some("0xdd2851cdd40ae6536831558dd46db62fac7a844d".parse().unwrap()), transaction_index: 0x32_u32.into(), - r#type: "0x2".to_string(), + tx_type: "0x2".to_string(), } ) } From 99b31cb9b2d886d87c9cde0cde7848c3306f8313 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 14:32:13 +0200 Subject: [PATCH 3/9] 243: Transaction index --- src/rpc_client/json/responses.rs | 7 ++++--- src/rpc_client/numeric/mod.rs | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rpc_client/json/responses.rs b/src/rpc_client/json/responses.rs index 11f01cdf..d64ec96a 100644 --- a/src/rpc_client/json/responses.rs +++ b/src/rpc_client/json/responses.rs @@ -2,7 +2,8 @@ use crate::rpc_client::amount::Amount; use crate::rpc_client::eth_rpc::{HttpResponsePayload, ResponseTransform}; use crate::rpc_client::json::{FixedSizeData, Hash}; use crate::rpc_client::numeric::{ - BlockNonce, BlockNumber, Difficulty, GasAmount, LogIndex, NumBytes, Timestamp, Wei, WeiPerGas, + BlockNonce, BlockNumber, Difficulty, GasAmount, LogIndex, NumBytes, Timestamp, + TransactionIndex, Wei, WeiPerGas, }; use candid::Deserialize; use evm_rpc_types::{JsonRpcError, RpcError}; @@ -45,7 +46,7 @@ pub struct TransactionReceipt { /// An array of log objects that generated this transaction pub logs: Vec, - /// The bloom filter which is used to retrive related logs + /// The bloom filter which is used to retrieve related logs #[serde(rename = "logsBloom")] pub logs_bloom: String, @@ -54,7 +55,7 @@ pub struct TransactionReceipt { /// The transactions index position in the block #[serde(rename = "transactionIndex")] - pub transaction_index: Amount<()>, + pub transaction_index: TransactionIndex, /// The type of the transaction (e.g. "0x0" for legacy transactions, "0x2" for EIP-1559 transactions) #[serde(rename = "type")] diff --git a/src/rpc_client/numeric/mod.rs b/src/rpc_client/numeric/mod.rs index 99e55a74..5b69578c 100644 --- a/src/rpc_client/numeric/mod.rs +++ b/src/rpc_client/numeric/mod.rs @@ -17,6 +17,9 @@ pub enum TransactionCountTag {} /// but depending on the block height the two may differ. pub type TransactionCount = Amount; +pub enum TransactionIndexTag{} +pub type TransactionIndex = Amount; + pub enum BlockNumberTag {} pub type BlockNumber = Amount; From 61aad06da409d4e1f7b854f31ae213c2df67d92d Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 14:32:40 +0200 Subject: [PATCH 4/9] 243: bytes_array macro to refactor FixedSizeData and Hash --- src/candid_rpc/cketh_conversion.rs | 18 ++-- src/rpc_client/json/mod.rs | 134 ++++++++++++----------------- 2 files changed, 66 insertions(+), 86 deletions(-) diff --git a/src/candid_rpc/cketh_conversion.rs b/src/candid_rpc/cketh_conversion.rs index 85fd9fa4..5a1bcbbc 100644 --- a/src/candid_rpc/cketh_conversion.rs +++ b/src/candid_rpc/cketh_conversion.rs @@ -37,7 +37,7 @@ pub(super) fn into_get_logs_param( .map(|topic| { topic .into_iter() - .map(|t| crate::rpc_client::json::FixedSizeData(t.into())) + .map(|t| crate::rpc_client::json::FixedSizeData::new(t.into())) .collect() }) .collect(), @@ -53,11 +53,15 @@ pub(super) fn from_log_entries( fn from_log_entry(value: crate::rpc_client::json::responses::LogEntry) -> evm_rpc_types::LogEntry { evm_rpc_types::LogEntry { address: from_address(value.address), - topics: value.topics.into_iter().map(|t| t.0.into()).collect(), + topics: value + .topics + .into_iter() + .map(|t| t.into_bytes().into()) + .collect(), data: value.data.0.into(), - block_hash: value.block_hash.map(|x| x.0.into()), + block_hash: value.block_hash.map(|x| x.into_bytes().into()), block_number: value.block_number.map(Nat256::from), - transaction_hash: value.transaction_hash.map(|x| x.0.into()), + transaction_hash: value.transaction_hash.map(|x| x.into_bytes().into()), transaction_index: value.transaction_index.map(Nat256::from), log_index: value.log_index.map(Nat256::from), removed: value.removed, @@ -106,7 +110,7 @@ pub(super) fn from_transaction_receipt( value: crate::rpc_client::json::responses::TransactionReceipt, ) -> evm_rpc_types::TransactionReceipt { evm_rpc_types::TransactionReceipt { - block_hash: Hex32::from(value.block_hash.0), + block_hash: Hex32::from(value.block_hash.into_bytes()), block_number: value.block_number.into(), effective_gas_price: value.effective_gas_price.into(), gas_used: value.gas_used.into(), @@ -114,7 +118,7 @@ pub(super) fn from_transaction_receipt( crate::rpc_client::json::responses::TransactionStatus::Success => Nat256::from(1_u8), crate::rpc_client::json::responses::TransactionStatus::Failure => Nat256::from(0_u8), }), - transaction_hash: Hex32::from(value.transaction_hash.0), + transaction_hash: Hex32::from(value.transaction_hash.into_bytes()), contract_address: value .contract_address .map(|address| Hex20::try_from(address).unwrap()), @@ -182,7 +186,7 @@ pub(super) fn from_send_raw_transaction_result( } pub(super) fn into_hash(value: Hex32) -> Hash { - Hash(value.into()) + Hash::new(value.into()) } fn from_address(value: ic_ethereum_types::Address) -> evm_rpc_types::Hex20 { diff --git a/src/rpc_client/json/mod.rs b/src/rpc_client/json/mod.rs index 1c514f02..74461fda 100644 --- a/src/rpc_client/json/mod.rs +++ b/src/rpc_client/json/mod.rs @@ -8,93 +8,69 @@ use std::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; pub mod requests; pub mod responses; -#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -#[serde(transparent)] -pub struct FixedSizeData(#[serde(with = "ic_ethereum_types::serde_data")] pub [u8; 32]); - -impl AsRef<[u8]> for FixedSizeData { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl std::str::FromStr for FixedSizeData { - type Err = String; - - fn from_str(s: &str) -> Result { - if !s.starts_with("0x") { - return Err("Ethereum hex string doesn't start with 0x".to_string()); +macro_rules! bytes_array { + ($name: ident, $size: expr) => { + #[doc = concat!("Ethereum byte array (hex representation is prefixed by 0x) wrapping a `[u8; ", stringify!($size), "]`. ")] + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct $name(#[serde(with = "ic_ethereum_types::serde_data")] [u8; $size]); + + impl $name { + pub fn new(value: [u8; $size]) -> Self { + Self(value) + } + + pub fn into_bytes(self) -> [u8; $size] { + self.0 + } } - let mut bytes = [0u8; 32]; - hex::decode_to_slice(&s[2..], &mut bytes) - .map_err(|e| format!("failed to decode hash from hex: {}", e))?; - Ok(Self(bytes)) - } -} - -impl Debug for FixedSizeData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", self) - } -} - -impl Display for FixedSizeData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", self) - } -} - -impl LowerHex for FixedSizeData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "0x{}", hex::encode(self.0)) - } -} - -impl UpperHex for FixedSizeData { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "0x{}", hex::encode_upper(self.0)) - } -} - -#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct Hash(#[serde(with = "ic_ethereum_types::serde_data")] pub [u8; 32]); -impl Debug for Hash { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", self) - } -} + impl AsRef<[u8]> for $name { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } -impl Display for Hash { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", self) - } -} + impl std::str::FromStr for $name { + type Err = String; + + fn from_str(s: &str) -> Result { + if !s.starts_with("0x") { + return Err("Ethereum hex string doesn't start with 0x".to_string()); + } + let mut bytes = [0u8; $size]; + hex::decode_to_slice(&s[2..], &mut bytes) + .map_err(|e| format!("failed to decode hash from hex: {}", e))?; + Ok(Self(bytes)) + } + } -impl LowerHex for Hash { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "0x{}", hex::encode(self.0)) - } -} + impl Debug for $name { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:x}", self) + } + } -impl UpperHex for Hash { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "0x{}", hex::encode_upper(self.0)) - } -} + impl Display for $name { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:x}", self) + } + } -impl std::str::FromStr for Hash { - type Err = String; + impl LowerHex for $name { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "0x{}", hex::encode(self.0)) + } + } - fn from_str(s: &str) -> Result { - if !s.starts_with("0x") { - return Err("Ethereum hash doesn't start with 0x".to_string()); + impl UpperHex for $name { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "0x{}", hex::encode_upper(self.0)) + } } - let mut bytes = [0u8; 32]; - hex::decode_to_slice(&s[2..], &mut bytes) - .map_err(|e| format!("failed to decode hash from hex: {}", e))?; - Ok(Self(bytes)) - } + }; } +bytes_array!(FixedSizeData, 32); +bytes_array!(Hash, 32); + impl HttpResponsePayload for Hash {} From a420ef3e16a1bdff28c222f1ff8a068f5d73b187 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 14:51:10 +0200 Subject: [PATCH 5/9] 243: transaction type --- evm_rpc_types/src/lib.rs | 8 +++++++- src/candid_rpc/cketh_conversion.rs | 8 +++----- src/rpc_client/json/mod.rs | 19 +++++++++++++++++-- src/rpc_client/json/responses.rs | 8 ++++---- src/rpc_client/tests.rs | 6 +++--- 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/evm_rpc_types/src/lib.rs b/evm_rpc_types/src/lib.rs index 094c5122..bae7ae7e 100644 --- a/evm_rpc_types/src/lib.rs +++ b/evm_rpc_types/src/lib.rs @@ -184,7 +184,13 @@ impl_hex_string!(Hex(Vec)); /// `FromHex::from_hex` will return `Err(FromHexError::OddLength)` /// when trying to decode such strings. #[derive(Clone, Debug, PartialEq, Eq)] -struct Byte([u8; 1]); +pub struct Byte([u8; 1]); + +impl Byte { + pub fn into_byte(self) -> u8 { + self.0[0] + } +} impl AsRef<[u8]> for Byte { fn as_ref(&self) -> &[u8] { diff --git a/src/candid_rpc/cketh_conversion.rs b/src/candid_rpc/cketh_conversion.rs index 5a1bcbbc..6116ce72 100644 --- a/src/candid_rpc/cketh_conversion.rs +++ b/src/candid_rpc/cketh_conversion.rs @@ -119,15 +119,13 @@ pub(super) fn from_transaction_receipt( crate::rpc_client::json::responses::TransactionStatus::Failure => Nat256::from(0_u8), }), transaction_hash: Hex32::from(value.transaction_hash.into_bytes()), - contract_address: value - .contract_address - .map(|address| Hex20::try_from(address).unwrap()), + contract_address: value.contract_address.map(from_address), from: from_address(value.from), logs: from_log_entries(value.logs), - logs_bloom: Hex256::try_from(value.logs_bloom).unwrap(), + logs_bloom: Hex256::from(value.logs_bloom.into_bytes()), to: value.to.map(from_address), transaction_index: value.transaction_index.into(), - tx_type: HexByte::try_from(value.tx_type).unwrap(), + tx_type: HexByte::from(value.tx_type.into_byte()), } } diff --git a/src/rpc_client/json/mod.rs b/src/rpc_client/json/mod.rs index 74461fda..61881ef7 100644 --- a/src/rpc_client/json/mod.rs +++ b/src/rpc_client/json/mod.rs @@ -1,7 +1,7 @@ //! Types used for JSON-RPC requests and responses with Ethereum JSON-RPC providers. -use crate::rpc_client::eth_rpc::HttpResponsePayload; use candid::Deserialize; +use evm_rpc_types::Byte; use serde::Serialize; use std::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; @@ -12,6 +12,7 @@ macro_rules! bytes_array { ($name: ident, $size: expr) => { #[doc = concat!("Ethereum byte array (hex representation is prefixed by 0x) wrapping a `[u8; ", stringify!($size), "]`. ")] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] + #[serde(transparent)] pub struct $name(#[serde(with = "ic_ethereum_types::serde_data")] [u8; $size]); impl $name { @@ -72,5 +73,19 @@ macro_rules! bytes_array { bytes_array!(FixedSizeData, 32); bytes_array!(Hash, 32); +bytes_array!(LogsBloom, 256); + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct JsonByte(#[serde(with = "ic_ethereum_types::serde_data")] Byte); + +impl JsonByte { + pub fn new(value: u8) -> Self { + Self(Byte::from(value)) + } + + pub fn into_byte(self) -> u8 { + self.0.into_byte() + } +} -impl HttpResponsePayload for Hash {} diff --git a/src/rpc_client/json/responses.rs b/src/rpc_client/json/responses.rs index d64ec96a..a97b1046 100644 --- a/src/rpc_client/json/responses.rs +++ b/src/rpc_client/json/responses.rs @@ -1,6 +1,6 @@ use crate::rpc_client::amount::Amount; use crate::rpc_client::eth_rpc::{HttpResponsePayload, ResponseTransform}; -use crate::rpc_client::json::{FixedSizeData, Hash}; +use crate::rpc_client::json::{FixedSizeData, Hash, JsonByte, LogsBloom}; use crate::rpc_client::numeric::{ BlockNonce, BlockNumber, Difficulty, GasAmount, LogIndex, NumBytes, Timestamp, TransactionIndex, Wei, WeiPerGas, @@ -38,7 +38,7 @@ pub struct TransactionReceipt { /// The contract address created, if the transaction was a contract creation, otherwise null. #[serde(rename = "contractAddress")] - pub contract_address: Option, + pub contract_address: Option
, /// Address of the sender. pub from: Address, @@ -48,7 +48,7 @@ pub struct TransactionReceipt { /// The bloom filter which is used to retrieve related logs #[serde(rename = "logsBloom")] - pub logs_bloom: String, + pub logs_bloom: LogsBloom, /// Address of the receiver or null in a contract creation transaction. pub to: Option
, @@ -59,7 +59,7 @@ pub struct TransactionReceipt { /// The type of the transaction (e.g. "0x0" for legacy transactions, "0x2" for EIP-1559 transactions) #[serde(rename = "type")] - pub tx_type: String, + pub tx_type: JsonByte, } impl HttpResponsePayload for TransactionReceipt { diff --git a/src/rpc_client/tests.rs b/src/rpc_client/tests.rs index 1041e78f..1005626c 100644 --- a/src/rpc_client/tests.rs +++ b/src/rpc_client/tests.rs @@ -409,7 +409,7 @@ mod multi_call_results { mod eth_get_transaction_receipt { use crate::rpc_client::json::responses::{TransactionReceipt, TransactionStatus}; - use crate::rpc_client::json::Hash; + use crate::rpc_client::json::{Hash, JsonByte}; use crate::rpc_client::numeric::{BlockNumber, GasAmount, WeiPerGas}; use assert_matches::assert_matches; use proptest::proptest; @@ -454,10 +454,10 @@ mod eth_get_transaction_receipt { contract_address: None, from: "0x1789f79e95324a47c5fd6693071188e82e9a3558".parse().unwrap(), logs: vec![], - logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string(), + logs_bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(), to: Some("0xdd2851cdd40ae6536831558dd46db62fac7a844d".parse().unwrap()), transaction_index: 0x32_u32.into(), - tx_type: "0x2".to_string(), + tx_type: JsonByte::new(2), } ) } From 188f81234050a9bac58671e650a214a47bf37eb3 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 15:14:50 +0200 Subject: [PATCH 6/9] 243: Block --- src/candid_rpc/cketh_conversion.rs | 26 +++++----- src/rpc_client/json/responses.rs | 82 +++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/candid_rpc/cketh_conversion.rs b/src/candid_rpc/cketh_conversion.rs index 6116ce72..979200dc 100644 --- a/src/candid_rpc/cketh_conversion.rs +++ b/src/candid_rpc/cketh_conversion.rs @@ -5,7 +5,7 @@ use crate::rpc_client::json::requests::BlockSpec; use crate::rpc_client::json::Hash; use evm_rpc_types::BlockTag; -use evm_rpc_types::{Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; +use evm_rpc_types::{Hex, Hex256, Hex32, HexByte, Nat256}; pub(super) fn into_block_spec(value: BlockTag) -> BlockSpec { use crate::rpc_client::json::requests; @@ -134,31 +134,31 @@ pub(super) fn from_block(value: crate::rpc_client::json::responses::Block) -> ev base_fee_per_gas: value.base_fee_per_gas.map(Nat256::from), number: value.number.into(), difficulty: value.difficulty.map(Nat256::from), - extra_data: Hex::try_from(value.extra_data).unwrap(), + extra_data: Hex::from(value.extra_data.0), gas_limit: value.gas_limit.into(), gas_used: value.gas_used.into(), - hash: Hex32::try_from(value.hash).unwrap(), - logs_bloom: Hex256::try_from(value.logs_bloom).unwrap(), - miner: Hex20::try_from(value.miner).unwrap(), - mix_hash: Hex32::try_from(value.mix_hash).unwrap(), + hash: Hex32::from(value.hash.into_bytes()), + logs_bloom: Hex256::from(value.logs_bloom.into_bytes()), + miner: from_address(value.miner), + mix_hash: Hex32::from(value.mix_hash.into_bytes()), nonce: value.nonce.into(), - parent_hash: Hex32::try_from(value.parent_hash).unwrap(), - receipts_root: Hex32::try_from(value.receipts_root).unwrap(), - sha3_uncles: Hex32::try_from(value.sha3_uncles).unwrap(), + parent_hash: Hex32::from(value.parent_hash.into_bytes()), + receipts_root: Hex32::from(value.receipts_root.into_bytes()), + sha3_uncles: Hex32::from(value.sha3_uncles.into_bytes()), size: value.size.into(), - state_root: Hex32::try_from(value.state_root).unwrap(), + state_root: Hex32::from(value.state_root.into_bytes()), timestamp: value.timestamp.into(), total_difficulty: value.total_difficulty.map(Nat256::from), transactions: value .transactions .into_iter() - .map(|tx| Hex32::try_from(tx).unwrap()) + .map(|tx| Hex32::from(tx.into_bytes())) .collect(), - transactions_root: value.transactions_root.map(|x| Hex32::try_from(x).unwrap()), + transactions_root: value.transactions_root.map(|x| Hex32::from(x.into_bytes())), uncles: value .uncles .into_iter() - .map(|tx| Hex32::try_from(tx).unwrap()) + .map(|tx| Hex32::from(tx.into_bytes())) .collect(), } } diff --git a/src/rpc_client/json/responses.rs b/src/rpc_client/json/responses.rs index a97b1046..6453f31f 100644 --- a/src/rpc_client/json/responses.rs +++ b/src/rpc_client/json/responses.rs @@ -1,4 +1,3 @@ -use crate::rpc_client::amount::Amount; use crate::rpc_client::eth_rpc::{HttpResponsePayload, ResponseTransform}; use crate::rpc_client::json::{FixedSizeData, Hash, JsonByte, LogsBloom}; use crate::rpc_client::numeric::{ @@ -144,14 +143,14 @@ pub struct LogEntry { /// None if the block is pending. #[serde(rename = "blockNumber")] pub block_number: Option, - // 32 Bytes - hash of the transactions from which this log was created. - // None when its pending log. + /// 32 Bytes - hash of the transactions from which this log was created. + /// None when its pending log. #[serde(rename = "transactionHash")] pub transaction_hash: Option, - // Integer of the transactions position within the block the log was created from. - // None if the log is pending. + /// Integer of the transactions position within the block the log was created from. + /// None if the log is pending. #[serde(rename = "transactionIndex")] - pub transaction_index: Option>, + pub transaction_index: Option, /// 32 Bytes - hash of the block in which this log appeared. /// None if the block is pending. #[serde(rename = "blockHash")] @@ -174,42 +173,89 @@ impl HttpResponsePayload for Vec { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Block { + /// Base fee per gas + /// Only included for blocks after the London Upgrade / EIP-1559. #[serde(rename = "baseFeePerGas")] pub base_fee_per_gas: Option, + + /// Block number pub number: BlockNumber, + + /// Difficulty pub difficulty: Option, + + /// Extra data #[serde(rename = "extraData")] - pub extra_data: String, + pub extra_data: Data, + + /// Maximum gas allowed in this block #[serde(rename = "gasLimit")] pub gas_limit: GasAmount, + + /// Gas used by all transactions in this block #[serde(rename = "gasUsed")] pub gas_used: GasAmount, - pub hash: String, + + /// Block hash + pub hash: Hash, + + /// Bloom filter for the logs. #[serde(rename = "logsBloom")] - pub logs_bloom: String, - pub miner: String, + pub logs_bloom: LogsBloom, + + /// Miner + pub miner: Address, + + /// Mix hash #[serde(rename = "mixHash")] - pub mix_hash: String, + pub mix_hash: Hash, + + /// Nonce pub nonce: BlockNonce, + + /// Parent block hash #[serde(rename = "parentHash")] - pub parent_hash: String, + pub parent_hash: Hash, + + /// Receipts root #[serde(rename = "receiptsRoot")] - pub receipts_root: String, + pub receipts_root: Hash, + + /// Ommers hash #[serde(rename = "sha3Uncles")] - pub sha3_uncles: String, + pub sha3_uncles: Hash, + + /// Block size pub size: NumBytes, + + /// State root #[serde(rename = "stateRoot")] - pub state_root: String, + pub state_root: Hash, + + /// Timestamp #[serde(rename = "timestamp")] pub timestamp: Timestamp, + + /// Total difficulty is the sum of all difficulty values up to and including this block. + /// + /// Note: this field was removed from the official JSON-RPC specification in + /// https://github.com/ethereum/execution-apis/pull/570 and may no longer be served by providers. #[serde(rename = "totalDifficulty")] pub total_difficulty: Option, + + /// List of transactions in the block. + /// Note that since `eth_get_block_by_number` sets `include_full_transactions` to false, + /// this field only contains the transaction hashes and not the full transactions. #[serde(default)] - pub transactions: Vec, + pub transactions: Vec, + + /// Transactions root #[serde(rename = "transactionsRoot")] - pub transactions_root: Option, + pub transactions_root: Option, + + /// Uncles #[serde(default)] - pub uncles: Vec, + pub uncles: Vec, } impl HttpResponsePayload for Block { From 0560d434cd44405d453005008fa99478052c0665 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 15:18:58 +0200 Subject: [PATCH 7/9] 243: Updated docs --- src/candid_rpc.rs | 1 + src/candid_rpc/cketh_conversion.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/candid_rpc.rs b/src/candid_rpc.rs index 65dc99da..4dc3dd2f 100644 --- a/src/candid_rpc.rs +++ b/src/candid_rpc.rs @@ -42,6 +42,7 @@ fn process_result(method: RpcMethod, result: Result>) -> } } +/// Adapt the `EthRpcClient` to the `Candid` interface used by the EVM-RPC canister. pub struct CandidRpcClient { client: EthRpcClient, } diff --git a/src/candid_rpc/cketh_conversion.rs b/src/candid_rpc/cketh_conversion.rs index 979200dc..83480062 100644 --- a/src/candid_rpc/cketh_conversion.rs +++ b/src/candid_rpc/cketh_conversion.rs @@ -1,6 +1,4 @@ -//! Conversion between ckETH types and EVM RPC types. -//! This module is meant to be temporary and should be removed once the dependency on ckETH is removed, -//! see +//! Conversion between JSON types and Candid EVM RPC types. use crate::rpc_client::json::requests::BlockSpec; use crate::rpc_client::json::Hash; From 4f913f1d2f9b53d12c4c15fe37bfbd917dc23c0b Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 15:21:56 +0200 Subject: [PATCH 8/9] 243: re-org candid_rpc --- src/{candid_rpc.rs => candid_rpc/mod.rs} | 76 +----------------------- src/candid_rpc/tests.rs | 73 +++++++++++++++++++++++ 2 files changed, 75 insertions(+), 74 deletions(-) rename src/{candid_rpc.rs => candid_rpc/mod.rs} (70%) create mode 100644 src/candid_rpc/tests.rs diff --git a/src/candid_rpc.rs b/src/candid_rpc/mod.rs similarity index 70% rename from src/candid_rpc.rs rename to src/candid_rpc/mod.rs index 4dc3dd2f..c224bc1b 100644 --- a/src/candid_rpc.rs +++ b/src/candid_rpc/mod.rs @@ -1,4 +1,6 @@ mod cketh_conversion; +#[cfg(test)] +mod tests; use crate::rpc_client::{EthRpcClient, MultiCallError}; use crate::{ @@ -163,77 +165,3 @@ fn get_transaction_hash(raw_signed_transaction_hex: &Hex) -> Option { Some(Hex32::from(transaction.hash.0)) } -#[cfg(test)] -mod test { - use super::*; - use crate::rpc_client::{MultiCallError, MultiCallResults}; - use evm_rpc_types::{ProviderError, RpcError}; - - #[test] - fn test_process_result_mapping() { - use evm_rpc_types::{EthMainnetService, RpcService}; - - let method = RpcMethod::EthGetTransactionCount; - - assert_eq!( - process_result(method, Ok(5)), - MultiRpcResult::Consistent(Ok(5)) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::<()>::ConsistentError( - RpcError::ProviderError(ProviderError::MissingRequiredProvider) - )) - ), - MultiRpcResult::Consistent(Err(RpcError::ProviderError( - ProviderError::MissingRequiredProvider - ))) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::<()>::InconsistentResults( - MultiCallResults::default() - )) - ), - MultiRpcResult::Inconsistent(vec![]) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::InconsistentResults( - MultiCallResults::from_non_empty_iter(vec![( - RpcService::EthMainnet(EthMainnetService::Ankr), - Ok(5) - )]) - )) - ), - MultiRpcResult::Inconsistent(vec![( - RpcService::EthMainnet(EthMainnetService::Ankr), - Ok(5) - )]) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::InconsistentResults( - MultiCallResults::from_non_empty_iter(vec![ - (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), - ( - RpcService::EthMainnet(EthMainnetService::Cloudflare), - Err(RpcError::ProviderError(ProviderError::NoPermission)) - ) - ]) - )) - ), - MultiRpcResult::Inconsistent(vec![ - (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), - ( - RpcService::EthMainnet(EthMainnetService::Cloudflare), - Err(RpcError::ProviderError(ProviderError::NoPermission)) - ) - ]) - ); - } -} diff --git a/src/candid_rpc/tests.rs b/src/candid_rpc/tests.rs new file mode 100644 index 00000000..2a1c5b00 --- /dev/null +++ b/src/candid_rpc/tests.rs @@ -0,0 +1,73 @@ +use crate::candid_rpc::process_result; +use crate::rpc_client::{MultiCallError, MultiCallResults}; +use crate::types::RpcMethod; +use evm_rpc_types::MultiRpcResult; +use evm_rpc_types::{ProviderError, RpcError}; + +#[test] +fn test_process_result_mapping() { + use evm_rpc_types::{EthMainnetService, RpcService}; + + let method = RpcMethod::EthGetTransactionCount; + + assert_eq!( + process_result(method, Ok(5)), + MultiRpcResult::Consistent(Ok(5)) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::<()>::ConsistentError( + RpcError::ProviderError(ProviderError::MissingRequiredProvider) + )) + ), + MultiRpcResult::Consistent(Err(RpcError::ProviderError( + ProviderError::MissingRequiredProvider + ))) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::<()>::InconsistentResults( + MultiCallResults::default() + )) + ), + MultiRpcResult::Inconsistent(vec![]) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::InconsistentResults( + MultiCallResults::from_non_empty_iter(vec![( + RpcService::EthMainnet(EthMainnetService::Ankr), + Ok(5) + )]) + )) + ), + MultiRpcResult::Inconsistent(vec![( + RpcService::EthMainnet(EthMainnetService::Ankr), + Ok(5) + )]) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::InconsistentResults( + MultiCallResults::from_non_empty_iter(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), + ( + RpcService::EthMainnet(EthMainnetService::Cloudflare), + Err(RpcError::ProviderError(ProviderError::NoPermission)) + ) + ]) + )) + ), + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), + ( + RpcService::EthMainnet(EthMainnetService::Cloudflare), + Err(RpcError::ProviderError(ProviderError::NoPermission)) + ) + ]) + ); +} From 0060bbaf183f4957fdfaa485c4df00f0e8f2f91b Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Wed, 25 Sep 2024 15:29:38 +0200 Subject: [PATCH 9/9] 243: formatting --- src/candid_rpc/mod.rs | 1 - src/rpc_client/json/mod.rs | 1 - src/rpc_client/numeric/mod.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/candid_rpc/mod.rs b/src/candid_rpc/mod.rs index c224bc1b..fa2721bc 100644 --- a/src/candid_rpc/mod.rs +++ b/src/candid_rpc/mod.rs @@ -164,4 +164,3 @@ fn get_transaction_hash(raw_signed_transaction_hex: &Hex) -> Option { let transaction: Transaction = rlp::decode(raw_signed_transaction_hex.as_ref()).ok()?; Some(Hex32::from(transaction.hash.0)) } - diff --git a/src/rpc_client/json/mod.rs b/src/rpc_client/json/mod.rs index 61881ef7..8c80fb78 100644 --- a/src/rpc_client/json/mod.rs +++ b/src/rpc_client/json/mod.rs @@ -88,4 +88,3 @@ impl JsonByte { self.0.into_byte() } } - diff --git a/src/rpc_client/numeric/mod.rs b/src/rpc_client/numeric/mod.rs index 5b69578c..597e6659 100644 --- a/src/rpc_client/numeric/mod.rs +++ b/src/rpc_client/numeric/mod.rs @@ -17,7 +17,7 @@ pub enum TransactionCountTag {} /// but depending on the block height the two may differ. pub type TransactionCount = Amount; -pub enum TransactionIndexTag{} +pub enum TransactionIndexTag {} pub type TransactionIndex = Amount; pub enum BlockNumberTag {}