Skip to content

Commit

Permalink
fix: always deserialize Block::totalDifficulty to None (#312)
Browse files Browse the repository at this point in the history
As explained in #311, the field `totalDifficulty` was removed from the
official Ethereum JSON RPC Block schema in
ethereum/execution-apis#570, leading to
inconsistent answers between providers when calling the EVM RPC canister
with `eth_getBlockByNumber`. This seems to affect all blocks and not
only post-merge blocks.

As a workaround, the EVM RPC canister no longer deserializes the field
`totalDifficulty` in the provider's JSON response and set the Candid
returned value for `evm_rpc_types::Block::totalDifficulty` always to
`None` to be backwards-compatible.
  • Loading branch information
gregorydemay authored Oct 16, 2024
1 parent f45b0c2 commit 4b4942a
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

* fix: ensure Candid API is the same as the interface exposed by the canister
* fix: always deserialize `Block::totalDifficulty` to `None` to avoid inconsistencies between providers

## [2.1.0] - 2024-10-14

Expand Down
5 changes: 4 additions & 1 deletion src/candid_rpc/cketh_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ pub(super) fn from_block(value: crate::rpc_client::json::responses::Block) -> ev
size: value.size.into(),
state_root: Hex32::from(value.state_root.into_bytes()),
timestamp: value.timestamp.into(),
total_difficulty: value.total_difficulty.map(Nat256::from),
// The field totalDifficulty was removed from the official Ethereum JSON RPC Block schema in
// https://github.com/ethereum/execution-apis/pull/570 and as a consequence is inconsistent between different providers.
// See https://github.com/internet-computer-protocol/evm-rpc-canister/issues/311.
total_difficulty: None,
transactions: value
.transactions
.into_iter()
Expand Down
7 changes: 0 additions & 7 deletions src/rpc_client/json/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,6 @@ pub struct Block {
#[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<Difficulty>,

/// 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.
Expand Down
11 changes: 6 additions & 5 deletions src/rpc_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ use evm_rpc_types::{
};
use ic_canister_log::log;
use json::requests::{
BlockSpec, FeeHistoryParams, GetBlockByNumberParams, GetLogsParam, GetTransactionCountParams,
BlockSpec, EthCallParams, FeeHistoryParams, GetBlockByNumberParams, GetLogsParam,
GetTransactionCountParams,
};
use json::responses::{
Block, Data, FeeHistory, LogEntry, SendRawTransactionResult, TransactionReceipt,
};
use json::responses::{Block, FeeHistory, LogEntry, SendRawTransactionResult, TransactionReceipt};
use json::Hash;
use serde::{de::DeserializeOwned, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Debug;
use crate::rpc_client::json::requests::EthCallParams;
use crate::rpc_client::json::responses::Data;

pub mod amount;
pub(crate) mod eth_rpc;
Expand Down Expand Up @@ -401,7 +402,7 @@ impl EthRpcClient {
.await
.reduce(self.consensus_strategy())
}

pub async fn eth_call(&self, params: EthCallParams) -> Result<Data, MultiCallError<Data>> {
self.parallel_call(
"eth_call",
Expand Down
2 changes: 1 addition & 1 deletion src/rpc_client/numeric/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ pub enum TimestampTag {}
pub type Timestamp = Amount<TimestampTag>;

pub enum ChainIdTag {}
pub type ChainId = Amount<ChainIdTag>;
pub type ChainId = Amount<ChainIdTag>;
25 changes: 23 additions & 2 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ fn eth_get_block_by_number_should_succeed() {
size: 0xcd35_u32.into(),
state_root: "0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6".parse().unwrap(),
timestamp: 0x656f96f3_u32.into(),
total_difficulty: Some(0xc70d815d562d3cfa955_u128.into()),
total_difficulty: None,
transactions: vec![],
transactions_root: None,
uncles: vec![],
Expand Down Expand Up @@ -734,7 +734,7 @@ fn eth_get_block_by_number_pre_london_fork_should_succeed() {
size: 0x21c_u32.into(),
state_root: "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".parse().unwrap(),
timestamp: Nat256::ZERO,
total_difficulty: Some(0x400000000_u64.into()),
total_difficulty: None,
transactions: vec![],
transactions_root: Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".parse().unwrap()),
uncles: vec![],
Expand All @@ -743,6 +743,27 @@ fn eth_get_block_by_number_pre_london_fork_should_succeed() {
}
}

#[test]
fn eth_get_block_by_number_should_be_consistent_when_total_difficulty_inconsistent() {
let setup = EvmRpcSetup::new().mock_api_keys();
let response = setup.eth_get_block_by_number(
RpcServices::EthMainnet(Some(vec![
EthMainnetService::Ankr,
EthMainnetService::PublicNode,
])),
None,
evm_rpc_types::BlockTag::Latest,
)
.mock_http_once(MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","result":{"baseFeePerGas":"0xd7232aa34","difficulty":"0x0","extraData":"0x546974616e2028746974616e6275696c6465722e78797a29","gasLimit":"0x1c9c380","gasUsed":"0xa768c4","hash":"0xc3674be7b9d95580d7f23c03d32e946f2b453679ee6505e3a778f003c5a3cfae","logsBloom":"0x3e6b8420e1a13038902c24d6c2a9720a7ad4860cdc870cd5c0490011e43631134f608935bd83171247407da2c15d85014f9984608c03684c74aad48b20bc24022134cdca5f2e9d2dee3b502a8ccd39eff8040b1d96601c460e119c408c620b44fa14053013220847045556ea70484e67ec012c322830cf56ef75e09bd0db28a00f238adfa587c9f80d7e30d3aba2863e63a5cad78954555966b1055a4936643366a0bb0b1bac68d0e6267fc5bf8304d404b0c69041125219aa70562e6a5a6362331a414a96d0716990a10161b87dd9568046a742d4280014975e232b6001a0360970e569d54404b27807d7a44c949ac507879d9d41ec8842122da6772101bc8b","miner":"0x388c818ca8b9251b393131c08a736a67ccb19297","mixHash":"0x516a58424d4883a3614da00a9c6f18cd5cd54335a08388229a993a8ecf05042f","nonce":"0x0000000000000000","number":"0x11db01d","parentHash":"0x43325027f6adf9befb223f8ae80db057daddcd7b48e41f60cd94bfa8877181ae","receiptsRoot":"0x66934c3fd9c547036fe0e56ad01bc43c84b170be7c4030a86805ddcdab149929","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0xcd35","stateRoot":"0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6","timestamp":"0x656f96f3","totalDifficulty":"0xc70d815d562d3cfa955","withdrawalsRoot":"0xecae44b2c53871003c5cc75285995764034c9b5978a904229d36c1280b141d48"},"id":0}"#))
.mock_http_once(MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","result":{"baseFeePerGas":"0xd7232aa34","difficulty":"0x0","extraData":"0x546974616e2028746974616e6275696c6465722e78797a29","gasLimit":"0x1c9c380","gasUsed":"0xa768c4","hash":"0xc3674be7b9d95580d7f23c03d32e946f2b453679ee6505e3a778f003c5a3cfae","logsBloom":"0x3e6b8420e1a13038902c24d6c2a9720a7ad4860cdc870cd5c0490011e43631134f608935bd83171247407da2c15d85014f9984608c03684c74aad48b20bc24022134cdca5f2e9d2dee3b502a8ccd39eff8040b1d96601c460e119c408c620b44fa14053013220847045556ea70484e67ec012c322830cf56ef75e09bd0db28a00f238adfa587c9f80d7e30d3aba2863e63a5cad78954555966b1055a4936643366a0bb0b1bac68d0e6267fc5bf8304d404b0c69041125219aa70562e6a5a6362331a414a96d0716990a10161b87dd9568046a742d4280014975e232b6001a0360970e569d54404b27807d7a44c949ac507879d9d41ec8842122da6772101bc8b","miner":"0x388c818ca8b9251b393131c08a736a67ccb19297","mixHash":"0x516a58424d4883a3614da00a9c6f18cd5cd54335a08388229a993a8ecf05042f","nonce":"0x0000000000000000","number":"0x11db01d","parentHash":"0x43325027f6adf9befb223f8ae80db057daddcd7b48e41f60cd94bfa8877181ae","receiptsRoot":"0x66934c3fd9c547036fe0e56ad01bc43c84b170be7c4030a86805ddcdab149929","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0xcd35","stateRoot":"0x13552447dd62f11ad885f21a583c4fa34144efe923c7e35fb018d6710f06b2b6","timestamp":"0x656f96f3","withdrawalsRoot":"0xecae44b2c53871003c5cc75285995764034c9b5978a904229d36c1280b141d48"},"id":0}"#))
.wait()
.expect_consistent()
.unwrap();

assert_eq!(response.number, 18_722_845_u32.into());
assert_eq!(response.total_difficulty, None);
}

#[test]
fn eth_get_transaction_receipt_should_succeed() {
let test_cases = [
Expand Down

0 comments on commit 4b4942a

Please sign in to comment.