feat: handle full tx blocks (#75)

* fix: implement full tx blocks

* better error handling

* fix tests
This commit is contained in:
Noah Citron 2022-10-27 13:46:32 -04:00 committed by GitHub
parent 5e53cd6300
commit 4444148f71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 20 deletions

View File

@ -141,12 +141,28 @@ impl<DB: Database> Client<DB> {
self.node.read().await.get_block_number() self.node.read().await.get_block_number()
} }
pub async fn get_block_by_number(&self, block: &BlockTag) -> Result<Option<ExecutionBlock>> { pub async fn get_block_by_number(
self.node.read().await.get_block_by_number(block) &self,
block: &BlockTag,
full_tx: bool,
) -> Result<Option<ExecutionBlock>> {
self.node
.read()
.await
.get_block_by_number(block, full_tx)
.await
} }
pub async fn get_block_by_hash(&self, hash: &Vec<u8>) -> Result<Option<ExecutionBlock>> { pub async fn get_block_by_hash(
self.node.read().await.get_block_by_hash(hash) &self,
hash: &Vec<u8>,
full_tx: bool,
) -> Result<Option<ExecutionBlock>> {
self.node
.read()
.await
.get_block_by_hash(hash, full_tx)
.await
} }
pub async fn chain_id(&self) -> u64 { pub async fn chain_id(&self) -> u64 {

View File

@ -193,16 +193,28 @@ impl Node {
Ok(payload.block_number) Ok(payload.block_number)
} }
pub fn get_block_by_number(&self, block: &BlockTag) -> Result<Option<ExecutionBlock>> { pub async fn get_block_by_number(
&self,
block: &BlockTag,
full_tx: bool,
) -> Result<Option<ExecutionBlock>> {
self.check_blocktag_age(block)?; self.check_blocktag_age(block)?;
match self.get_payload(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), Err(_) => Ok(None),
} }
} }
pub fn get_block_by_hash(&self, hash: &Vec<u8>) -> Result<Option<ExecutionBlock>> { pub async fn get_block_by_hash(
&self,
hash: &Vec<u8>,
full_tx: bool,
) -> Result<Option<ExecutionBlock>> {
let payloads = self let payloads = self
.payloads .payloads
.iter() .iter()
@ -210,7 +222,11 @@ impl Node {
.collect::<Vec<(&u64, &ExecutionPayload)>>(); .collect::<Vec<(&u64, &ExecutionPayload)>>();
match payloads.get(0) { 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), None => Ok(None),
} }
} }

View File

@ -175,21 +175,21 @@ impl EthRpcServer for RpcInner {
async fn get_block_by_number( async fn get_block_by_number(
&self, &self,
block: BlockTag, block: BlockTag,
_full_tx: bool, full_tx: bool,
) -> Result<Option<ExecutionBlock>, Error> { ) -> Result<Option<ExecutionBlock>, Error> {
let node = self.node.read().await; 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) Ok(block)
} }
async fn get_block_by_hash( async fn get_block_by_hash(
&self, &self,
hash: &str, hash: &str,
_full_tx: bool, full_tx: bool,
) -> Result<Option<ExecutionBlock>, Error> { ) -> Result<Option<ExecutionBlock>, Error> {
let hash = convert_err(hex_str_to_bytes(hash))?; let hash = convert_err(hex_str_to_bytes(hash))?;
let node = self.node.read().await; 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) Ok(block)
} }

View File

@ -15,6 +15,7 @@ use revm::KECCAK_EMPTY;
use triehash_ethereum::ordered_trie_root; use triehash_ethereum::ordered_trie_root;
use crate::errors::ExecutionError; use crate::errors::ExecutionError;
use crate::types::Transactions;
use super::proof::{encode_account, verify_proof}; use super::proof::{encode_account, verify_proof};
use super::rpc::Rpc; use super::rpc::Rpc;
@ -114,16 +115,41 @@ impl<R: Rpc> ExecutionClient<R> {
self.rpc.send_raw_transaction(bytes).await self.rpc.send_raw_transaction(bytes).await
} }
pub fn get_block(&self, payload: &ExecutionPayload) -> Result<ExecutionBlock> { pub async fn get_block(
&self,
payload: &ExecutionPayload,
full_tx: bool,
) -> Result<ExecutionBlock> {
let empty_nonce = "0x0000000000000000".to_string(); let empty_nonce = "0x0000000000000000".to_string();
let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"; let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347";
let txs = payload let tx_hashes = payload
.transactions .transactions
.iter() .iter()
.map(|tx| H256::from_slice(&keccak256(tx.to_vec()))) .map(|tx| H256::from_slice(&keccak256(tx.to_vec())))
.collect::<Vec<H256>>(); .collect::<Vec<H256>>();
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::<Result<Vec<_>>>()?;
Transactions::Full(txs)
} else {
Transactions::Hashes(tx_hashes)
};
Ok(ExecutionBlock { Ok(ExecutionBlock {
number: payload.block_number, number: payload.block_number,
base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()), base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()),

View File

@ -1,8 +1,11 @@
use std::{collections::HashMap, fmt}; use std::{collections::HashMap, fmt};
use ethers::prelude::{Address, H256, U256}; use ethers::{
prelude::{Address, H256, U256},
types::Transaction,
};
use eyre::Result; use eyre::Result;
use serde::{Deserialize, Serialize}; use serde::{ser::SerializeSeq, Deserialize, Serialize};
use common::utils::u64_to_hex_string; use common::utils::u64_to_hex_string;
@ -45,11 +48,18 @@ pub struct ExecutionBlock {
pub timestamp: u64, pub timestamp: u64,
#[serde(serialize_with = "serialize_u64_string")] #[serde(serialize_with = "serialize_u64_string")]
pub total_difficulty: u64, pub total_difficulty: u64,
pub transactions: Vec<H256>, #[serde(serialize_with = "serialize_transactions")]
pub transactions: Transactions,
pub transactions_root: H256, pub transactions_root: H256,
pub uncles: Vec<H256>, pub uncles: Vec<H256>,
} }
#[derive(Deserialize, Serialize, Debug)]
pub enum Transactions {
Hashes(Vec<H256>),
Full(Vec<Transaction>),
}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CallOpts { pub struct CallOpts {
@ -102,3 +112,27 @@ where
let num_string = u64_to_hex_string(*x); let num_string = u64_to_hex_string(*x);
s.serialize_str(&num_string) s.serialize_str(&num_string)
} }
fn serialize_transactions<S>(txs: &Transactions, s: S) -> Result<S::Ok, S::Error>
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()
}
}
}

View File

@ -157,13 +157,13 @@ async fn test_get_receipt_not_included() {
assert!(receipt_opt.is_none()); assert!(receipt_opt.is_none());
} }
#[test] #[tokio::test]
fn test_get_block() { async fn test_get_block() {
let execution = get_client(); let execution = get_client();
let mut payload = ExecutionPayload::default(); let mut payload = ExecutionPayload::default();
payload.block_number = 12345; 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); assert_eq!(block.number, 12345);
} }