Compare commits

...

3 Commits

Author SHA1 Message Date
Noah Citron 2e6b948c8f
feat(cli): add version cli flag (#216) 2023-03-15 16:04:09 -04:00
Noah Citron f5760f7a8a
chore: bump version to 0.3.0 (#215) 2023-03-15 15:40:01 -04:00
Noah Citron ba3f31111f
feat: support capella (#214)
* handle forks using superstructs

* fix ssz for superstructs

* add capella types

* fix tests

* clippy

* clippy take two
2023-03-15 00:46:00 -04:00
19 changed files with 309 additions and 107 deletions

70
Cargo.lock generated
View File

@ -524,7 +524,7 @@ dependencies = [
[[package]] [[package]]
name = "cli" name = "cli"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"clap", "clap",
"client", "client",
@ -541,7 +541,7 @@ dependencies = [
[[package]] [[package]]
name = "client" name = "client"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"common", "common",
"config", "config",
@ -645,7 +645,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "common" name = "common"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -657,7 +657,7 @@ dependencies = [
[[package]] [[package]]
name = "config" name = "config"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"common", "common",
"ethers", "ethers",
@ -677,7 +677,7 @@ dependencies = [
[[package]] [[package]]
name = "consensus" name = "consensus"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@ -695,6 +695,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"ssz-rs", "ssz-rs",
"superstruct",
"thiserror", "thiserror",
"tokio", "tokio",
"toml", "toml",
@ -975,6 +976,41 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]] [[package]]
name = "der" name = "der"
version = "0.6.1" version = "0.6.1"
@ -1492,7 +1528,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]] [[package]]
name = "execution" name = "execution"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@ -1990,7 +2026,7 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "helios" name = "helios"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"client", "client",
"common", "common",
@ -2184,6 +2220,12 @@ dependencies = [
"cxx-build", "cxx-build",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.3.0" version = "0.3.0"
@ -4206,6 +4248,20 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "superstruct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f4e1f478a7728f8855d7e620e9a152cf8932c6614f86564c886f9b8141f3201"
dependencies = [
"darling",
"itertools",
"proc-macro2",
"quote",
"smallvec",
"syn",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "helios" name = "helios"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
autobenches = false autobenches = false
exclude = [ exclude = [
@ -17,6 +17,7 @@ members = [
"execution", "execution",
"helios-ts", "helios-ts",
] ]
default-members = ["cli"]
[profile.bench] [profile.bench]
debug = true debug = true

View File

@ -2,7 +2,7 @@ cargo-features = ["different-binary-name"]
[package] [package]
name = "cli" name = "cli"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
[[bin]] [[bin]]

View File

@ -80,6 +80,8 @@ fn get_config() -> Config {
} }
#[derive(Parser)] #[derive(Parser)]
#[clap(version, about)]
/// Helios is a fast, secure, and portable light client for Ethereum
struct Cli { struct Cli {
#[clap(short, long, default_value = "mainnet")] #[clap(short, long, default_value = "mainnet")]
network: String, network: String,

View File

@ -1,6 +1,6 @@
[package] [package]
name = "client" name = "client"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -109,11 +109,11 @@ impl Node {
.map_err(NodeError::ConsensusPayloadError)?; .map_err(NodeError::ConsensusPayloadError)?;
self.payloads self.payloads
.insert(latest_payload.block_number, latest_payload); .insert(*latest_payload.block_number(), latest_payload);
self.payloads self.payloads
.insert(finalized_payload.block_number, finalized_payload.clone()); .insert(*finalized_payload.block_number(), finalized_payload.clone());
self.finalized_payloads self.finalized_payloads
.insert(finalized_payload.block_number, finalized_payload); .insert(*finalized_payload.block_number(), finalized_payload);
let start_slot = self let start_slot = self
.current_slot .current_slot
@ -124,7 +124,7 @@ impl Node {
.await .await
.map_err(NodeError::ConsensusPayloadError)?; .map_err(NodeError::ConsensusPayloadError)?;
for payload in backfill_payloads { for payload in backfill_payloads {
self.payloads.insert(payload.block_number, payload); self.payloads.insert(*payload.block_number(), payload);
} }
self.current_slot = Some(latest_header.slot); self.current_slot = Some(latest_header.slot);
@ -188,14 +188,14 @@ impl Node {
pub fn get_block_transaction_count_by_hash(&self, hash: &Vec<u8>) -> Result<u64> { pub fn get_block_transaction_count_by_hash(&self, hash: &Vec<u8>) -> Result<u64> {
let payload = self.get_payload_by_hash(hash)?; let payload = self.get_payload_by_hash(hash)?;
let transaction_count = payload.1.transactions.len(); let transaction_count = payload.1.transactions().len();
Ok(transaction_count as u64) Ok(transaction_count as u64)
} }
pub fn get_block_transaction_count_by_number(&self, block: BlockTag) -> Result<u64> { pub fn get_block_transaction_count_by_number(&self, block: BlockTag) -> Result<u64> {
let payload = self.get_payload(block)?; let payload = self.get_payload(block)?;
let transaction_count = payload.transactions.len(); let transaction_count = payload.transactions().len();
Ok(transaction_count as u64) Ok(transaction_count as u64)
} }
@ -269,7 +269,7 @@ impl Node {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
let base_fee = U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()); let base_fee = U256::from_little_endian(&payload.base_fee_per_gas().to_bytes_le());
let tip = U256::from(10_u64.pow(9)); let tip = U256::from(10_u64.pow(9));
Ok(base_fee + tip) Ok(base_fee + tip)
} }
@ -284,7 +284,7 @@ impl Node {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
Ok(payload.block_number) Ok(*payload.block_number())
} }
pub async fn get_block_by_number( pub async fn get_block_by_number(
@ -367,7 +367,7 @@ impl Node {
pub fn get_coinbase(&self) -> Result<Address> { pub fn get_coinbase(&self) -> Result<Address> {
self.check_head_age()?; self.check_head_age()?;
let payload = self.get_payload(BlockTag::Latest)?; let payload = self.get_payload(BlockTag::Latest)?;
let coinbase_address = Address::from_slice(&payload.fee_recipient); let coinbase_address = Address::from_slice(payload.fee_recipient());
Ok(coinbase_address) Ok(coinbase_address)
} }
@ -398,7 +398,7 @@ impl Node {
let payloads = self let payloads = self
.payloads .payloads
.iter() .iter()
.filter(|entry| &entry.1.block_hash.to_vec() == hash) .filter(|entry| &entry.1.block_hash().to_vec() == hash)
.collect::<Vec<(&u64, &ExecutionPayload)>>(); .collect::<Vec<(&u64, &ExecutionPayload)>>();
payloads payloads

View File

@ -1,6 +1,6 @@
[package] [package]
name = "common" name = "common"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -1,7 +1,7 @@
[package] [package]
name = "config" name = "config"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -73,7 +73,9 @@ impl Config {
pub fn fork_version(&self, slot: u64) -> Vec<u8> { pub fn fork_version(&self, slot: u64) -> Vec<u8> {
let epoch = slot / 32; let epoch = slot / 32;
if epoch >= self.forks.bellatrix.epoch { if epoch >= self.forks.capella.epoch {
self.forks.capella.fork_version.clone()
} else if epoch >= self.forks.bellatrix.epoch {
self.forks.bellatrix.fork_version.clone() self.forks.bellatrix.fork_version.clone()
} else if epoch >= self.forks.altair.epoch { } else if epoch >= self.forks.altair.epoch {
self.forks.altair.fork_version.clone() self.forks.altair.fork_version.clone()

View File

@ -62,6 +62,10 @@ pub fn mainnet() -> BaseConfig {
epoch: 144896, epoch: 144896,
fork_version: hex_str_to_bytes("0x02000000").unwrap(), fork_version: hex_str_to_bytes("0x02000000").unwrap(),
}, },
capella: Fork {
epoch: u64::MAX, // TODO: set epoch when known
fork_version: hex_str_to_bytes("0x03000000").unwrap(),
},
}, },
max_checkpoint_age: 1_209_600, // 14 days max_checkpoint_age: 1_209_600, // 14 days
} }
@ -96,6 +100,10 @@ pub fn goerli() -> BaseConfig {
epoch: 112260, epoch: 112260,
fork_version: hex_str_to_bytes("0x02001020").unwrap(), fork_version: hex_str_to_bytes("0x02001020").unwrap(),
}, },
capella: Fork {
epoch: 162304,
fork_version: hex_str_to_bytes("0x03001020").unwrap(),
},
}, },
max_checkpoint_age: 1_209_600, // 14 days max_checkpoint_age: 1_209_600, // 14 days
} }

View File

@ -18,6 +18,7 @@ pub struct Forks {
pub genesis: Fork, pub genesis: Fork,
pub altair: Fork, pub altair: Fork,
pub bellatrix: Fork, pub bellatrix: Fork,
pub capella: Fork,
} }
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "consensus" name = "consensus"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@ -19,6 +19,7 @@ log = "0.4.17"
chrono = "0.4.23" chrono = "0.4.23"
thiserror = "1.0.37" thiserror = "1.0.37"
reqwest = { version = "0.11.13", features = ["json"] } reqwest = { version = "0.11.13", features = ["json"] }
superstruct = "0.7.0"
common = { path = "../common" } common = { path = "../common" }
config = { path = "../config" } config = { path = "../config" }

View File

@ -103,7 +103,7 @@ impl<R: ConsensusRpc> ConsensusClient<R> {
) )
.into()) .into())
} else { } else {
Ok(block.body.execution_payload) Ok(block.body.execution_payload().clone())
} }
} }
@ -115,30 +115,33 @@ impl<R: ConsensusRpc> ConsensusClient<R> {
let payloads_fut = (start_slot..end_slot) let payloads_fut = (start_slot..end_slot)
.rev() .rev()
.map(|slot| self.rpc.get_block(slot)); .map(|slot| self.rpc.get_block(slot));
let mut prev_parent_hash: Bytes32 = self let mut prev_parent_hash: Bytes32 = self
.rpc .rpc
.get_block(end_slot) .get_block(end_slot)
.await? .await?
.body .body
.execution_payload .execution_payload()
.parent_hash; .parent_hash()
.clone();
let mut payloads: Vec<ExecutionPayload> = Vec::new(); let mut payloads: Vec<ExecutionPayload> = Vec::new();
for result in join_all(payloads_fut).await { for result in join_all(payloads_fut).await {
if result.is_err() { if result.is_err() {
continue; continue;
} }
let payload = result.unwrap().body.execution_payload; let payload = result.unwrap().body.execution_payload().clone();
if payload.block_hash != prev_parent_hash { if payload.block_hash() != &prev_parent_hash {
warn!( warn!(
"error while backfilling blocks: {}", "error while backfilling blocks: {}",
ConsensusError::InvalidHeaderHash( ConsensusError::InvalidHeaderHash(
format!("{prev_parent_hash:02X?}"), format!("{prev_parent_hash:02X?}"),
format!("{:02X?}", payload.parent_hash), format!("{:02X?}", payload.parent_hash()),
) )
); );
break; break;
} }
prev_parent_hash = payload.parent_hash.clone(); prev_parent_hash = payload.parent_hash().clone();
payloads.push(payload); payloads.push(payload);
} }
Ok(payloads) Ok(payloads)

View File

@ -4,6 +4,7 @@ use ssz_rs::prelude::*;
use common::types::Bytes32; use common::types::Bytes32;
use common::utils::hex_str_to_bytes; use common::utils::hex_str_to_bytes;
use superstruct::superstruct;
pub type BLSPubKey = Vector<u8, 48>; pub type BLSPubKey = Vector<u8, 48>;
pub type SignatureBytes = Vector<u8, 96>; pub type SignatureBytes = Vector<u8, 96>;
@ -24,7 +25,15 @@ pub struct BeaconBlock {
pub body: BeaconBlockBody, pub body: BeaconBlockBody,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] #[superstruct(
variants(Bellatrix, Capella),
variant_attributes(
derive(serde::Deserialize, Clone, Debug, SimpleSerialize, Default),
serde(deny_unknown_fields)
)
)]
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(untagged)]
pub struct BeaconBlockBody { pub struct BeaconBlockBody {
#[serde(deserialize_with = "signature_deserialize")] #[serde(deserialize_with = "signature_deserialize")]
randao_reveal: SignatureBytes, randao_reveal: SignatureBytes,
@ -38,9 +47,79 @@ pub struct BeaconBlockBody {
voluntary_exits: List<SignedVoluntaryExit, 16>, voluntary_exits: List<SignedVoluntaryExit, 16>,
sync_aggregate: SyncAggregate, sync_aggregate: SyncAggregate,
pub execution_payload: ExecutionPayload, pub execution_payload: ExecutionPayload,
#[superstruct(only(Capella))]
bls_to_execution_changes: List<SignedBlsToExecutionChange, 16>,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] impl ssz_rs::Merkleized for BeaconBlockBody {
fn hash_tree_root(&mut self) -> Result<Node, MerkleizationError> {
match self {
BeaconBlockBody::Bellatrix(body) => body.hash_tree_root(),
BeaconBlockBody::Capella(body) => body.hash_tree_root(),
}
}
}
impl ssz_rs::Sized for BeaconBlockBody {
fn is_variable_size() -> bool {
true
}
fn size_hint() -> usize {
0
}
}
impl ssz_rs::Serialize for BeaconBlockBody {
fn serialize(&self, buffer: &mut Vec<u8>) -> Result<usize, SerializeError> {
match self {
BeaconBlockBody::Bellatrix(body) => body.serialize(buffer),
BeaconBlockBody::Capella(body) => body.serialize(buffer),
}
}
}
impl ssz_rs::Deserialize for BeaconBlockBody {
fn deserialize(_encoding: &[u8]) -> Result<Self, DeserializeError>
where
Self: Sized,
{
panic!("not implemented");
}
}
#[derive(Default, Clone, Debug, SimpleSerialize, serde::Deserialize)]
pub struct SignedBlsToExecutionChange {
message: BlsToExecutionChange,
#[serde(deserialize_with = "signature_deserialize")]
signature: SignatureBytes,
}
#[derive(Default, Clone, Debug, SimpleSerialize, serde::Deserialize)]
pub struct BlsToExecutionChange {
#[serde(deserialize_with = "u64_deserialize")]
validator_index: u64,
#[serde(deserialize_with = "pubkey_deserialize")]
from_bls_pubkey: BLSPubKey,
#[serde(deserialize_with = "address_deserialize")]
to_execution_address: Address,
}
impl Default for BeaconBlockBody {
fn default() -> Self {
BeaconBlockBody::Bellatrix(BeaconBlockBodyBellatrix::default())
}
}
#[superstruct(
variants(Bellatrix, Capella),
variant_attributes(
derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone),
serde(deny_unknown_fields)
)
)]
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(untagged)]
pub struct ExecutionPayload { pub struct ExecutionPayload {
#[serde(deserialize_with = "bytes32_deserialize")] #[serde(deserialize_with = "bytes32_deserialize")]
pub parent_hash: Bytes32, pub parent_hash: Bytes32,
@ -70,10 +149,67 @@ pub struct ExecutionPayload {
pub block_hash: Bytes32, pub block_hash: Bytes32,
#[serde(deserialize_with = "transactions_deserialize")] #[serde(deserialize_with = "transactions_deserialize")]
pub transactions: List<Transaction, 1048576>, pub transactions: List<Transaction, 1048576>,
#[superstruct(only(Capella))]
withdrawals: List<Withdrawal, 16>,
}
#[derive(Default, Clone, Debug, SimpleSerialize, serde::Deserialize)]
pub struct Withdrawal {
#[serde(deserialize_with = "u64_deserialize")]
index: u64,
#[serde(deserialize_with = "u64_deserialize")]
validator_index: u64,
#[serde(deserialize_with = "address_deserialize")]
address: Address,
#[serde(deserialize_with = "u64_deserialize")]
amount: u64,
}
impl ssz_rs::Merkleized for ExecutionPayload {
fn hash_tree_root(&mut self) -> Result<Node, MerkleizationError> {
match self {
ExecutionPayload::Bellatrix(payload) => payload.hash_tree_root(),
ExecutionPayload::Capella(payload) => payload.hash_tree_root(),
}
}
}
impl ssz_rs::Sized for ExecutionPayload {
fn is_variable_size() -> bool {
true
}
fn size_hint() -> usize {
0
}
}
impl ssz_rs::Serialize for ExecutionPayload {
fn serialize(&self, buffer: &mut Vec<u8>) -> Result<usize, SerializeError> {
match self {
ExecutionPayload::Bellatrix(payload) => payload.serialize(buffer),
ExecutionPayload::Capella(payload) => payload.serialize(buffer),
}
}
}
impl ssz_rs::Deserialize for ExecutionPayload {
fn deserialize(_encoding: &[u8]) -> Result<Self, DeserializeError>
where
Self: Sized,
{
panic!("not implemented");
}
}
impl Default for ExecutionPayload {
fn default() -> Self {
ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default())
}
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct ProposerSlashing { pub struct ProposerSlashing {
signed_header_1: SignedBeaconBlockHeader, signed_header_1: SignedBeaconBlockHeader,
signed_header_2: SignedBeaconBlockHeader, signed_header_2: SignedBeaconBlockHeader,
} }
@ -100,7 +236,7 @@ struct BeaconBlockHeader {
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct AttesterSlashing { pub struct AttesterSlashing {
attestation_1: IndexedAttestation, attestation_1: IndexedAttestation,
attestation_2: IndexedAttestation, attestation_2: IndexedAttestation,
} }
@ -115,7 +251,7 @@ struct IndexedAttestation {
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct Attestation { pub struct Attestation {
aggregation_bits: Bitlist<2048>, aggregation_bits: Bitlist<2048>,
data: AttestationData, data: AttestationData,
#[serde(deserialize_with = "signature_deserialize")] #[serde(deserialize_with = "signature_deserialize")]
@ -143,7 +279,7 @@ struct Checkpoint {
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct SignedVoluntaryExit { pub struct SignedVoluntaryExit {
message: VoluntaryExit, message: VoluntaryExit,
#[serde(deserialize_with = "signature_deserialize")] #[serde(deserialize_with = "signature_deserialize")]
signature: SignatureBytes, signature: SignatureBytes,
@ -158,7 +294,7 @@ struct VoluntaryExit {
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct Deposit { pub struct Deposit {
#[serde(deserialize_with = "bytes_vector_deserialize")] #[serde(deserialize_with = "bytes_vector_deserialize")]
proof: Vector<Bytes32, 33>, proof: Vector<Bytes32, 33>,
data: DepositData, data: DepositData,

View File

@ -38,5 +38,5 @@ async fn test_get_payload() {
client.sync().await.unwrap(); client.sync().await.unwrap();
let payload = client.get_execution_payload(&None).await.unwrap(); let payload = client.get_execution_payload(&None).await.unwrap();
assert_eq!(payload.block_number, 7530932); assert_eq!(*payload.block_number(), 7530932);
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "execution" name = "execution"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -111,7 +111,7 @@ impl<'a, R: ExecutionRpc> Evm<'a, R> {
let rpc = db.execution.rpc.clone(); let rpc = db.execution.rpc.clone();
let payload = db.current_payload.clone(); let payload = db.current_payload.clone();
let execution = db.execution.clone(); let execution = db.execution.clone();
let block = db.current_payload.block_number; let block = *db.current_payload.block_number();
let opts_moved = CallOpts { let opts_moved = CallOpts {
from: opts.from, from: opts.from,
@ -139,7 +139,7 @@ impl<'a, R: ExecutionRpc> Evm<'a, R> {
}; };
let producer_account = AccessListItem { let producer_account = AccessListItem {
address: Address::from_slice(&payload.fee_recipient), address: Address::from_slice(payload.fee_recipient()),
storage_keys: Vec::default(), storage_keys: Vec::default(),
}; };
@ -182,10 +182,10 @@ impl<'a, R: ExecutionRpc> Evm<'a, R> {
env.tx.gas_limit = opts.gas.map(|v| v.as_u64()).unwrap_or(u64::MAX); env.tx.gas_limit = opts.gas.map(|v| v.as_u64()).unwrap_or(u64::MAX);
env.tx.gas_price = opts.gas_price.unwrap_or(U256::zero()); env.tx.gas_price = opts.gas_price.unwrap_or(U256::zero());
env.block.number = U256::from(payload.block_number); env.block.number = U256::from(*payload.block_number());
env.block.coinbase = Address::from_slice(&payload.fee_recipient); env.block.coinbase = Address::from_slice(payload.fee_recipient());
env.block.timestamp = U256::from(payload.timestamp); env.block.timestamp = U256::from(*payload.timestamp());
env.block.difficulty = U256::from_little_endian(&payload.prev_randao); env.block.difficulty = U256::from_little_endian(payload.prev_randao());
env.cfg.chain_id = self.chain_id.into(); env.cfg.chain_id = self.chain_id.into();
@ -266,7 +266,7 @@ impl<'a, R: ExecutionRpc> Database for ProofDB<'a, R> {
.payloads .payloads
.get(&number) .get(&number)
.ok_or(BlockNotFoundError::new(BlockTag::Number(number)))?; .ok_or(BlockNotFoundError::new(BlockTag::Number(number)))?;
Ok(H256::from_slice(&payload.block_hash)) Ok(H256::from_slice(payload.block_hash()))
} }
fn storage(&mut self, address: H160, slot: U256) -> Result<U256, Report> { fn storage(&mut self, address: H160, slot: U256) -> Result<U256, Report> {
@ -304,6 +304,7 @@ fn is_precompile(address: &Address) -> bool {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use common::utils::hex_str_to_bytes; use common::utils::hex_str_to_bytes;
use consensus::types::ExecutionPayloadBellatrix;
use ssz_rs::Vector; use ssz_rs::Vector;
use crate::rpc::mock_rpc::MockRpc; use crate::rpc::mock_rpc::MockRpc;
@ -319,15 +320,16 @@ mod tests {
// Construct proofdb params // Construct proofdb params
let execution = get_client(); let execution = get_client();
let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap(); let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap();
let payload = ExecutionPayload { let payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
state_root: Vector::from_iter( state_root: Vector::from_iter(
hex_str_to_bytes( hex_str_to_bytes(
"0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d", "0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d",
) )
.unwrap(), .unwrap(),
), ),
..ExecutionPayload::default() ..ExecutionPayloadBellatrix::default()
}; });
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload.clone()); payloads.insert(7530933, payload.clone());

View File

@ -54,7 +54,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let proof = self let proof = self
.rpc .rpc
.get_proof(address, slots, payload.block_number) .get_proof(address, slots, *payload.block_number())
.await?; .await?;
let account_path = keccak256(address.as_bytes()).to_vec(); let account_path = keccak256(address.as_bytes()).to_vec();
@ -62,7 +62,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let is_valid = verify_proof( let is_valid = verify_proof(
&proof.account_proof, &proof.account_proof,
&payload.state_root, payload.state_root(),
&account_path, &account_path,
&account_encoded, &account_encoded,
); );
@ -98,7 +98,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let code = if proof.code_hash == KECCAK_EMPTY { let code = if proof.code_hash == KECCAK_EMPTY {
Vec::new() Vec::new()
} else { } else {
let code = self.rpc.get_code(address, payload.block_number).await?; let code = self.rpc.get_code(address, *payload.block_number()).await?;
let code_hash = keccak256(&code).into(); let code_hash = keccak256(&code).into();
if proof.code_hash != code_hash { if proof.code_hash != code_hash {
@ -136,7 +136,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"; let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347";
let tx_hashes = payload let tx_hashes = payload
.transactions .transactions()
.iter() .iter()
.map(|tx| H256::from_slice(&keccak256(tx))) .map(|tx| H256::from_slice(&keccak256(tx)))
.collect::<Vec<H256>>(); .collect::<Vec<H256>>();
@ -144,7 +144,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let txs = if full_tx { let txs = if full_tx {
let txs_fut = tx_hashes.iter().map(|hash| async move { let txs_fut = tx_hashes.iter().map(|hash| async move {
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(payload.block_number, payload.clone()); payloads.insert(*payload.block_number(), payload.clone());
let tx = self let tx = self
.get_transaction(hash, &payloads) .get_transaction(hash, &payloads)
.await? .await?
@ -163,22 +163,22 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
}; };
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()),
difficulty: U256::from(0), difficulty: U256::from(0),
extra_data: payload.extra_data.to_vec(), extra_data: payload.extra_data().to_vec(),
gas_limit: payload.gas_limit, gas_limit: *payload.gas_limit(),
gas_used: payload.gas_used, gas_used: *payload.gas_used(),
hash: H256::from_slice(&payload.block_hash), hash: H256::from_slice(payload.block_hash()),
logs_bloom: payload.logs_bloom.to_vec(), logs_bloom: payload.logs_bloom().to_vec(),
miner: Address::from_slice(&payload.fee_recipient), miner: Address::from_slice(payload.fee_recipient()),
parent_hash: H256::from_slice(&payload.parent_hash), parent_hash: H256::from_slice(payload.parent_hash()),
receipts_root: H256::from_slice(&payload.receipts_root), receipts_root: H256::from_slice(payload.receipts_root()),
state_root: H256::from_slice(&payload.state_root), state_root: H256::from_slice(payload.state_root()),
timestamp: payload.timestamp, timestamp: *payload.timestamp(),
total_difficulty: 0, total_difficulty: 0,
transactions: txs, transactions: txs,
mix_hash: H256::from_slice(&payload.prev_randao), mix_hash: H256::from_slice(payload.prev_randao()),
nonce: empty_nonce, nonce: empty_nonce,
sha3_uncles: H256::from_str(empty_uncle_hash)?, sha3_uncles: H256::from_str(empty_uncle_hash)?,
size: 0, size: 0,
@ -192,10 +192,10 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
payload: &ExecutionPayload, payload: &ExecutionPayload,
index: usize, index: usize,
) -> Result<Option<Transaction>> { ) -> Result<Option<Transaction>> {
let tx = payload.transactions[index].clone(); let tx = payload.transactions()[index].clone();
let tx_hash = H256::from_slice(&keccak256(tx)); let tx_hash = H256::from_slice(&keccak256(tx));
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(payload.block_number, payload.clone()); payloads.insert(*payload.block_number(), payload.clone());
let tx_option = self.get_transaction(&tx_hash, &payloads).await?; let tx_option = self.get_transaction(&tx_hash, &payloads).await?;
let tx = tx_option.ok_or(eyre::eyre!("not reachable"))?; let tx = tx_option.ok_or(eyre::eyre!("not reachable"))?;
@ -222,7 +222,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let payload = payload.unwrap(); let payload = payload.unwrap();
let tx_hashes = payload let tx_hashes = payload
.transactions .transactions()
.iter() .iter()
.map(|tx| H256::from_slice(&keccak256(tx))) .map(|tx| H256::from_slice(&keccak256(tx)))
.collect::<Vec<H256>>(); .collect::<Vec<H256>>();
@ -239,7 +239,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let expected_receipt_root = ordered_trie_root(receipts_encoded); let expected_receipt_root = ordered_trie_root(receipts_encoded);
let expected_receipt_root = H256::from_slice(&expected_receipt_root.to_fixed_bytes()); let expected_receipt_root = H256::from_slice(&expected_receipt_root.to_fixed_bytes());
let payload_receipt_root = H256::from_slice(&payload.receipts_root); let payload_receipt_root = H256::from_slice(payload.receipts_root());
if expected_receipt_root != payload_receipt_root || !receipts.contains(&receipt) { if expected_receipt_root != payload_receipt_root || !receipts.contains(&receipt) {
return Err(ExecutionError::ReceiptRootMismatch(tx_hash.to_string()).into()); return Err(ExecutionError::ReceiptRootMismatch(tx_hash.to_string()).into());
@ -275,7 +275,7 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
let tx_encoded = tx.rlp().to_vec(); let tx_encoded = tx.rlp().to_vec();
let txs_encoded = payload let txs_encoded = payload
.transactions .transactions()
.iter() .iter()
.map(|tx| tx.to_vec()) .map(|tx| tx.to_vec())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -396,29 +396,19 @@ impl<R: ExecutionRpc> ExecutionClient<R> {
.get(&block_id) .get(&block_id)
.ok_or(ExecutionError::EmptyExecutionPayload())?; .ok_or(ExecutionError::EmptyExecutionPayload())?;
let converted_base_fee_per_gas = ethers::types::U256::from_little_endian( let converted_base_fee_per_gas = ethers::types::U256::from_little_endian(
&execution_payload.base_fee_per_gas.to_bytes_le(), &execution_payload.base_fee_per_gas().to_bytes_le(),
); );
fee_history fee_history
.base_fee_per_gas .base_fee_per_gas
.push(converted_base_fee_per_gas); .push(converted_base_fee_per_gas);
let gas_used_ratio_helios = ((execution_payload.gas_used as f64 let gas_used_ratio_helios = ((*execution_payload.gas_used() as f64
/ execution_payload.gas_limit as f64) / *execution_payload.gas_limit() as f64)
* 10.0_f64.powi(12)) * 10.0_f64.powi(12))
.round() .round()
/ 10.0_f64.powi(12); / 10.0_f64.powi(12);
fee_history.gas_used_ratio.push(gas_used_ratio_helios); fee_history.gas_used_ratio.push(gas_used_ratio_helios);
} }
// TODO: Maybe place behind a query option param?
// Optionally verify the computed fee history using the rpc
// verify_fee_history(
// &self.rpc,
// &fee_history,
// fee_history.base_fee_per_gas.len(),
// request_latest_block,
// reward_percentiles,
// ).await?;
Ok(Some(fee_history)) Ok(Some(fee_history))
} }
} }

View File

@ -5,7 +5,7 @@ use ethers::types::{Address, Filter, H256, U256};
use ssz_rs::{List, Vector}; use ssz_rs::{List, Vector};
use common::utils::hex_str_to_bytes; use common::utils::hex_str_to_bytes;
use consensus::types::ExecutionPayload; use consensus::types::{ExecutionPayload, ExecutionPayloadBellatrix};
use execution::rpc::mock_rpc::MockRpc; use execution::rpc::mock_rpc::MockRpc;
use execution::ExecutionClient; use execution::ExecutionClient;
@ -18,13 +18,13 @@ async fn test_get_account() {
let execution = get_client(); let execution = get_client();
let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap(); let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap();
let payload = ExecutionPayload { let payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
state_root: Vector::from_iter( state_root: Vector::from_iter(
hex_str_to_bytes("0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d") hex_str_to_bytes("0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d")
.unwrap(), .unwrap(),
), ),
..ExecutionPayload::default() ..ExecutionPayloadBellatrix::default()
}; });
let account = execution let account = execution
.get_account(&address, None, &payload) .get_account(&address, None, &payload)
@ -55,7 +55,7 @@ async fn test_get_tx() {
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap(); H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload::default(); let mut payload = ExecutionPayload::default();
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap())); payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload); payloads.insert(7530933, payload);
@ -104,15 +104,15 @@ async fn test_get_tx_not_included() {
#[tokio::test] #[tokio::test]
async fn test_get_logs() { async fn test_get_logs() {
let execution = get_client(); let execution = get_client();
let mut payload = ExecutionPayload { let mut payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
receipts_root: Vector::from_iter( receipts_root: Vector::from_iter(
hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f") hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f")
.unwrap(), .unwrap(),
), ),
..ExecutionPayload::default() ..ExecutionPayloadBellatrix::default()
}; });
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap())); payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload); payloads.insert(7530933, payload);
@ -134,15 +134,15 @@ async fn test_get_receipt() {
let tx_hash = let tx_hash =
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap(); H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload { let mut payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
receipts_root: Vector::from_iter( receipts_root: Vector::from_iter(
hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f") hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f")
.unwrap(), .unwrap(),
), ),
..ExecutionPayload::default() ..ExecutionPayloadBellatrix::default()
}; });
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap())); payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload); payloads.insert(7530933, payload);
@ -163,7 +163,7 @@ async fn test_get_receipt_bad_proof() {
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap(); H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload::default(); let mut payload = ExecutionPayload::default();
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap())); payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let mut payloads = BTreeMap::new(); let mut payloads = BTreeMap::new();
payloads.insert(7530933, payload); payloads.insert(7530933, payload);
@ -191,10 +191,10 @@ async fn test_get_receipt_not_included() {
#[tokio::test] #[tokio::test]
async fn test_get_block() { async fn test_get_block() {
let execution = get_client(); let execution = get_client();
let payload = ExecutionPayload { let payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
block_number: 12345, block_number: 12345,
..ExecutionPayload::default() ..ExecutionPayloadBellatrix::default()
}; });
let block = execution.get_block(&payload, false).await.unwrap(); let block = execution.get_block(&payload, false).await.unwrap();
@ -207,11 +207,11 @@ async fn test_get_tx_by_block_hash_and_index() {
let tx_hash = let tx_hash =
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap(); H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
let mut payload = ExecutionPayload { let mut payload = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
block_number: 7530933, block_number: 7530933,
..ExecutionPayload::default() ..ExecutionPayloadBellatrix::default()
}; });
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap())); payload.transactions_mut().push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
let tx = execution let tx = execution
.get_transaction_by_block_hash_and_index(&payload, 0) .get_transaction_by_block_hash_and_index(&payload, 0)