feat: handle full tx blocks (#75)
* fix: implement full tx blocks * better error handling * fix tests
This commit is contained in:
parent
5e53cd6300
commit
4444148f71
|
@ -141,12 +141,28 @@ impl<DB: Database> Client<DB> {
|
|||
self.node.read().await.get_block_number()
|
||||
}
|
||||
|
||||
pub async fn get_block_by_number(&self, block: &BlockTag) -> Result<Option<ExecutionBlock>> {
|
||||
self.node.read().await.get_block_by_number(block)
|
||||
pub async fn get_block_by_number(
|
||||
&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>> {
|
||||
self.node.read().await.get_block_by_hash(hash)
|
||||
pub async fn get_block_by_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 {
|
||||
|
|
|
@ -193,16 +193,28 @@ impl Node {
|
|||
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)?;
|
||||
|
||||
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<u8>) -> Result<Option<ExecutionBlock>> {
|
||||
pub async fn get_block_by_hash(
|
||||
&self,
|
||||
hash: &Vec<u8>,
|
||||
full_tx: bool,
|
||||
) -> Result<Option<ExecutionBlock>> {
|
||||
let payloads = self
|
||||
.payloads
|
||||
.iter()
|
||||
|
@ -210,7 +222,11 @@ impl Node {
|
|||
.collect::<Vec<(&u64, &ExecutionPayload)>>();
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,21 +175,21 @@ impl EthRpcServer for RpcInner {
|
|||
async fn get_block_by_number(
|
||||
&self,
|
||||
block: BlockTag,
|
||||
_full_tx: bool,
|
||||
full_tx: bool,
|
||||
) -> Result<Option<ExecutionBlock>, 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<Option<ExecutionBlock>, 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<R: Rpc> ExecutionClient<R> {
|
|||
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_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347";
|
||||
|
||||
let txs = payload
|
||||
let tx_hashes = payload
|
||||
.transactions
|
||||
.iter()
|
||||
.map(|tx| H256::from_slice(&keccak256(tx.to_vec())))
|
||||
.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 {
|
||||
number: payload.block_number,
|
||||
base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()),
|
||||
|
|
|
@ -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<H256>,
|
||||
#[serde(serialize_with = "serialize_transactions")]
|
||||
pub transactions: Transactions,
|
||||
pub transactions_root: H256,
|
||||
pub uncles: Vec<H256>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub enum Transactions {
|
||||
Hashes(Vec<H256>),
|
||||
Full(Vec<Transaction>),
|
||||
}
|
||||
|
||||
#[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<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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue