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()
|
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 {
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue