Skip to content

Commit

Permalink
refactor: Use reth try_from impl to convert blocks and headers (#…
Browse files Browse the repository at this point in the history
…1007)

* feat: replace header conversion method with reth native impl call

* feat: replace block conversion method with reth native impl call

* fix: lints

* fix: improve code

* fix: improve code

* fix: pass default access_list

* fix: pass None to withdrawals in block

* fix withdrawals

* fix clippy

* fix legacy

* fix test_raw_block test

---------

Co-authored-by: Thomas Coratger <thomas.coratger@gmail.com>
  • Loading branch information
bhavyagosai and tcoratger authored Apr 25, 2024
1 parent 6735f07 commit 117199d
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 99 deletions.
17 changes: 13 additions & 4 deletions src/eth_provider/database/types/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use reth_primitives::B256;
#[cfg(any(test, feature = "arbitrary", feature = "testing"))]
use reth_primitives::{Address, TransactionSigned, U256};
use reth_primitives::{Address, TransactionSigned, TxType, U256};
use reth_rpc_types::Transaction;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -28,6 +28,8 @@ impl<'a> arbitrary::Arbitrary<'a> for StoredTransaction {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let transaction = TransactionSigned::arbitrary(u)?;

let transaction_type = Into::<u8>::into(transaction.tx_type()) % 3;

Ok(StoredTransaction {
tx: Transaction {
hash: transaction.hash,
Expand All @@ -40,8 +42,16 @@ impl<'a> arbitrary::Arbitrary<'a> for StoredTransaction {
value: transaction.value(),
gas_price: Some(u128::arbitrary(u)?),
gas: u64::arbitrary(u)? as u128,
max_fee_per_gas: Some(transaction.max_fee_per_gas()),
max_priority_fee_per_gas: Some(transaction.max_priority_fee_per_gas().unwrap_or_default()),
max_fee_per_gas: if TryInto::<TxType>::try_into(transaction_type).unwrap() == TxType::Legacy {
None
} else {
Some(transaction.max_fee_per_gas())
},
max_priority_fee_per_gas: if TryInto::<TxType>::try_into(transaction_type).unwrap() == TxType::Legacy {
None
} else {
Some(transaction.max_priority_fee_per_gas().unwrap_or_default())
},
max_fee_per_blob_gas: transaction.max_fee_per_blob_gas(),
input: transaction.input().clone(),
signature: Some(reth_rpc_types::Signature {
Expand All @@ -63,7 +73,6 @@ impl<'a> arbitrary::Arbitrary<'a> for StoredTransaction {
.collect(),
)
}),

transaction_type: Some(Into::<u8>::into(transaction.tx_type()) % 3),
other: Default::default(),
},
Expand Down
5 changes: 3 additions & 2 deletions src/eth_provider/provider.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::eth_provider::starknet::kakarot_core::account_contract::BytecodeOutput;
use crate::models::block::rpc_to_primitive_header;
use alloy_rlp::{Decodable, Encodable};
use async_trait::async_trait;
use auto_impl::auto_impl;
Expand Down Expand Up @@ -826,7 +825,9 @@ where

// This is how reth computes the block size.
// `https://github.com/paradigmxyz/reth/blob/v0.2.0-beta.5/crates/rpc/rpc-types-compat/src/block.rs#L66`
let size = rpc_to_primitive_header(header.clone())?.length();
let size = reth_primitives::Header::try_from(header.clone())
.map_err(|_| EthereumDataFormatError::PrimitiveError)?
.length();
Ok(Some(
Block {
header,
Expand Down
9 changes: 4 additions & 5 deletions src/eth_rpc/servers/debug_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ use std::sync::Arc;

use alloy_rlp::Encodable;
use jsonrpsee::core::{async_trait, RpcResult as Result};
use reth_primitives::{Bytes, Log, Receipt, ReceiptWithBloom, TransactionSigned, B256};
use reth_primitives::{Block, Bytes, Header, Log, Receipt, ReceiptWithBloom, TransactionSigned, B256};
use reth_rpc_types::trace::geth::{GethDebugTracingOptions, TraceResult};
use reth_rpc_types::{BlockId, BlockNumberOrTag};

use crate::eth_provider::error::{EthApiError, EthereumDataFormatError, SignatureError};
use crate::eth_rpc::api::debug_api::DebugApiServer;
use crate::models::block::rpc_to_primitive_block;
use crate::models::block::rpc_to_primitive_header;
use crate::tracing::builder::TracerBuilder;
use crate::{eth_provider::provider::EthereumProvider, models::transaction::rpc_to_primitive_transaction};

Expand All @@ -34,7 +32,7 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
.eth_provider
.header(&block_id)
.await?
.map(rpc_to_primitive_header)
.map(Header::try_from)
.transpose()
.map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::HeaderConversionError))?
{
Expand All @@ -52,7 +50,8 @@ impl<P: EthereumProvider + Send + Sync + 'static> DebugApiServer for DebugRpc<P>
};
let mut raw_block = Vec::new();
if let Some(block) = block {
let block = rpc_to_primitive_block(block.inner).map_err(EthApiError::from)?;
let block =
Block::try_from(block.inner).map_err(|_| EthApiError::from(EthereumDataFormatError::PrimitiveError))?;
block.encode(&mut raw_block);
}
Ok(raw_block.into())
Expand Down
89 changes: 10 additions & 79 deletions src/models/block.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::transaction::rpc_to_primitive_transaction;
use crate::eth_provider::constant::STARKNET_MODULUS;
use crate::{eth_provider::error::EthereumDataFormatError, into_via_try_wrapper};
use reth_primitives::{BlockId as EthereumBlockId, BlockNumberOrTag, TransactionSigned, Withdrawals, U256};
use reth_primitives::{BlockId as EthereumBlockId, BlockNumberOrTag, U256};
use starknet::core::types::{BlockId as StarknetBlockId, BlockTag};

#[derive(Debug)]
Expand Down Expand Up @@ -67,84 +66,14 @@ impl From<EthBlockNumberOrTag> for StarknetBlockId {
}
}

pub fn rpc_to_primitive_header(
header: reth_rpc_types::Header,
) -> Result<reth_primitives::Header, EthereumDataFormatError> {
Ok(reth_primitives::Header {
base_fee_per_gas: header
.base_fee_per_gas
.map(|base_fee_per_gas| base_fee_per_gas.try_into().map_err(|_| EthereumDataFormatError::PrimitiveError))
.transpose()?,
beneficiary: header.miner,
blob_gas_used: header
.blob_gas_used
.map(|blob_gas_used| blob_gas_used.try_into().map_err(|_| EthereumDataFormatError::PrimitiveError))
.transpose()?,
difficulty: header.difficulty,
excess_blob_gas: header
.excess_blob_gas
.map(|excess_blob_gas| excess_blob_gas.try_into().map_err(|_| EthereumDataFormatError::PrimitiveError))
.transpose()?,
extra_data: header.extra_data,
gas_limit: header.gas_limit.try_into().map_err(|_| EthereumDataFormatError::PrimitiveError)?,
gas_used: header.gas_used.try_into().map_err(|_| EthereumDataFormatError::PrimitiveError)?,
logs_bloom: header.logs_bloom,
mix_hash: header.mix_hash.unwrap_or_default(),
nonce: u64::from_be_bytes(header.nonce.unwrap_or_default().0),
number: header.number.ok_or(EthereumDataFormatError::PrimitiveError)?,
ommers_hash: header.uncles_hash,
parent_beacon_block_root: header.parent_beacon_block_root,
parent_hash: header.parent_hash,
receipts_root: header.receipts_root,
state_root: header.state_root,
timestamp: header.timestamp,
transactions_root: header.transactions_root,
// Withdrawals are not allowed so we push a None value
withdrawals_root: Default::default(),
})
}

pub fn rpc_to_primitive_block(block: reth_rpc_types::Block) -> Result<reth_primitives::Block, EthereumDataFormatError> {
let body = {
let transactions: Result<Vec<TransactionSigned>, EthereumDataFormatError> = match block.transactions {
reth_rpc_types::BlockTransactions::Full(transactions) => transactions
.into_iter()
.map(|tx| {
let signature = tx.signature.ok_or(EthereumDataFormatError::PrimitiveError)?;
Ok(TransactionSigned::from_transaction_and_signature(
rpc_to_primitive_transaction(tx)?,
reth_primitives::Signature {
r: signature.r,
s: signature.s,
odd_y_parity: signature.y_parity.unwrap_or(reth_rpc_types::Parity(false)).0,
},
))
})
.collect(),
reth_rpc_types::BlockTransactions::Hashes(_) | reth_rpc_types::BlockTransactions::Uncle => {
return Err(EthereumDataFormatError::PrimitiveError);
}
};
transactions?
};
// ⚠️ Kakarot does not support omners or withdrawals and returns default values for those fields ⚠️
Ok(reth_primitives::Block {
header: rpc_to_primitive_header(block.header)?,
body,
ommers: Default::default(),
withdrawals: Some(Withdrawals::default()),
})
}

#[cfg(test)]
mod tests {
use crate::models::transaction::rpc_to_primitive_transaction;
use std::str::FromStr;

use reth_primitives::{Address, Bloom, Bytes, B256, B64, U256};
use reth_primitives::{Address, Block, Bloom, Bytes, TransactionSigned, B256, B64, U256};
use reth_rpc_types::{other::OtherFields, Parity, Signature};

use super::*;

fn base_rpc_header() -> reth_rpc_types::Header {
reth_rpc_types::Header {
parent_hash: B256::from_str(&format!("0x{:0>64}", "01")).unwrap(),
Expand All @@ -153,7 +82,7 @@ mod tests {
state_root: B256::from_str(&format!("0x{:0>64}", "04")).unwrap(),
transactions_root: B256::from_str(&format!("0x{:0>64}", "05")).unwrap(),
receipts_root: B256::from_str(&format!("0x{:0>64}", "06")).unwrap(),
withdrawals_root: Some(B256::from_str(&format!("0x{:0>64}", "07")).unwrap()),
withdrawals_root: None,
logs_bloom: Bloom::ZERO,
difficulty: U256::ZERO,
base_fee_per_gas: Some(8),
Expand All @@ -173,6 +102,8 @@ mod tests {
}

fn base_rpc_transaction() -> reth_rpc_types::Transaction {
let access_list = reth_rpc_types::AccessList::default();

reth_rpc_types::Transaction {
hash: B256::default(),
nonce: 1,
Expand All @@ -196,7 +127,7 @@ mod tests {
}),
chain_id: Some(1),
blob_versioned_hashes: Some(vec![]),
access_list: None,
access_list: Some(access_list),
transaction_type: Some(2),
other: serde_json::from_str("{}").unwrap(),
}
Expand All @@ -212,15 +143,15 @@ mod tests {
base_rpc_transaction(),
]),
size: None,
withdrawals: Some(Vec::default()),
withdrawals: None,
other: OtherFields::default(),
}
}

#[test]
fn test_rpc_to_primitive_block() {
let block = base_rpc_block();
let primitive_block = rpc_to_primitive_block(block).unwrap();
let primitive_block = Block::try_from(block).unwrap();
assert_eq!(primitive_block.header.parent_hash, B256::from_str(&format!("0x{:0>64}", "01")).unwrap());
assert_eq!(primitive_block.header.ommers_hash, B256::from_str(&format!("0x{:0>64}", "02")).unwrap());
assert_eq!(primitive_block.header.beneficiary, Address::from_str(&format!("0x{:0>40}", "03")).unwrap());
Expand Down Expand Up @@ -271,7 +202,7 @@ mod tests {
)
]
);
assert_eq!(primitive_block.withdrawals, Some(Withdrawals::default()));
assert!(primitive_block.withdrawals.is_none());
assert_eq!(primitive_block.ommers, Vec::default());
}
}
13 changes: 4 additions & 9 deletions tests/tests/debug_api.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#![cfg(feature = "testing")]
use alloy_rlp::{Decodable, Encodable};
use alloy_rlp::Encodable;
use kakarot_rpc::eth_provider::provider::EthereumProvider;
use kakarot_rpc::models::block::rpc_to_primitive_block;
use kakarot_rpc::models::transaction::rpc_to_primitive_transaction;
use kakarot_rpc::test_utils::fixtures::{katana, setup};
use kakarot_rpc::test_utils::katana::Katana;
use kakarot_rpc::test_utils::mongo::{BLOCK_HASH, BLOCK_NUMBER, EIP1599_TX_HASH, EIP2930_TX_HASH, LEGACY_TX_HASH};
use kakarot_rpc::test_utils::rpc::start_kakarot_rpc_server;
use reth_primitives::{
BlockNumberOrTag, Bytes, Log, Receipt, ReceiptWithBloom, TransactionSigned, TransactionSignedEcRecovered,
Block, BlockNumberOrTag, Bytes, Log, Receipt, ReceiptWithBloom, TransactionSigned, TransactionSignedEcRecovered,
};
use reth_rpc_types_compat::transaction::from_recovered_with_block_context;
use rstest::*;
Expand Down Expand Up @@ -476,17 +475,13 @@ async fn test_raw_block(#[future] katana: Katana, _setup: ()) {
let response: Value = serde_json::from_str(&response).expect("Failed to deserialize response body");
let rpc_block: reth_rpc_types::Block =
serde_json::from_value(response["result"].clone()).expect("Failed to deserialize result");
let primitive_block = rpc_to_primitive_block(rpc_block).unwrap();
let primitive_block = Block::try_from(rpc_block).unwrap();

// Encode primitive block and compare with the result of debug_getRawBlock
let mut buf = Vec::new();
primitive_block.encode(&mut buf);
assert_eq!(rlp_bytes.clone().unwrap(), Bytes::from(buf));

// Decode encoded block and compare with the block from eth_getBlockByNumber
let decoded_block = reth_primitives::Block::decode(&mut rlp_bytes.unwrap().as_ref()).unwrap();
assert_eq!(decoded_block, primitive_block);

// Stop the Kakarot RPC server.
drop(server_handle);
}
Expand Down Expand Up @@ -571,7 +566,7 @@ async fn test_raw_header(#[future] katana: Katana, _setup: ()) {

// Encode header into RLP bytes and assert equality with RLP bytes fetched by block number.
let mut data = vec![];
rpc_to_primitive_block(block.inner).unwrap().header.encode(&mut data);
Block::try_from(block.inner).unwrap().header.encode(&mut data);
assert_eq!(rlp_bytes_by_block_number, data);

// Stop the Kakarot RPC server.
Expand Down

0 comments on commit 117199d

Please sign in to comment.