From aa8cffd78af566c63c0c93e8f04bc994ca47e40a Mon Sep 17 00:00:00 2001 From: sprites0 <199826320+sprites0@users.noreply.github.com> Date: Wed, 16 Apr 2025 04:55:21 +0000 Subject: [PATCH] refactor: Sync with hyper-evm-sync before changes --- bin/reth/src/block_ingest.rs | 109 ++++++++--------------------- bin/reth/src/main.rs | 1 + bin/reth/src/serialized.rs | 129 +++++++++++------------------------ bin/reth/src/spot_meta.rs | 61 +++++++++++++++++ 4 files changed, 131 insertions(+), 169 deletions(-) create mode 100644 bin/reth/src/spot_meta.rs diff --git a/bin/reth/src/block_ingest.rs b/bin/reth/src/block_ingest.rs index 6152c103a..f5ef56c95 100644 --- a/bin/reth/src/block_ingest.rs +++ b/bin/reth/src/block_ingest.rs @@ -15,59 +15,18 @@ use reth_node_builder::EngineTypes; use reth_node_builder::NodeTypesWithEngine; use reth_node_builder::{rpc::RethRpcAddOns, FullNode}; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes, PayloadId}; -use reth_primitives::TransactionSigned; +use reth_primitives::{Transaction as TypedTransaction, TransactionSigned}; use reth_provider::{BlockHashReader, StageCheckpointReader}; use reth_rpc_api::EngineApiClient; use reth_rpc_layer::AuthClientService; use reth_stages::StageId; -use serde::{Deserialize, Serialize}; use tracing::{debug, info}; -use crate::serialized::TypedTransaction; -use crate::serialized::{self, BlockInner}; +use crate::serialized::{BlockAndReceipts, EvmBlock}; +use crate::spot_meta::erc20_contract_to_spot_token; pub(crate) struct BlockIngest(pub PathBuf); -#[derive(Debug, Clone, Serialize, Deserialize)] -struct EvmContract { - pub address: Address, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SpotToken { - pub index: u64, - #[serde(rename = "evmContract")] - pub evm_contract: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SpotMeta { - tokens: Vec, -} - -async fn fetch_spot_meta(is_testnet: bool) -> Result> { - let url = if is_testnet { - "https://api.hyperliquid-testnet.xyz" - } else { - "https://api.hyperliquid.xyz" - }; - let url = format!("{}/info", url); - // post body: {"type": "spotMeta"} - let client = reqwest::Client::new(); - let response = client.post(url).json(&serde_json::json!({"type": "spotMeta"})).send().await?; - Ok(response.json().await?) -} - -fn to_evm_map(meta: &SpotMeta) -> std::collections::HashMap { - let mut map = std::collections::HashMap::new(); - for token in &meta.tokens { - if let Some(evm_contract) = &token.evm_contract { - map.insert(evm_contract.address, token.index); - } - } - map -} - async fn submit_payload( engine_api_client: &HttpClient>, payload: EthBuiltPayload, @@ -95,7 +54,7 @@ async fn submit_payload( } impl BlockIngest { - pub(crate) fn collect_block(&self, height: u64) -> Option { + pub(crate) fn collect_block(&self, height: u64) -> Option { let f = ((height - 1) / 1_000_000) * 1_000_000; let s = ((height - 1) / 1_000) * 1_000; let path = format!("{}/{f}/{s}/{height}.rmp.lz4", self.0.to_string_lossy()); @@ -103,7 +62,7 @@ impl BlockIngest { let file = std::fs::File::open(path).unwrap(); let file = std::io::BufReader::new(file); let mut decoder = lz4_flex::frame::FrameDecoder::new(file); - let blocks: Vec = rmp_serde::from_read(&mut decoder).unwrap(); + let blocks: Vec = rmp_serde::from_read(&mut decoder).unwrap(); Some(blocks[0].clone()) } else { None @@ -135,14 +94,14 @@ impl BlockIngest { std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(); let engine_api = node.auth_server_handle().http_client(); - let mut evm_map = to_evm_map(&fetch_spot_meta(node.chain_spec().chain_id() == 998).await?); + let mut evm_map = erc20_contract_to_spot_token(node.chain_spec().chain_id()).await?; loop { let Some(original_block) = self.collect_block(height) else { tokio::time::sleep(std::time::Duration::from_millis(200)).await; continue; }; - let BlockInner::Reth115(mut block) = original_block.block; + let EvmBlock::Reth115(mut block) = original_block.block; { debug!(target: "reth::cli", ?block, "Built new payload"); let timestamp = block.header().timestamp(); @@ -153,38 +112,28 @@ impl BlockIngest { std::mem::take(block.body_mut()); let mut system_txs = vec![]; for transaction in original_block.system_txs { - let s = match &transaction.tx { - TypedTransaction::Legacy(tx) => match tx.input().len() { - 0 => U256::from(0x1), - _ => { - let TxKind::Call(to) = tx.to else { - panic!("Unexpected contract creation"); - }; - loop { - match evm_map.get(&to).cloned() { - Some(s) => { - break { - let mut addr = [0u8; 32]; - addr[12] = 0x20; - addr[24..32].copy_from_slice(s.to_be_bytes().as_ref()); - U256::from_be_bytes(addr) - } - } - None => { - info!("Contract not found: {:?}, fetching again...", to); - evm_map = to_evm_map( - &fetch_spot_meta( - node.chain_spec().chain_id() == 998, - ) - .await?, - ); - continue; - } - } - } + let TypedTransaction::Legacy(tx) = &transaction.tx else { + panic!("Unexpected transaction type"); + }; + let TxKind::Call(to) = tx.to else { + panic!("Unexpected contract creation"); + }; + let s = if tx.input().is_empty() { + U256::from(0x1) + } else { + loop { + if let Some(spot) = evm_map.get(&to) { + break spot.to_s(); } - }, - _ => unreachable!(), + + info!( + "Contract not found: {:?} from spot mapping, fetching again...", + to + ); + evm_map = + erc20_contract_to_spot_token(node.chain_spec().chain_id()) + .await?; + } }; let signature = PrimitiveSignature::new( // from anvil @@ -192,7 +141,7 @@ impl BlockIngest { s, true, ); - let typed_transaction = transaction.tx.to_reth(); + let typed_transaction = transaction.tx; let tx = TransactionSigned::new( typed_transaction, signature, diff --git a/bin/reth/src/main.rs b/bin/reth/src/main.rs index f1c8331d2..31509187c 100644 --- a/bin/reth/src/main.rs +++ b/bin/reth/src/main.rs @@ -6,6 +6,7 @@ static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::ne mod block_ingest; mod forwarder; mod serialized; +mod spot_meta; use std::path::PathBuf; diff --git a/bin/reth/src/serialized.rs b/bin/reth/src/serialized.rs index d351a377c..14ad2d571 100644 --- a/bin/reth/src/serialized.rs +++ b/bin/reth/src/serialized.rs @@ -1,105 +1,56 @@ -use alloy_consensus::{TxEip1559, TxEip2930, TxLegacy}; -use reth_primitives::{Log, SealedBlock, Transaction}; +use alloy_primitives::{Address, Bytes, Log}; +use reth_primitives::{SealedBlock, Transaction}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct SerializedTransaction { - pub transaction: TypedTransaction, - pub signature: SerializedSignature, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct BlockAndReceipts { + pub(crate) block: EvmBlock, + pub(crate) receipts: Vec, + #[serde(default)] + pub(crate) system_txs: Vec, + #[serde(default)] + pub(crate) read_precompile_calls: + Vec<(Address, Vec<(ReadPrecompileInput, ReadPrecompileResult)>)>, } -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct SerializedSignature { - pub r: [u8; 32], - pub s: [u8; 32], - pub v: [u8; 8], -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) enum BlockInner { +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) enum EvmBlock { Reth115(SealedBlock), } -/// A raw transaction. -/// -/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub(crate) enum TypedTransaction { - /// Legacy transaction (type `0x0`). - /// - /// Traditional Ethereum transactions, containing parameters `nonce`, `gasPrice`, `gasLimit`, - /// `to`, `value`, `data`, `v`, `r`, and `s`. - /// - /// These transactions do not utilize access lists nor do they incorporate EIP-1559 fee market - /// changes. - Legacy(TxLegacy), - /// Transaction with an [`AccessList`] ([EIP-2930](https://eips.ethereum.org/EIPS/eip-2930)), type `0x1`. - /// - /// The `accessList` specifies an array of addresses and storage keys that the transaction - /// plans to access, enabling gas savings on cross-contract calls by pre-declaring the accessed - /// contract and storage slots. - Eip2930(TxEip2930), - /// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)), type `0x2`. - /// - /// Unlike traditional transactions, EIP-1559 transactions use an in-protocol, dynamically - /// changing base fee per gas, adjusted at each block to manage network congestion. - /// - /// - `maxPriorityFeePerGas`, specifying the maximum fee above the base fee the sender is - /// willing to pay - /// - `maxFeePerGas`, setting the maximum total fee the sender is willing to pay. - /// - /// The base fee is burned, while the priority fee is paid to the miner who includes the - /// transaction, incentivizing miners to include transactions with higher priority fees per - /// gas. - Eip1559(TxEip1559), +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct LegacyReceipt { + tx_type: LegacyTxType, + success: bool, + cumulative_gas_used: u64, + logs: Vec, } -impl TypedTransaction { - pub(crate) fn to_reth(self) -> Transaction { - match self { - Self::Legacy(tx) => Transaction::Legacy(tx), - Self::Eip2930(tx) => Transaction::Eip2930(tx), - Self::Eip1559(tx) => Transaction::Eip1559(tx), - } - } +#[derive(Debug, Clone, Serialize, Deserialize)] +enum LegacyTxType { + Legacy = 0, + Eip2930 = 1, + Eip1559 = 2, + Eip4844 = 3, + Eip7702 = 4, } -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) enum TxType { - /// Legacy transaction type. - Legacy, - /// EIP-2930 transaction type. - Eip2930, - /// EIP-1559 transaction type. - Eip1559, - /// EIP-4844 transaction type. - Eip4844, - /// EIP-7702 transaction type. - Eip7702, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct SystemTx { + pub(crate) tx: Transaction, + pub(crate) receipt: Option, } -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct Receipt { - /// Receipt type. - pub tx_type: TxType, - /// If transaction is executed successfully. - /// - /// This is the `statusCode` - pub success: bool, - /// Gas used - pub cumulative_gas_used: u64, - /// Log send from contracts. - pub logs: Vec, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)] +pub(crate) struct ReadPrecompileInput { + pub(crate) input: Bytes, + pub(crate) gas_limit: u64, } -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct SystemTransaction { - pub receipt: Receipt, - pub tx: TypedTransaction, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct Block { - pub block: BlockInner, - pub system_txs: Vec, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) enum ReadPrecompileResult { + Ok { gas_used: u64, bytes: Bytes }, + OutOfGas, + Error, + UnexpectedError, } diff --git a/bin/reth/src/spot_meta.rs b/bin/reth/src/spot_meta.rs new file mode 100644 index 000000000..599a44b54 --- /dev/null +++ b/bin/reth/src/spot_meta.rs @@ -0,0 +1,61 @@ +use alloy_primitives::{Address, U256}; +use eyre::{Error, Result}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +pub(crate) const MAINNET_CHAIN_ID: u64 = 999; +pub(crate) const TESTNET_CHAIN_ID: u64 = 998; + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct EvmContract { + address: Address, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct SpotToken { + index: u64, + #[serde(rename = "evmContract")] + evm_contract: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct SpotMeta { + tokens: Vec, +} + +pub(crate) struct SpotId { + pub index: u64, +} + +impl SpotId { + pub(crate) fn to_s(&self) -> U256 { + let mut addr = [0u8; 32]; + addr[12] = 0x20; + addr[24..32].copy_from_slice(self.index.to_be_bytes().as_ref()); + U256::from_be_bytes(addr) + } +} + +async fn fetch_spot_meta(chain_id: u64) -> Result { + let url = match chain_id { + MAINNET_CHAIN_ID => "https://api.hyperliquid.xyz/info", + TESTNET_CHAIN_ID => "https://api.hyperliquid-testnet.xyz/info", + _ => return Err(Error::msg("unknown chain id")), + }; + let client = reqwest::Client::new(); + let response = client.post(url).json(&serde_json::json!({"type": "spotMeta"})).send().await?; + Ok(response.json().await?) +} + +pub(crate) async fn erc20_contract_to_spot_token( + chain_id: u64, +) -> Result> { + let meta = fetch_spot_meta(chain_id).await?; + let mut map = BTreeMap::new(); + for token in &meta.tokens { + if let Some(evm_contract) = &token.evm_contract { + map.insert(evm_contract.address, SpotId { index: token.index }); + } + } + Ok(map) +}