From a685ff2250a4aa4cb1510ab3556b4282cffcdd48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:09:57 -0300 Subject: [PATCH 01/11] feat: compute transactions root for a block --- crates/core/Cargo.toml | 2 + crates/core/src/types/block.rs | 110 +++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 10c65efed1f..59ffd7b2559 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -13,6 +13,8 @@ serde.workspace = true serde_json.workspace = true thiserror.workspace = true keccak-hash = "0.10.0" +patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" } +sha3 = "0.10.8" [dev-dependencies] hex-literal = "0.4.1" diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index ea2678c6357..f16b1be2bfe 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -1,5 +1,7 @@ use crate::{rlp::encode::RLPEncode, Address, H256, U256}; use bytes::Bytes; +use patricia_merkle_tree::PatriciaMerkleTree; +use sha3::Keccak256; pub type BlockNumber = u64; pub type Bloom = [u8; 256]; @@ -56,13 +58,59 @@ impl RLPEncode for BlockHeader { // The body of a block on the chain #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Body { +pub struct BlockBody { transactions: Vec, + // TODO: ommers list is always empty, so we can remove it ommers: Vec, withdrawals: Vec, } -impl RLPEncode for Body { +impl BlockBody { + pub const fn empty() -> Self { + Self { + transactions: Vec::new(), + ommers: Vec::new(), + withdrawals: Vec::new(), + } + } + + pub fn compute_transactions_root(&self) -> H256 { + let transactions_iter: Vec<_> = self + .transactions + .iter() + .enumerate() + .map(|(i, tx)| { + // TODO: check if tree is RLP encoding the value + + // I think the tree is RLP encoding the key, so this is not needed? + // let mut k = Vec::new(); + // i.encode(&mut k); + + let mut v = Vec::new(); + let tx_type = tx.tx_type(); + + // Legacy transactions don't have a prefix + if tx_type != 0 { + v.push(tx_type); + } + tx.encode(&mut v); + dbg!(&v); + dbg!(v.len()); + + // Key: RLP(tx_index) + // Value: tx_type || RLP(tx) if tx_type != 0 + // RLP(tx) else + (i.to_be_bytes(), v) + }) + .collect(); + let root = PatriciaMerkleTree::<_, _, Keccak256>::compute_hash_from_sorted_iter( + &transactions_iter, + ); + H256(root.into()) + } +} + +impl RLPEncode for BlockBody { fn encode(&self, buf: &mut dyn bytes::BufMut) { self.transactions.encode(buf); self.ommers.encode(buf); @@ -93,6 +141,15 @@ pub enum Transaction { EIP1559Transaction(EIP1559Transaction), } +impl Transaction { + pub fn tx_type(&self) -> u8 { + match self { + Transaction::LegacyTransaction(_) => 0, + Transaction::EIP1559Transaction(_) => 2, + } + } +} + impl RLPEncode for Transaction { fn encode(&self, buf: &mut dyn bytes::BufMut) { match self { @@ -104,10 +161,10 @@ impl RLPEncode for Transaction { #[derive(Clone, Debug, PartialEq, Eq)] pub struct LegacyTransaction { - nonce: U256, - gas_price: u64, + nonce: u64, + gas_price: U256, gas: u64, - to: Address, + to: Option
, value: U256, data: Bytes, v: U256, @@ -117,10 +174,16 @@ pub struct LegacyTransaction { impl RLPEncode for LegacyTransaction { fn encode(&self, buf: &mut dyn bytes::BufMut) { + // TODO: prepend size header self.nonce.encode(buf); self.gas_price.encode(buf); self.gas.encode(buf); - self.to.encode(buf); + // TODO: implement encode for Option? + match &self.to { + Some(to) => to.encode(buf), + // TODO: move to a constant? + None => buf.put_u8(0x80), + } self.value.encode(buf); self.data.encode(buf); self.v.encode(buf); @@ -147,6 +210,7 @@ pub struct EIP1559Transaction { impl RLPEncode for EIP1559Transaction { fn encode(&self, buf: &mut dyn bytes::BufMut) { + // TODO: prepend size header self.chain_id.encode(buf); self.signer_nonce.encode(buf); self.max_priority_fee_per_gas.encode(buf); @@ -161,3 +225,37 @@ impl RLPEncode for EIP1559Transaction { self.signature_s.encode(buf); } } + +#[cfg(test)] +mod tests { + use hex_literal::hex; + + use super::{BlockBody, LegacyTransaction}; + use crate::{types::Transaction, U256}; + + #[test] + fn test_compute_transactions_root() { + let mut body = BlockBody::empty(); + let tx = LegacyTransaction { + nonce: 0, + gas_price: 0x0a.into(), + gas: 0x05f5e100, + to: Some(hex!("1000000000000000000000000000000000000000").into()), + value: 0.into(), + data: Default::default(), + v: U256::from(0x1b), + r: U256::from(hex!( + "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37" + )), + s: U256::from(hex!( + "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b" + )), + }; + body.transactions.push(Transaction::LegacyTransaction(tx)); + let expected_root = + hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb"); + let result = body.compute_transactions_root(); + + assert_eq!(result, expected_root.into()); + } +} From 611bc8622b91a1a3177253147c89098e6d352233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:19:59 -0300 Subject: [PATCH 02/11] fix: was encoding LegacyTransaction incorrectly --- crates/core/src/rlp/structs.rs | 7 +++++++ crates/core/src/types/block.rs | 34 ++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/crates/core/src/rlp/structs.rs b/crates/core/src/rlp/structs.rs index a47de5f9032..20a5cb93927 100644 --- a/crates/core/src/rlp/structs.rs +++ b/crates/core/src/rlp/structs.rs @@ -144,6 +144,13 @@ impl<'a> Encoder<'a> { self } + pub fn encode_optional_field(mut self, opt_value: &Option) -> Self { + if let Some(value) = opt_value { + ::encode(value, &mut self.temp_buf); + } + self + } + pub fn finish(self) { encode_length(self.temp_buf.len(), self.buf); self.buf.put_slice(&self.temp_buf); diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index f16b1be2bfe..98ceecf6ef4 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -1,4 +1,7 @@ -use crate::{rlp::encode::RLPEncode, Address, H256, U256}; +use crate::{ + rlp::{encode::RLPEncode, structs}, + Address, H256, U256, +}; use bytes::Bytes; use patricia_merkle_tree::PatriciaMerkleTree; use sha3::Keccak256; @@ -174,21 +177,24 @@ pub struct LegacyTransaction { impl RLPEncode for LegacyTransaction { fn encode(&self, buf: &mut dyn bytes::BufMut) { - // TODO: prepend size header - self.nonce.encode(buf); - self.gas_price.encode(buf); - self.gas.encode(buf); + let encoder = structs::Encoder::new(buf) + .encode_field(&self.nonce) + .encode_field(&self.gas_price) + .encode_field(&self.gas); + // TODO: implement encode for Option? - match &self.to { - Some(to) => to.encode(buf), + let encoder = match &self.to { + Some(to) => encoder.encode_field(to), // TODO: move to a constant? - None => buf.put_u8(0x80), - } - self.value.encode(buf); - self.data.encode(buf); - self.v.encode(buf); - self.r.encode(buf); - self.s.encode(buf); + None => encoder.encode_field(&0_u64), + }; + encoder + .encode_field(&self.value) + .encode_field(&self.data) + .encode_field(&self.v) + .encode_field(&self.r) + .encode_field(&self.s) + .finish(); } } From 49f03744001cd9eed671e2709c71e12fb4e68a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:33:38 -0300 Subject: [PATCH 03/11] fix: RLP encode index --- crates/core/src/types/block.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index 98ceecf6ef4..39e29177cdb 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -85,25 +85,22 @@ impl BlockBody { .map(|(i, tx)| { // TODO: check if tree is RLP encoding the value - // I think the tree is RLP encoding the key, so this is not needed? - // let mut k = Vec::new(); - // i.encode(&mut k); + // Key: RLP(tx_index) + let mut k = Vec::new(); + i.encode(&mut k); + // Value: tx_type || RLP(tx) if tx_type != 0 + // RLP(tx) else let mut v = Vec::new(); - let tx_type = tx.tx_type(); - - // Legacy transactions don't have a prefix - if tx_type != 0 { - v.push(tx_type); + match tx { + // Legacy transactions don't have a prefix + Transaction::LegacyTransaction(_) => {} + _ => v.push(tx.tx_type()), } + tx.encode(&mut v); - dbg!(&v); - dbg!(v.len()); - // Key: RLP(tx_index) - // Value: tx_type || RLP(tx) if tx_type != 0 - // RLP(tx) else - (i.to_be_bytes(), v) + (k, v) }) .collect(); let root = PatriciaMerkleTree::<_, _, Keccak256>::compute_hash_from_sorted_iter( From a81897f16494d25fc574b57247e95631a676b183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:23:16 -0300 Subject: [PATCH 04/11] refactor: add `Transaction::encode_with_type` --- crates/core/src/types/block.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index c775bafe795..6bc3b2c0d80 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -85,8 +85,6 @@ impl BlockBody { .iter() .enumerate() .map(|(i, tx)| { - // TODO: check if tree is RLP encoding the value - // Key: RLP(tx_index) let mut k = Vec::new(); i.encode(&mut k); @@ -94,13 +92,7 @@ impl BlockBody { // Value: tx_type || RLP(tx) if tx_type != 0 // RLP(tx) else let mut v = Vec::new(); - match tx { - // Legacy transactions don't have a prefix - Transaction::LegacyTransaction(_) => {} - _ => v.push(tx.tx_type()), - } - - tx.encode(&mut v); + tx.encode_with_type(&mut v); (k, v) }) @@ -148,6 +140,18 @@ pub enum Transaction { } impl Transaction { + pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) { + // tx_type || RLP(tx) if tx_type != 0 + // RLP(tx) else + match self { + // Legacy transactions don't have a prefix + Transaction::LegacyTransaction(_) => {} + _ => buf.put_u8(self.tx_type()), + } + + self.encode(buf); + } + pub fn tx_type(&self) -> u8 { match self { Transaction::LegacyTransaction(_) => 0, From b966cf45644005545a57d46275c358a2226015d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:29:34 -0300 Subject: [PATCH 05/11] refactor: add `TxType` enum for encoding tx type --- crates/core/src/types/block.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index 6bc3b2c0d80..f7144a2bd66 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -139,6 +139,14 @@ pub enum Transaction { EIP1559Transaction(EIP1559Transaction), } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TxType { + Legacy = 0x00, + EIP2930 = 0x01, + EIP1559 = 0x02, + EIP4844 = 0x03, +} + impl Transaction { pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) { // tx_type || RLP(tx) if tx_type != 0 @@ -146,16 +154,16 @@ impl Transaction { match self { // Legacy transactions don't have a prefix Transaction::LegacyTransaction(_) => {} - _ => buf.put_u8(self.tx_type()), + _ => buf.put_u8(self.tx_type() as u8), } self.encode(buf); } - pub fn tx_type(&self) -> u8 { + pub fn tx_type(&self) -> TxType { match self { - Transaction::LegacyTransaction(_) => 0, - Transaction::EIP1559Transaction(_) => 2, + Transaction::LegacyTransaction(_) => TxType::Legacy, + Transaction::EIP1559Transaction(_) => TxType::EIP1559, } } } From 2c59b6df04f3b53dcccd95c03e541124d1b3f803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:33:02 -0300 Subject: [PATCH 06/11] refactor: add `TxKind` struct --- crates/core/src/types/block.rs | 55 +++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index f7144a2bd66..a7fd404f9b7 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -1,5 +1,8 @@ use crate::{ - rlp::{encode::RLPEncode, structs::Encoder}, + rlp::{ + constants::RLP_NULL, decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, + structs::Encoder, + }, Address, H256, U256, }; use bytes::Bytes; @@ -177,12 +180,40 @@ impl RLPEncode for Transaction { } } +/// The transaction's kind: call or create. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TxKind { + Call(Address), + Create, +} + +impl RLPEncode for TxKind { + fn encode(&self, buf: &mut dyn bytes::BufMut) { + match self { + Self::Call(address) => address.encode(buf), + Self::Create => buf.put_u8(RLP_NULL), + } + } +} + +impl RLPDecode for TxKind { + fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> { + let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?; + if *first_byte == RLP_NULL { + return Ok((Self::Create, &rlp[1..])); + } + Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest)) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct LegacyTransaction { nonce: u64, gas_price: U256, gas: u64, - to: Option
, + /// The recipient of the transaction. + /// Create transactions contain a [`null`](RLP_NULL) value in this field. + to: TxKind, value: U256, data: Bytes, v: U256, @@ -192,18 +223,11 @@ pub struct LegacyTransaction { impl RLPEncode for LegacyTransaction { fn encode(&self, buf: &mut dyn bytes::BufMut) { - let encoder = Encoder::new(buf) + Encoder::new(buf) .encode_field(&self.nonce) .encode_field(&self.gas_price) - .encode_field(&self.gas); - - // TODO: implement encode for Option? - let encoder = match &self.to { - Some(to) => encoder.encode_field(to), - // TODO: move to a constant? - None => encoder.encode_field(&0_u64), - }; - encoder + .encode_field(&self.gas) + .encode_field(&self.to) .encode_field(&self.value) .encode_field(&self.data) .encode_field(&self.v) @@ -253,7 +277,10 @@ mod tests { use hex_literal::hex; use super::{BlockBody, LegacyTransaction}; - use crate::{types::Transaction, U256}; + use crate::{ + types::{Transaction, TxKind}, + U256, + }; #[test] fn test_compute_transactions_root() { @@ -262,7 +289,7 @@ mod tests { nonce: 0, gas_price: 0x0a.into(), gas: 0x05f5e100, - to: Some(hex!("1000000000000000000000000000000000000000").into()), + to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()), value: 0.into(), data: Default::default(), v: U256::from(0x1b), From 67f1db0ffd77820ea8da5ca21a389b94c4e3d578 Mon Sep 17 00:00:00 2001 From: ricomateo Date: Tue, 2 Jul 2024 15:16:57 -0300 Subject: [PATCH 07/11] add receipts root computation --- crates/core/src/types/block.rs | 51 ++++++++++++++++++++++++++++++-- crates/core/src/types/receipt.rs | 43 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index a7fd404f9b7..ffd2aff740e 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -3,6 +3,7 @@ use crate::{ constants::RLP_NULL, decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, structs::Encoder, }, + types::Receipt, Address, H256, U256, }; use bytes::Bytes; @@ -105,6 +106,28 @@ impl BlockBody { ); H256(root.into()) } + + pub fn compute_receipts_root(&self, receipts: Vec) -> H256 { + let receipts_iter: Vec<_> = receipts + .iter() + .enumerate() + .map(|(i, receipt)| { + // Key: RLP(index) + let mut k = Vec::new(); + i.encode(&mut k); + + // Value: tx_type || RLP(tx) if tx_type != 0 + // RLP(tx) else + let mut v = Vec::new(); + receipt.encode_with_type(&mut v); + + (k, v) + }) + .collect(); + let root = + PatriciaMerkleTree::<_, _, Keccak256>::compute_hash_from_sorted_iter(&receipts_iter); + H256(root.into()) + } } impl RLPEncode for BlockBody { @@ -274,11 +297,13 @@ impl RLPEncode for EIP1559Transaction { #[cfg(test)] mod tests { + use ethereum_types::H160; use hex_literal::hex; + use keccak_hash::H256; - use super::{BlockBody, LegacyTransaction}; + use super::{BlockBody, Bytes, LegacyTransaction, TxType}; use crate::{ - types::{Transaction, TxKind}, + types::{Log, Receipt, Transaction, TxKind}, U256, }; @@ -307,4 +332,26 @@ mod tests { assert_eq!(result, expected_root.into()); } + + #[test] + fn test_compute_receipts_root() { + let body = BlockBody::empty(); + let tx_type = TxType::EIP1559; + let succeeded = true; + let topic1 = H256::from_slice(&[0x01; 32]); + let topic2 = H256::from_slice(&[0x02; 32]); + let log = Log::new( + H160::from_slice(&[0x01; 20]), + vec![topic1, topic2], + Bytes::from("beef"), + ); + let logs = vec![log]; + let bloom = [0x03; 256]; + let cumulative_gas_used = 1024; + let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, bloom, logs); + let result = body.compute_receipts_root(vec![receipt]); + let expected_root = + hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb"); + assert_eq!(result, expected_root.into()); + } } diff --git a/crates/core/src/types/receipt.rs b/crates/core/src/types/receipt.rs index 90f3195ec83..35920c3bcb0 100644 --- a/crates/core/src/types/receipt.rs +++ b/crates/core/src/types/receipt.rs @@ -2,17 +2,50 @@ use crate::rlp::{encode::RLPEncode, structs::Encoder}; use crate::types::Bloom; use bytes::Bytes; use ethereum_types::{Address, H256}; + +use super::TxType; pub type Index = u64; /// Result of a transaction #[derive(Clone, Debug, PartialEq, Eq)] pub struct Receipt { + tx_type: TxType, succeeded: bool, cumulative_gas_used: u64, bloom: Bloom, logs: Vec, } +impl Receipt { + pub fn new( + tx_type: TxType, + succeeded: bool, + cumulative_gas_used: u64, + bloom: Bloom, + logs: Vec, + ) -> Self { + Self { + tx_type, + succeeded, + cumulative_gas_used, + bloom, + logs, + } + } + + pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) { + // tx_type || RLP(tx) if tx_type != 0 + // RLP(tx) else + match self.tx_type { + // Legacy transactions don't have a prefix + TxType::Legacy => {} + _ => buf.put_u8(self.tx_type as u8), + } + + self.encode(buf); + } +} + impl RLPEncode for Receipt { fn encode(&self, buf: &mut dyn bytes::BufMut) { Encoder::new(buf) @@ -32,6 +65,16 @@ pub struct Log { data: Bytes, } +impl Log { + pub fn new(address: Address, topics: Vec, data: Bytes) -> Self { + Self { + address, + topics, + data, + } + } +} + impl RLPEncode for Log { fn encode(&self, buf: &mut dyn bytes::BufMut) { Encoder::new(buf) From 3f2dac6e04ebca639e79319845adc8325bef4864 Mon Sep 17 00:00:00 2001 From: ricomateo Date: Tue, 2 Jul 2024 17:07:18 -0300 Subject: [PATCH 08/11] fix: the receipt trie value was being computed wrong the value must be rlp(receipt) instead of tx_type || rlp(receipt) (if tx_type != 0) --- crates/core/src/types/block.rs | 35 ++++++++++++-------------------- crates/core/src/types/receipt.rs | 24 +--------------------- 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index ffd2aff740e..05309c868cc 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -116,10 +116,9 @@ impl BlockBody { let mut k = Vec::new(); i.encode(&mut k); - // Value: tx_type || RLP(tx) if tx_type != 0 - // RLP(tx) else + // Value: RLP(receipt) let mut v = Vec::new(); - receipt.encode_with_type(&mut v); + receipt.encode(&mut v); (k, v) }) @@ -297,15 +296,12 @@ impl RLPEncode for EIP1559Transaction { #[cfg(test)] mod tests { - use ethereum_types::H160; - use hex_literal::hex; - use keccak_hash::H256; - - use super::{BlockBody, Bytes, LegacyTransaction, TxType}; + use super::{BlockBody, LegacyTransaction}; use crate::{ - types::{Log, Receipt, Transaction, TxKind}, + types::{Receipt, Transaction, TxKind}, U256, }; + use hex_literal::hex; #[test] fn test_compute_transactions_root() { @@ -335,23 +331,18 @@ mod tests { #[test] fn test_compute_receipts_root() { + // example taken from + // https://github.com/ethereum/go-ethereum/blob/f8aa62353666a6368fb3f1a378bd0a82d1542052/cmd/evm/testdata/1/exp.json#L18 let body = BlockBody::empty(); - let tx_type = TxType::EIP1559; let succeeded = true; - let topic1 = H256::from_slice(&[0x01; 32]); - let topic2 = H256::from_slice(&[0x02; 32]); - let log = Log::new( - H160::from_slice(&[0x01; 20]), - vec![topic1, topic2], - Bytes::from("beef"), - ); - let logs = vec![log]; - let bloom = [0x03; 256]; - let cumulative_gas_used = 1024; - let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, bloom, logs); + let cumulative_gas_used = 0x5208; + let bloom = [0x00; 256]; + let logs = vec![]; + let receipt = Receipt::new(succeeded, cumulative_gas_used, bloom, logs); + let result = body.compute_receipts_root(vec![receipt]); let expected_root = - hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb"); + hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2"); assert_eq!(result, expected_root.into()); } } diff --git a/crates/core/src/types/receipt.rs b/crates/core/src/types/receipt.rs index 35920c3bcb0..2ff4874c954 100644 --- a/crates/core/src/types/receipt.rs +++ b/crates/core/src/types/receipt.rs @@ -2,14 +2,11 @@ use crate::rlp::{encode::RLPEncode, structs::Encoder}; use crate::types::Bloom; use bytes::Bytes; use ethereum_types::{Address, H256}; - -use super::TxType; pub type Index = u64; /// Result of a transaction #[derive(Clone, Debug, PartialEq, Eq)] pub struct Receipt { - tx_type: TxType, succeeded: bool, cumulative_gas_used: u64, bloom: Bloom, @@ -17,33 +14,14 @@ pub struct Receipt { } impl Receipt { - pub fn new( - tx_type: TxType, - succeeded: bool, - cumulative_gas_used: u64, - bloom: Bloom, - logs: Vec, - ) -> Self { + pub fn new(succeeded: bool, cumulative_gas_used: u64, bloom: Bloom, logs: Vec) -> Self { Self { - tx_type, succeeded, cumulative_gas_used, bloom, logs, } } - - pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) { - // tx_type || RLP(tx) if tx_type != 0 - // RLP(tx) else - match self.tx_type { - // Legacy transactions don't have a prefix - TxType::Legacy => {} - _ => buf.put_u8(self.tx_type as u8), - } - - self.encode(buf); - } } impl RLPEncode for Receipt { From 8e2369958ef6516c569df205b9094a14df90f7bc Mon Sep 17 00:00:00 2001 From: ricomateo Date: Tue, 2 Jul 2024 17:44:13 -0300 Subject: [PATCH 09/11] fix: compute receipts trie value correctly the previous way in which the receipts trie values were computed was the right way --- crates/core/src/types/block.rs | 10 ++++++---- crates/core/src/types/receipt.rs | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index 05309c868cc..238ff110a0c 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -116,9 +116,10 @@ impl BlockBody { let mut k = Vec::new(); i.encode(&mut k); - // Value: RLP(receipt) + // Value: tx_type || RLP(receipt) if tx_type != 0 + // RLP(receipt) else let mut v = Vec::new(); - receipt.encode(&mut v); + receipt.encode_with_type(&mut v); (k, v) }) @@ -298,7 +299,7 @@ impl RLPEncode for EIP1559Transaction { mod tests { use super::{BlockBody, LegacyTransaction}; use crate::{ - types::{Receipt, Transaction, TxKind}, + types::{Receipt, Transaction, TxKind, TxType}, U256, }; use hex_literal::hex; @@ -334,11 +335,12 @@ mod tests { // example taken from // https://github.com/ethereum/go-ethereum/blob/f8aa62353666a6368fb3f1a378bd0a82d1542052/cmd/evm/testdata/1/exp.json#L18 let body = BlockBody::empty(); + let tx_type = TxType::Legacy; let succeeded = true; let cumulative_gas_used = 0x5208; let bloom = [0x00; 256]; let logs = vec![]; - let receipt = Receipt::new(succeeded, cumulative_gas_used, bloom, logs); + let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, bloom, logs); let result = body.compute_receipts_root(vec![receipt]); let expected_root = diff --git a/crates/core/src/types/receipt.rs b/crates/core/src/types/receipt.rs index 2ff4874c954..59aec62b181 100644 --- a/crates/core/src/types/receipt.rs +++ b/crates/core/src/types/receipt.rs @@ -2,11 +2,14 @@ use crate::rlp::{encode::RLPEncode, structs::Encoder}; use crate::types::Bloom; use bytes::Bytes; use ethereum_types::{Address, H256}; + +use super::TxType; pub type Index = u64; /// Result of a transaction #[derive(Clone, Debug, PartialEq, Eq)] pub struct Receipt { + tx_type: TxType, succeeded: bool, cumulative_gas_used: u64, bloom: Bloom, @@ -14,14 +17,31 @@ pub struct Receipt { } impl Receipt { - pub fn new(succeeded: bool, cumulative_gas_used: u64, bloom: Bloom, logs: Vec) -> Self { + pub fn new( + tx_type: TxType, + succeeded: bool, + cumulative_gas_used: u64, + bloom: Bloom, + logs: Vec, + ) -> Self { Self { + tx_type, succeeded, cumulative_gas_used, bloom, logs, } } + + pub fn encode_with_type(&self, buf: &mut dyn bytes::BufMut) { + // tx_type || RLP(receipt) if tx_type != 0 + // RLP(receipt) else + match self.tx_type { + TxType::Legacy => {} + _ => buf.put_u8(self.tx_type as u8), + } + self.encode(buf); + } } impl RLPEncode for Receipt { From 3b32d29535b9fabac1bace6206dfc3fbf0f24f5e Mon Sep 17 00:00:00 2001 From: ricomateo Date: Tue, 2 Jul 2024 17:49:07 -0300 Subject: [PATCH 10/11] remove unnecessary constructor --- crates/core/src/types/receipt.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/crates/core/src/types/receipt.rs b/crates/core/src/types/receipt.rs index 59aec62b181..9bdc4a115f0 100644 --- a/crates/core/src/types/receipt.rs +++ b/crates/core/src/types/receipt.rs @@ -63,16 +63,6 @@ pub struct Log { data: Bytes, } -impl Log { - pub fn new(address: Address, topics: Vec, data: Bytes) -> Self { - Self { - address, - topics, - data, - } - } -} - impl RLPEncode for Log { fn encode(&self, buf: &mut dyn bytes::BufMut) { Encoder::new(buf) From f02abf163aae731991b4bdf53e22a087affbde89 Mon Sep 17 00:00:00 2001 From: ricomateo Date: Wed, 3 Jul 2024 09:22:02 -0300 Subject: [PATCH 11/11] fix: move compute_receipts_root out of BlockBody impl --- crates/core/src/types/block.rs | 37 ++++++++++++++-------------- crates/core/src/types/transaction.rs | 8 +++--- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/crates/core/src/types/block.rs b/crates/core/src/types/block.rs index e094897aea2..1aeb98c5966 100644 --- a/crates/core/src/types/block.rs +++ b/crates/core/src/types/block.rs @@ -105,28 +105,27 @@ impl BlockBody { ); H256(root.into()) } +} - pub fn compute_receipts_root(&self, receipts: Vec) -> H256 { - let receipts_iter: Vec<_> = receipts - .iter() - .enumerate() - .map(|(i, receipt)| { - // Key: RLP(index) - let mut k = Vec::new(); - i.encode(&mut k); +pub fn compute_receipts_root(receipts: Vec) -> H256 { + let receipts_iter: Vec<_> = receipts + .iter() + .enumerate() + .map(|(i, receipt)| { + // Key: RLP(index) + let mut k = Vec::new(); + i.encode(&mut k); - // Value: tx_type || RLP(receipt) if tx_type != 0 - // RLP(receipt) else - let mut v = Vec::new(); - receipt.encode_with_type(&mut v); + // Value: tx_type || RLP(receipt) if tx_type != 0 + // RLP(receipt) else + let mut v = Vec::new(); + receipt.encode_with_type(&mut v); - (k, v) - }) - .collect(); - let root = - PatriciaMerkleTree::<_, _, Keccak256>::compute_hash_from_sorted_iter(&receipts_iter); - H256(root.into()) - } + (k, v) + }) + .collect(); + let root = PatriciaMerkleTree::<_, _, Keccak256>::compute_hash_from_sorted_iter(&receipts_iter); + H256(root.into()) } impl RLPEncode for BlockBody { diff --git a/crates/core/src/types/transaction.rs b/crates/core/src/types/transaction.rs index 342eb8b4b4b..b56dfe2c075 100644 --- a/crates/core/src/types/transaction.rs +++ b/crates/core/src/types/transaction.rs @@ -285,7 +285,10 @@ mod tests { use hex_literal::hex; use crate::{ - types::{BlockBody, LegacyTransaction, Receipt, Transaction, TxKind, TxType}, + types::{ + compute_receipts_root, BlockBody, LegacyTransaction, Receipt, Transaction, TxKind, + TxType, + }, U256, }; @@ -319,7 +322,6 @@ mod tests { fn test_compute_receipts_root() { // example taken from // https://github.com/ethereum/go-ethereum/blob/f8aa62353666a6368fb3f1a378bd0a82d1542052/cmd/evm/testdata/1/exp.json#L18 - let body = BlockBody::empty(); let tx_type = TxType::Legacy; let succeeded = true; let cumulative_gas_used = 0x5208; @@ -327,7 +329,7 @@ mod tests { let logs = vec![]; let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, bloom, logs); - let result = body.compute_receipts_root(vec![receipt]); + let result = compute_receipts_root(vec![receipt]); let expected_root = hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2"); assert_eq!(result, expected_root.into());