From 4444148f71b269c1c5ba17d9714cbe9a2b0bd79d Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Thu, 27 Oct 2022 13:46:32 -0400 Subject: [PATCH] feat: handle full tx blocks (#75) * fix: implement full tx blocks * better error handling * fix tests --- client/src/client.rs | 24 ++++++++++++++++++---- client/src/node.rs | 24 ++++++++++++++++++---- client/src/rpc.rs | 8 ++++---- execution/src/execution.rs | 30 +++++++++++++++++++++++++-- execution/src/types.rs | 40 +++++++++++++++++++++++++++++++++--- execution/tests/execution.rs | 6 +++--- 6 files changed, 112 insertions(+), 20 deletions(-) diff --git a/client/src/client.rs b/client/src/client.rs index 15e5947..4cfa349 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -141,12 +141,28 @@ impl Client { self.node.read().await.get_block_number() } - pub async fn get_block_by_number(&self, block: &BlockTag) -> Result> { - self.node.read().await.get_block_by_number(block) + pub async fn get_block_by_number( + &self, + block: &BlockTag, + full_tx: bool, + ) -> Result> { + self.node + .read() + .await + .get_block_by_number(block, full_tx) + .await } - pub async fn get_block_by_hash(&self, hash: &Vec) -> Result> { - self.node.read().await.get_block_by_hash(hash) + pub async fn get_block_by_hash( + &self, + hash: &Vec, + full_tx: bool, + ) -> Result> { + self.node + .read() + .await + .get_block_by_hash(hash, full_tx) + .await } pub async fn chain_id(&self) -> u64 { diff --git a/client/src/node.rs b/client/src/node.rs index 6a6d89a..9618dda 100644 --- a/client/src/node.rs +++ b/client/src/node.rs @@ -193,16 +193,28 @@ impl Node { Ok(payload.block_number) } - pub fn get_block_by_number(&self, block: &BlockTag) -> Result> { + pub async fn get_block_by_number( + &self, + block: &BlockTag, + full_tx: bool, + ) -> Result> { self.check_blocktag_age(block)?; match self.get_payload(block) { - Ok(payload) => self.execution.get_block(payload).map(|b| Some(b)), + Ok(payload) => self + .execution + .get_block(payload, full_tx) + .await + .map(|b| Some(b)), Err(_) => Ok(None), } } - pub fn get_block_by_hash(&self, hash: &Vec) -> Result> { + pub async fn get_block_by_hash( + &self, + hash: &Vec, + full_tx: bool, + ) -> Result> { let payloads = self .payloads .iter() @@ -210,7 +222,11 @@ impl Node { .collect::>(); match payloads.get(0) { - Some(payload_entry) => self.execution.get_block(payload_entry.1).map(|b| Some(b)), + Some(payload_entry) => self + .execution + .get_block(payload_entry.1, full_tx) + .await + .map(|b| Some(b)), None => Ok(None), } } diff --git a/client/src/rpc.rs b/client/src/rpc.rs index 7cd89ba..a3b7e1f 100644 --- a/client/src/rpc.rs +++ b/client/src/rpc.rs @@ -175,21 +175,21 @@ impl EthRpcServer for RpcInner { async fn get_block_by_number( &self, block: BlockTag, - _full_tx: bool, + full_tx: bool, ) -> Result, Error> { let node = self.node.read().await; - let block = convert_err(node.get_block_by_number(&block))?; + let block = convert_err(node.get_block_by_number(&block, full_tx).await)?; Ok(block) } async fn get_block_by_hash( &self, hash: &str, - _full_tx: bool, + full_tx: bool, ) -> Result, Error> { let hash = convert_err(hex_str_to_bytes(hash))?; let node = self.node.read().await; - let block = convert_err(node.get_block_by_hash(&hash))?; + let block = convert_err(node.get_block_by_hash(&hash, full_tx).await)?; Ok(block) } diff --git a/execution/src/execution.rs b/execution/src/execution.rs index 21f06d5..17f3495 100644 --- a/execution/src/execution.rs +++ b/execution/src/execution.rs @@ -15,6 +15,7 @@ use revm::KECCAK_EMPTY; use triehash_ethereum::ordered_trie_root; use crate::errors::ExecutionError; +use crate::types::Transactions; use super::proof::{encode_account, verify_proof}; use super::rpc::Rpc; @@ -114,16 +115,41 @@ impl ExecutionClient { self.rpc.send_raw_transaction(bytes).await } - pub fn get_block(&self, payload: &ExecutionPayload) -> Result { + pub async fn get_block( + &self, + payload: &ExecutionPayload, + full_tx: bool, + ) -> Result { let empty_nonce = "0x0000000000000000".to_string(); let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"; - let txs = payload + let tx_hashes = payload .transactions .iter() .map(|tx| H256::from_slice(&keccak256(tx.to_vec()))) .collect::>(); + let txs = if full_tx { + let txs_fut = tx_hashes.iter().map(|hash| async move { + let mut payloads = BTreeMap::new(); + payloads.insert(payload.block_number, payload.clone()); + let tx = self + .get_transaction(hash, &payloads) + .await? + .ok_or(eyre::eyre!("not reachable"))?; + + Ok(tx) + }); + + let txs = join_all(txs_fut) + .await + .into_iter() + .collect::>>()?; + Transactions::Full(txs) + } else { + Transactions::Hashes(tx_hashes) + }; + Ok(ExecutionBlock { number: payload.block_number, base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()), diff --git a/execution/src/types.rs b/execution/src/types.rs index 461e39b..38c574d 100644 --- a/execution/src/types.rs +++ b/execution/src/types.rs @@ -1,8 +1,11 @@ use std::{collections::HashMap, fmt}; -use ethers::prelude::{Address, H256, U256}; +use ethers::{ + prelude::{Address, H256, U256}, + types::Transaction, +}; use eyre::Result; -use serde::{Deserialize, Serialize}; +use serde::{ser::SerializeSeq, Deserialize, Serialize}; use common::utils::u64_to_hex_string; @@ -45,11 +48,18 @@ pub struct ExecutionBlock { pub timestamp: u64, #[serde(serialize_with = "serialize_u64_string")] pub total_difficulty: u64, - pub transactions: Vec, + #[serde(serialize_with = "serialize_transactions")] + pub transactions: Transactions, pub transactions_root: H256, pub uncles: Vec, } +#[derive(Deserialize, Serialize, Debug)] +pub enum Transactions { + Hashes(Vec), + Full(Vec), +} + #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CallOpts { @@ -102,3 +112,27 @@ where let num_string = u64_to_hex_string(*x); s.serialize_str(&num_string) } + +fn serialize_transactions(txs: &Transactions, s: S) -> Result +where + S: serde::Serializer, +{ + match txs { + Transactions::Hashes(hashes) => { + let mut seq = s.serialize_seq(Some(hashes.len()))?; + for hash in hashes { + seq.serialize_element(&hash)?; + } + + seq.end() + } + Transactions::Full(txs) => { + let mut seq = s.serialize_seq(Some(txs.len()))?; + for tx in txs { + seq.serialize_element(&tx)?; + } + + seq.end() + } + } +} diff --git a/execution/tests/execution.rs b/execution/tests/execution.rs index 555eac9..556471d 100644 --- a/execution/tests/execution.rs +++ b/execution/tests/execution.rs @@ -157,13 +157,13 @@ async fn test_get_receipt_not_included() { assert!(receipt_opt.is_none()); } -#[test] -fn test_get_block() { +#[tokio::test] +async fn test_get_block() { let execution = get_client(); let mut payload = ExecutionPayload::default(); payload.block_number = 12345; - let block = execution.get_block(&payload).unwrap(); + let block = execution.get_block(&payload, false).await.unwrap(); assert_eq!(block.number, 12345); }