diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 678195e6c9a..e0854877362 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] tinyvec = "1.6.0" -ethereum-types = "0.14.1" +ethereum-types = {version = "0.14.1", features = ["serialize"]} serde.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/crates/core/src/rlp/decode.rs b/crates/core/src/rlp/decode.rs index 3f11d3e384c..384ddb994e1 100644 --- a/crates/core/src/rlp/decode.rs +++ b/crates/core/src/rlp/decode.rs @@ -186,6 +186,13 @@ impl RLPDecode for crate::U256 { } } +impl RLPDecode for crate::Bloom { + fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> { + let (value, rest) = RLPDecode::decode_unfinished(rlp)?; + Ok((crate::Bloom(value), rest)) + } +} + impl RLPDecode for String { fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> { let (str_bytes, rest) = decode_bytes(rlp)?; diff --git a/crates/core/src/rlp/encode.rs b/crates/core/src/rlp/encode.rs index efe8c212744..0e336ccaa02 100644 --- a/crates/core/src/rlp/encode.rs +++ b/crates/core/src/rlp/encode.rs @@ -365,6 +365,12 @@ impl RLPEncode for ethereum_types::Signature { } } +impl RLPEncode for ethereum_types::Bloom { + fn encode(&self, buf: &mut dyn BufMut) { + self.0.encode(buf) + } +} + #[cfg(test)] mod tests { use std::net::IpAddr; diff --git a/crates/core/src/serde_utils.rs b/crates/core/src/serde_utils.rs index 7fbf29f58de..541819bc61f 100644 --- a/crates/core/src/serde_utils.rs +++ b/crates/core/src/serde_utils.rs @@ -1,4 +1,4 @@ -use serde::{de::Error, Deserialize, Deserializer}; +use serde::{de::Error, Deserialize, Deserializer, Serializer}; pub mod u256 { use super::*; @@ -35,6 +35,26 @@ pub mod u256 { pub mod u64 { use super::*; + pub mod hex_str { + use super::*; + + pub fn deserialize<'de, D>(d: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(d)?; + u64::from_str_radix(value.trim_start_matches("0x"), 16) + .map_err(|_| D::Error::custom("Failed to deserialize u64 value")) + } + + pub fn serialize(value: &u64, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{:#x}", value)) + } + } + pub fn deser_dec_str<'de, D>(d: D) -> Result where D: Deserializer<'de>, @@ -44,23 +64,15 @@ pub mod u64 { .parse() .map_err(|_| D::Error::custom("Failed to deserialize u64 value")) } - - pub fn deser_hex_str<'de, D>(d: D) -> Result - where - D: Deserializer<'de>, - { - let value = String::deserialize(d)?; - u64::from_str_radix(value.trim_start_matches("0x"), 16) - .map_err(|_| D::Error::custom("Failed to deserialize u64 value")) - } } +/// Serializes to and deserializes from 0x prefixed hex string pub mod bytes { use ::bytes::Bytes; use super::*; - pub fn deser_hex_str<'de, D>(d: D) -> Result + pub fn deserialize<'de, D>(d: D) -> Result where D: Deserializer<'de>, { @@ -69,4 +81,11 @@ pub mod bytes { .map_err(|e| D::Error::custom(e.to_string()))?; Ok(Bytes::from(bytes)) } + + pub fn serialize(value: &Bytes, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("0x{:x}", value)) + } } diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index 6f4b2b06a55..1aa6318397b 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -12,9 +12,10 @@ use crate::{ Address, H256, U256, }; use bytes::Bytes; +use ethereum_types::Bloom; use keccak_hash::keccak; use patricia_merkle_tree::PatriciaMerkleTree; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sha3::Keccak256; use std::cmp::{max, Ordering}; @@ -22,7 +23,6 @@ use std::cmp::{max, Ordering}; use super::Transaction; pub type BlockNumber = u64; -pub type Bloom = [u8; 256]; use lazy_static::lazy_static; @@ -31,26 +31,39 @@ lazy_static! { } /// Header part of a block on the chain. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] pub struct BlockHeader { pub parent_hash: H256, + #[serde(rename(serialize = "sha3Uncles"))] pub ommers_hash: H256, // ommer = uncle + #[serde(rename(serialize = "miner"))] pub coinbase: Address, pub state_root: H256, pub transactions_root: H256, pub receipt_root: H256, pub logs_bloom: Bloom, pub difficulty: U256, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub number: BlockNumber, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub gas_limit: u64, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub gas_used: u64, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub timestamp: u64, + #[serde(with = "crate::serde_utils::bytes")] pub extra_data: Bytes, + #[serde(rename(serialize = "mixHash"))] pub prev_randao: H256, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub nonce: u64, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub base_fee_per_gas: u64, pub withdrawals_root: H256, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub blob_gas_used: u64, + #[serde(with = "crate::serde_utils::u64::hex_str")] pub excess_blob_gas: u64, pub parent_beacon_block_root: H256, } @@ -136,10 +149,11 @@ impl RLPDecode for BlockHeader { } // The body of a block on the chain -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] pub struct BlockBody { pub transactions: Vec, // TODO: ommers list is always empty, so we can remove it + #[serde(rename(serialize = "uncles"))] pub ommers: Vec, pub withdrawals: Vec, } @@ -253,12 +267,12 @@ impl BlockHeader { } } -#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Withdrawal { - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] index: u64, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] validator_index: u64, address: Address, amount: U256, @@ -370,6 +384,55 @@ pub fn validate_block_header(header: &BlockHeader, parent_header: &BlockHeader) && header.parent_hash == parent_header.compute_block_hash() } +#[allow(unused)] +mod serializable { + use super::*; + + #[derive(Debug, Serialize)] + pub struct BlockSerializable { + hash: H256, + #[serde(flatten)] + header: BlockHeader, + #[serde(flatten)] + body: BlockBodyWrapper, + } + + #[derive(Debug, Serialize)] + #[serde(untagged)] + enum BlockBodyWrapper { + Full(BlockBody), + OnlyHashes(OnlyHashesBlockBody), + } + + #[derive(Debug, Serialize)] + struct OnlyHashesBlockBody { + // Only tx hashes + pub transactions: Vec, + pub uncles: Vec, + pub withdrawals: Vec, + } + + impl BlockSerializable { + pub fn from_block( + header: BlockHeader, + body: BlockBody, + full_transactions: bool, + ) -> BlockSerializable { + let body = if full_transactions { + BlockBodyWrapper::Full(body) + } else { + BlockBodyWrapper::OnlyHashes(OnlyHashesBlockBody { + transactions: body.transactions.iter().map(|t| t.compute_hash()).collect(), + uncles: body.ommers, + withdrawals: body.withdrawals, + }) + }; + let hash = header.compute_block_hash(); + BlockSerializable { hash, header, body } + } + } +} + #[cfg(test)] mod test { @@ -377,6 +440,9 @@ mod test { use ethereum_types::H160; use hex_literal::hex; + use serializable::BlockSerializable; + + use crate::types::{EIP1559Transaction, TxKind}; use super::*; @@ -489,4 +555,88 @@ mod test { }; assert!(validate_block_header(&block, &parent_block)) } + + #[test] + fn serialize_block() { + let block_header = BlockHeader { + parent_hash: H256::from_str( + "0x1ac1bf1eef97dc6b03daba5af3b89881b7ae4bc1600dc434f450a9ec34d44999", + ) + .unwrap(), + ommers_hash: H256::from_str( + "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + ) + .unwrap(), + coinbase: Address::from_str("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(), + state_root: H256::from_str( + "0x9de6f95cb4ff4ef22a73705d6ba38c4b927c7bca9887ef5d24a734bb863218d9", + ) + .unwrap(), + transactions_root: H256::from_str( + "0x578602b2b7e3a3291c3eefca3a08bc13c0d194f9845a39b6f3bcf843d9fed79d", + ) + .unwrap(), + receipt_root: H256::from_str( + "0x035d56bac3f47246c5eed0e6642ca40dc262f9144b582f058bc23ded72aa72fa", + ) + .unwrap(), + logs_bloom: Bloom::from([0; 256]), + difficulty: U256::zero(), + number: 1, + gas_limit: 0x016345785d8a0000, + gas_used: 0xa8de, + timestamp: 0x03e8, + extra_data: Bytes::new(), + prev_randao: H256::zero(), + nonce: 0x0000000000000000, + base_fee_per_gas: 0x07, + withdrawals_root: H256::from_str( + "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + blob_gas_used: 0x00, + excess_blob_gas: 0x00, + parent_beacon_block_root: H256::zero(), + }; + + let tx = EIP1559Transaction { + nonce: 0, + max_fee_per_gas: 78, + max_priority_fee_per_gas: 17, + to: TxKind::Call(Address::from_slice( + &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(), + )), + value: 3000000000000000_u64.into(), + data: Bytes::from_static(b"0x1568"), + signature_r: U256::from_str_radix( + "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d", + 16, + ) + .unwrap(), + signature_s: U256::from_str_radix( + "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4", + 16, + ) + .unwrap(), + signature_y_parity: false, + chain_id: 3151908, + gas_limit: 63000, + access_list: vec![( + Address::from_slice( + &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(), + ), + vec![], + )], + }; + + let block_body = BlockBody { + transactions: vec![Transaction::EIP1559Transaction(tx)], + ommers: vec![], + withdrawals: vec![], + }; + + let block = BlockSerializable::from_block(block_header, block_body, true); + let expected_block = r#"{"hash":"0x63d6a2504601fc2db0ccf02a28055eb0cdb40c444ecbceec0f613980421a035e","parentHash":"0x1ac1bf1eef97dc6b03daba5af3b89881b7ae4bc1600dc434f450a9ec34d44999","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba","stateRoot":"0x9de6f95cb4ff4ef22a73705d6ba38c4b927c7bca9887ef5d24a734bb863218d9","transactionsRoot":"0x578602b2b7e3a3291c3eefca3a08bc13c0d194f9845a39b6f3bcf843d9fed79d","receiptRoot":"0x035d56bac3f47246c5eed0e6642ca40dc262f9144b582f058bc23ded72aa72fa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x16345785d8a0000","gasUsed":"0xa8de","timestamp":"0x3e8","extraData":"0x","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","baseFeePerGas":"0x7","withdrawalsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","blobGasUsed":"0x0","excessBlobGas":"0x0","parentBeaconBlockRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactions":[{"type":"0x2","nonce":"0x0","to":"0x6177843db3138ae69679a54b95cf345ed759450d","gas":"0xf618","value":"0xaa87bee538000","input":"0x307831353638","maxPriorityFeePerGas":"0x11","maxFeePerGas":"0x4e","gasPrice":"0x4e","accessList":[{"address":"0x6177843db3138ae69679a54b95cf345ed759450d","storageKeys":[]}],"chainId":"0x301824","yParity":"0x0","r":"0x151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d","s":"0x64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4"}],"uncles":[],"withdrawals":[]}"#; + assert_eq!(serde_json::to_string(&block).unwrap(), expected_block) + } } diff --git a/crates/core/src/types/engine/payload.rs b/crates/core/src/types/engine/payload.rs index 3addd4dda0f..64219b4d42b 100644 --- a/crates/core/src/types/engine/payload.rs +++ b/crates/core/src/types/engine/payload.rs @@ -20,24 +20,24 @@ pub struct ExecutionPayloadV3 { receipts_root: H256, logs_bloom: Bloom, prev_randao: H256, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] block_number: u64, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] gas_limit: u64, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] gas_used: u64, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] timestamp: u64, - #[serde(deserialize_with = "crate::serde_utils::bytes::deser_hex_str")] + #[serde(with = "crate::serde_utils::bytes")] extra_data: Bytes, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] base_fee_per_gas: u64, pub block_hash: H256, transactions: Vec, withdrawals: Vec, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] blob_gas_used: u64, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] excess_blob_gas: u64, } @@ -50,7 +50,7 @@ impl<'de> Deserialize<'de> for EncodedTransaction { where D: serde::Deserializer<'de>, { - Ok(EncodedTransaction(serde_utils::bytes::deser_hex_str( + Ok(EncodedTransaction(serde_utils::bytes::deserialize( deserializer, )?)) } @@ -92,7 +92,7 @@ impl ExecutionPayloadV3 { state_root: self.state_root, transactions_root: block_body.compute_transactions_root(), receipt_root: self.receipts_root, - logs_bloom: self.logs_bloom.into(), + logs_bloom: self.logs_bloom, difficulty: 0.into(), number: self.block_number, gas_limit: self.gas_limit, diff --git a/crates/core/src/types/genesis.rs b/crates/core/src/types/genesis.rs index 052f42517a6..0de750f066c 100644 --- a/crates/core/src/types/genesis.rs +++ b/crates/core/src/types/genesis.rs @@ -15,9 +15,9 @@ pub struct Genesis { pub coinbase: Address, pub difficulty: U256, pub extra_data: Bytes, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] pub gas_limit: u64, - #[serde(deserialize_with = "crate::serde_utils::u64::deser_hex_str")] + #[serde(with = "crate::serde_utils::u64::hex_str")] pub nonce: u64, pub mixhash: H256, #[serde(deserialize_with = "crate::serde_utils::u64::deser_dec_str")] diff --git a/crates/core/src/types/receipt.rs b/crates/core/src/types/receipt.rs index 9bdc4a115f0..3ac47782ebb 100644 --- a/crates/core/src/types/receipt.rs +++ b/crates/core/src/types/receipt.rs @@ -1,7 +1,6 @@ use crate::rlp::{encode::RLPEncode, structs::Encoder}; -use crate::types::Bloom; use bytes::Bytes; -use ethereum_types::{Address, H256}; +use ethereum_types::{Address, Bloom, H256}; use super::TxType; pub type Index = u64; diff --git a/crates/core/src/types/transaction.rs b/crates/core/src/types/transaction.rs index f6202373d1a..7d0351787d7 100644 --- a/crates/core/src/types/transaction.rs +++ b/crates/core/src/types/transaction.rs @@ -1,6 +1,7 @@ use bytes::Bytes; use ethereum_types::{Address, H256, U256}; use secp256k1::{ecdsa::RecoveryId, Message, SECP256K1}; +use serde::{ser::SerializeStruct, Serialize}; use sha3::{Digest, Keccak256}; use crate::rlp::{ @@ -11,7 +12,8 @@ use crate::rlp::{ structs::{Decoder, Encoder}, }; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[serde(untagged)] pub enum Transaction { LegacyTransaction(LegacyTransaction), EIP2930Transaction(EIP2930Transaction), @@ -52,13 +54,13 @@ pub struct EIP2930Transaction { #[derive(Clone, Debug, PartialEq, Eq)] pub struct EIP1559Transaction { pub chain_id: u64, - pub signer_nonce: u64, + pub nonce: u64, pub max_priority_fee_per_gas: u64, pub max_fee_per_gas: u64, pub gas_limit: u64, - pub destination: Address, - pub amount: U256, - pub payload: Bytes, + pub to: TxKind, + pub value: U256, + pub data: Bytes, pub access_list: Vec<(Address, Vec)>, pub signature_y_parity: bool, pub signature_r: U256, @@ -223,13 +225,13 @@ impl RLPEncode for EIP1559Transaction { fn encode(&self, buf: &mut dyn bytes::BufMut) { Encoder::new(buf) .encode_field(&self.chain_id) - .encode_field(&self.signer_nonce) + .encode_field(&self.nonce) .encode_field(&self.max_priority_fee_per_gas) .encode_field(&self.max_fee_per_gas) .encode_field(&self.gas_limit) - .encode_field(&self.destination) - .encode_field(&self.amount) - .encode_field(&self.payload) + .encode_field(&self.to) + .encode_field(&self.value) + .encode_field(&self.data) .encode_field(&self.access_list) .encode_field(&self.signature_y_parity) .encode_field(&self.signature_r) @@ -323,14 +325,14 @@ impl RLPDecode for EIP1559Transaction { fn decode_unfinished(rlp: &[u8]) -> Result<(EIP1559Transaction, &[u8]), RLPDecodeError> { let decoder = Decoder::new(rlp)?; let (chain_id, decoder) = decoder.decode_field("chain_id")?; - let (signer_nonce, decoder) = decoder.decode_field("signer_nonce")?; + let (nonce, decoder) = decoder.decode_field("nonce")?; let (max_priority_fee_per_gas, decoder) = decoder.decode_field("max_priority_fee_per_gas")?; let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?; let (gas_limit, decoder) = decoder.decode_field("gas_limit")?; - let (destination, decoder) = decoder.decode_field("destination")?; - let (amount, decoder) = decoder.decode_field("amount")?; - let (payload, decoder) = decoder.decode_field("payload")?; + let (to, decoder) = decoder.decode_field("to")?; + let (value, decoder) = decoder.decode_field("value")?; + let (data, decoder) = decoder.decode_field("data")?; let (access_list, decoder) = decoder.decode_field("access_list")?; let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?; let (signature_r, decoder) = decoder.decode_field("signature_r")?; @@ -338,13 +340,13 @@ impl RLPDecode for EIP1559Transaction { let tx = EIP1559Transaction { chain_id, - signer_nonce, + nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, - destination, - amount, - payload, + to, + value, + data, access_list, signature_y_parity, signature_r, @@ -437,13 +439,13 @@ impl Transaction { let mut buf = vec![self.tx_type() as u8]; Encoder::new(&mut buf) .encode_field(&tx.chain_id) - .encode_field(&tx.signer_nonce) + .encode_field(&tx.nonce) .encode_field(&tx.max_priority_fee_per_gas) .encode_field(&tx.max_fee_per_gas) .encode_field(&tx.gas_limit) - .encode_field(&tx.destination) - .encode_field(&tx.amount) - .encode_field(&tx.payload) + .encode_field(&tx.to) + .encode_field(&tx.value) + .encode_field(&tx.data) .encode_field(&tx.access_list) .finish(); recover_address( @@ -500,7 +502,7 @@ impl Transaction { match self { Transaction::LegacyTransaction(tx) => tx.to.clone(), Transaction::EIP2930Transaction(tx) => tx.to.clone(), - Transaction::EIP1559Transaction(tx) => TxKind::Call(tx.destination), + Transaction::EIP1559Transaction(tx) => tx.to.clone(), Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to), } } @@ -509,7 +511,7 @@ impl Transaction { match self { Transaction::LegacyTransaction(tx) => tx.value, Transaction::EIP2930Transaction(tx) => tx.value, - Transaction::EIP1559Transaction(tx) => tx.amount, + Transaction::EIP1559Transaction(tx) => tx.value, Transaction::EIP4844Transaction(tx) => tx.value, } } @@ -545,7 +547,7 @@ impl Transaction { match self { Transaction::LegacyTransaction(tx) => tx.nonce, Transaction::EIP2930Transaction(tx) => tx.nonce, - Transaction::EIP1559Transaction(tx) => tx.signer_nonce, + Transaction::EIP1559Transaction(tx) => tx.nonce, Transaction::EIP4844Transaction(tx) => tx.nonce, } } @@ -554,7 +556,7 @@ impl Transaction { match self { Transaction::LegacyTransaction(tx) => &tx.data, Transaction::EIP2930Transaction(tx) => &tx.data, - Transaction::EIP1559Transaction(tx) => &tx.payload, + Transaction::EIP1559Transaction(tx) => &tx.data, Transaction::EIP4844Transaction(tx) => &tx.data, } } @@ -576,6 +578,10 @@ impl Transaction { Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas), } } + + pub fn compute_hash(&self) -> H256 { + keccak_hash::keccak(self.encode_to_vec()) + } } fn recover_address( @@ -606,6 +612,178 @@ fn recover_address( Address::from_slice(&hash[12..]) } +// Serialization + +mod serde_impl { + use super::*; + + impl Serialize for TxKind { + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::Serializer, + { + match self { + TxKind::Call(address) => serializer.serialize_str(&format!("{:#x}", address)), + TxKind::Create => serializer.serialize_str(""), + } + } + } + + impl Serialize for TxType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!("{:#x}", *self as u8)) + } + } + + #[derive(Serialize)] + #[serde(rename_all = "camelCase")] + struct AccessListEntry { + address: Address, + storage_keys: Vec, + } + + impl From<&(Address, Vec)> for AccessListEntry { + fn from(value: &(Address, Vec)) -> AccessListEntry { + AccessListEntry { + address: value.0, + storage_keys: value.1.clone(), + } + } + } + + impl Serialize for LegacyTransaction { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut struct_serializer = serializer.serialize_struct("LegacyTransaction", 11)?; + struct_serializer.serialize_field("type", &TxType::Legacy)?; + struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?; + struct_serializer.serialize_field("to", &self.to)?; + struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?; + struct_serializer.serialize_field("value", &self.value)?; + struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?; + struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?; + struct_serializer.serialize_field("chainId", &format!("{:#x}", 1))?; // Mainnet as defaut. TODO: check this + struct_serializer.serialize_field("v", &self.v)?; + struct_serializer.serialize_field("r", &self.r)?; + struct_serializer.serialize_field("s", &self.s)?; + struct_serializer.end() + } + } + + impl Serialize for EIP2930Transaction { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut struct_serializer = serializer.serialize_struct("Eip2930Transaction", 12)?; + struct_serializer.serialize_field("type", &TxType::EIP2930)?; + struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?; + struct_serializer.serialize_field("to", &self.to)?; + struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?; + struct_serializer.serialize_field("value", &self.value)?; + struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?; + struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?; + struct_serializer.serialize_field( + "accessList", + &self + .access_list + .iter() + .map(AccessListEntry::from) + .collect::>(), + )?; + struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?; + struct_serializer + .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?; + struct_serializer.serialize_field("r", &self.signature_r)?; + struct_serializer.serialize_field("s", &self.signature_s)?; + struct_serializer.end() + } + } + + impl Serialize for EIP1559Transaction { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?; + struct_serializer.serialize_field("type", &TxType::EIP1559)?; + struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?; + struct_serializer.serialize_field("to", &self.to)?; + struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?; + struct_serializer.serialize_field("value", &self.value)?; + struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?; + struct_serializer.serialize_field( + "maxPriorityFeePerGas", + &format!("{:#x}", self.max_priority_fee_per_gas), + )?; + struct_serializer + .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?; + struct_serializer + .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?; + struct_serializer.serialize_field( + "accessList", + &self + .access_list + .iter() + .map(AccessListEntry::from) + .collect::>(), + )?; + struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?; + struct_serializer + .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?; + struct_serializer.serialize_field("r", &self.signature_r)?; + struct_serializer.serialize_field("s", &self.signature_s)?; + struct_serializer.end() + } + } + + impl Serialize for EIP4844Transaction { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut struct_serializer = serializer.serialize_struct("Eip4844Transaction", 15)?; + struct_serializer.serialize_field("type", &TxType::EIP4844)?; + struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?; + struct_serializer.serialize_field("to", &self.to)?; + struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?; + struct_serializer.serialize_field("value", &self.value)?; + struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?; + struct_serializer.serialize_field( + "maxPriorityFeePerGas", + &format!("{:#x}", self.max_priority_fee_per_gas), + )?; + struct_serializer + .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?; + struct_serializer.serialize_field( + "maxFeePerBlobGas", + &format!("{:#x}", self.max_fee_per_blob_gas), + )?; + struct_serializer.serialize_field( + "accessList", + &self + .access_list + .iter() + .map(AccessListEntry::from) + .collect::>(), + )?; + struct_serializer + .serialize_field("blobVersionedHahses", &self.blob_versioned_hashes)?; + struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?; + struct_serializer + .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?; + struct_serializer.serialize_field("r", &self.signature_r)?; + struct_serializer.serialize_field("s", &self.signature_s)?; + struct_serializer.end() + } + } +} + #[cfg(test)] mod tests { use crate::types::{compute_receipts_root, BlockBody, Receipt}; @@ -648,7 +826,7 @@ mod tests { let cumulative_gas_used = 0x5208; let bloom = [0x00; 256]; let logs = vec![]; - let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, bloom, logs); + let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, bloom.into(), logs); let result = compute_receipts_root(&[receipt]); let expected_root = @@ -691,14 +869,14 @@ mod tests { let encoded_tx_bytes = hex::decode(encoded_tx).unwrap(); let tx = EIP1559Transaction::decode(&encoded_tx_bytes).unwrap(); let expected_tx = EIP1559Transaction { - signer_nonce: 0, + nonce: 0, max_fee_per_gas: 78, max_priority_fee_per_gas: 17, - destination: Address::from_slice( + to: TxKind::Call(Address::from_slice( &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(), - ), - amount: 3000000000000000_u64.into(), - payload: Bytes::new(), + )), + value: 3000000000000000_u64.into(), + data: Bytes::new(), signature_r: U256::from_str_radix( "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d", 16, diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index fe0486836f9..2a4ec4af5bb 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -188,7 +188,8 @@ mod tests { use bytes::Bytes; use ethereum_rust_core::{ rlp::decode::RLPDecode, - types::{self, Bloom, Transaction}, + types::{self, Transaction}, + Bloom, }; use ethereum_types::{H256, U256}; diff --git a/ef_tests/src/types.rs b/ef_tests/src/types.rs index c01edf5bf8e..3f586909dd7 100644 --- a/ef_tests/src/types.rs +++ b/ef_tests/src/types.rs @@ -1,7 +1,7 @@ use bytes::Bytes; use ethereum_rust_core::types::{ code_hash, Account as ethereum_rustAccount, AccountInfo, EIP1559Transaction, LegacyTransaction, - Transaction as ethereum_rustTransacion, + Transaction as ethereum_rustTransaction, TxKind, }; use ethereum_rust_core::{types::BlockHeader, Address, Bloom, H256, U256, U64}; use serde::{Deserialize, Serialize}; @@ -26,7 +26,7 @@ pub struct TestUnit { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub struct Account { pub balance: U256, - #[serde(deserialize_with = "ethereum_rust_core::serde_utils::bytes::deser_hex_str")] + #[serde(with = "ethereum_rust_core::serde_utils::bytes")] pub code: Bytes, pub nonce: U256, pub storage: HashMap, @@ -100,7 +100,7 @@ pub struct Block { pub struct Transaction { #[serde(rename = "type")] pub transaction_type: Option, - #[serde(deserialize_with = "ethereum_rust_core::serde_utils::bytes::deser_hex_str")] + #[serde(with = "ethereum_rust_core::serde_utils::bytes")] pub data: Bytes, pub gas_limit: U256, pub gas_price: Option, @@ -130,7 +130,7 @@ impl From
for BlockHeader { state_root: val.state_root, transactions_root: val.transactions_trie, receipt_root: val.receipt_trie, - logs_bloom: val.bloom.into(), + logs_bloom: val.bloom, difficulty: val.difficulty, number: val.number.as_u64(), gas_limit: val.gas_limit.as_u64(), @@ -148,14 +148,14 @@ impl From
for BlockHeader { } } -impl From for ethereum_rustTransacion { +impl From for ethereum_rustTransaction { fn from(val: Transaction) -> Self { match val.transaction_type { Some(tx_type) => match tx_type.as_u64() { - 2 => ethereum_rustTransacion::EIP1559Transaction(val.into()), + 2 => ethereum_rustTransaction::EIP1559Transaction(val.into()), _ => unimplemented!(), }, - None => ethereum_rustTransacion::LegacyTransaction(val.into()), + None => ethereum_rustTransaction::LegacyTransaction(val.into()), } } } @@ -165,16 +165,16 @@ impl From for EIP1559Transaction { EIP1559Transaction { // Note: gas_price is not used in this conversion as it is not part of EIP1559Transaction, this could be a problem chain_id: val.chain_id.map(|id| id.as_u64()).unwrap_or(1 /*mainnet*/), // TODO: Consider converting this into Option - signer_nonce: val.nonce.as_u64(), + nonce: val.nonce.as_u64(), max_priority_fee_per_gas: val.max_priority_fee_per_gas.unwrap_or_default().as_u64(), // TODO: Consider converting this into Option max_fee_per_gas: val .max_fee_per_gas .unwrap_or(val.gas_price.unwrap_or_default()) .as_u64(), // TODO: Consider converting this into Option gas_limit: val.gas_limit.as_u64(), - destination: val.to, - amount: val.value, - payload: val.data, + to: TxKind::Call(val.to), + value: val.value, + data: val.data, access_list: val .access_list .unwrap_or_default() @@ -194,7 +194,7 @@ impl From for LegacyTransaction { nonce: val.nonce.as_u64(), gas_price: val.gas_price.unwrap_or_default().as_u64(), // TODO: Consider converting this into Option gas: val.gas_limit.as_u64(), - to: ethereum_rust_core::types::TxKind::Call(val.to), + to: TxKind::Call(val.to), value: val.value, data: val.data, v: val.v,