refactor: Sync with hyper-evm-sync before changes

This commit is contained in:
sprites0
2025-04-16 04:55:21 +00:00
parent 9805589e7b
commit aa8cffd78a
4 changed files with 131 additions and 169 deletions

View File

@ -15,59 +15,18 @@ use reth_node_builder::EngineTypes;
use reth_node_builder::NodeTypesWithEngine; use reth_node_builder::NodeTypesWithEngine;
use reth_node_builder::{rpc::RethRpcAddOns, FullNode}; use reth_node_builder::{rpc::RethRpcAddOns, FullNode};
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes, PayloadId}; 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_provider::{BlockHashReader, StageCheckpointReader};
use reth_rpc_api::EngineApiClient; use reth_rpc_api::EngineApiClient;
use reth_rpc_layer::AuthClientService; use reth_rpc_layer::AuthClientService;
use reth_stages::StageId; use reth_stages::StageId;
use serde::{Deserialize, Serialize};
use tracing::{debug, info}; use tracing::{debug, info};
use crate::serialized::TypedTransaction; use crate::serialized::{BlockAndReceipts, EvmBlock};
use crate::serialized::{self, BlockInner}; use crate::spot_meta::erc20_contract_to_spot_token;
pub(crate) struct BlockIngest(pub PathBuf); 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<EvmContract>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SpotMeta {
tokens: Vec<SpotToken>,
}
async fn fetch_spot_meta(is_testnet: bool) -> Result<SpotMeta, Box<dyn std::error::Error>> {
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<Address, u64> {
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: PayloadTypes + EngineTypes>( async fn submit_payload<Engine: PayloadTypes + EngineTypes>(
engine_api_client: &HttpClient<AuthClientService<HttpBackend>>, engine_api_client: &HttpClient<AuthClientService<HttpBackend>>,
payload: EthBuiltPayload, payload: EthBuiltPayload,
@ -95,7 +54,7 @@ async fn submit_payload<Engine: PayloadTypes + EngineTypes>(
} }
impl BlockIngest { impl BlockIngest {
pub(crate) fn collect_block(&self, height: u64) -> Option<super::serialized::Block> { pub(crate) fn collect_block(&self, height: u64) -> Option<BlockAndReceipts> {
let f = ((height - 1) / 1_000_000) * 1_000_000; let f = ((height - 1) / 1_000_000) * 1_000_000;
let s = ((height - 1) / 1_000) * 1_000; let s = ((height - 1) / 1_000) * 1_000;
let path = format!("{}/{f}/{s}/{height}.rmp.lz4", self.0.to_string_lossy()); 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::fs::File::open(path).unwrap();
let file = std::io::BufReader::new(file); let file = std::io::BufReader::new(file);
let mut decoder = lz4_flex::frame::FrameDecoder::new(file); let mut decoder = lz4_flex::frame::FrameDecoder::new(file);
let blocks: Vec<serialized::Block> = rmp_serde::from_read(&mut decoder).unwrap(); let blocks: Vec<BlockAndReceipts> = rmp_serde::from_read(&mut decoder).unwrap();
Some(blocks[0].clone()) Some(blocks[0].clone())
} else { } else {
None None
@ -135,14 +94,14 @@ impl BlockIngest {
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(); std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis();
let engine_api = node.auth_server_handle().http_client(); 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 { loop {
let Some(original_block) = self.collect_block(height) else { let Some(original_block) = self.collect_block(height) else {
tokio::time::sleep(std::time::Duration::from_millis(200)).await; tokio::time::sleep(std::time::Duration::from_millis(200)).await;
continue; 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"); debug!(target: "reth::cli", ?block, "Built new payload");
let timestamp = block.header().timestamp(); let timestamp = block.header().timestamp();
@ -153,38 +112,28 @@ impl BlockIngest {
std::mem::take(block.body_mut()); std::mem::take(block.body_mut());
let mut system_txs = vec![]; let mut system_txs = vec![];
for transaction in original_block.system_txs { for transaction in original_block.system_txs {
let s = match &transaction.tx { let TypedTransaction::Legacy(tx) = &transaction.tx else {
TypedTransaction::Legacy(tx) => match tx.input().len() { panic!("Unexpected transaction type");
0 => U256::from(0x1), };
_ => { let TxKind::Call(to) = tx.to else {
let TxKind::Call(to) = tx.to else { panic!("Unexpected contract creation");
panic!("Unexpected contract creation"); };
}; let s = if tx.input().is_empty() {
loop { U256::from(0x1)
match evm_map.get(&to).cloned() { } else {
Some(s) => { loop {
break { if let Some(spot) = evm_map.get(&to) {
let mut addr = [0u8; 32]; break spot.to_s();
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;
}
}
}
} }
},
_ => 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( let signature = PrimitiveSignature::new(
// from anvil // from anvil
@ -192,7 +141,7 @@ impl BlockIngest {
s, s,
true, true,
); );
let typed_transaction = transaction.tx.to_reth(); let typed_transaction = transaction.tx;
let tx = TransactionSigned::new( let tx = TransactionSigned::new(
typed_transaction, typed_transaction,
signature, signature,

View File

@ -6,6 +6,7 @@ static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::ne
mod block_ingest; mod block_ingest;
mod forwarder; mod forwarder;
mod serialized; mod serialized;
mod spot_meta;
use std::path::PathBuf; use std::path::PathBuf;

View File

@ -1,105 +1,56 @@
use alloy_consensus::{TxEip1559, TxEip2930, TxLegacy}; use alloy_primitives::{Address, Bytes, Log};
use reth_primitives::{Log, SealedBlock, Transaction}; use reth_primitives::{SealedBlock, Transaction};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct SerializedTransaction { pub(crate) struct BlockAndReceipts {
pub transaction: TypedTransaction, pub(crate) block: EvmBlock,
pub signature: SerializedSignature, pub(crate) receipts: Vec<LegacyReceipt>,
#[serde(default)]
pub(crate) system_txs: Vec<SystemTx>,
#[serde(default)]
pub(crate) read_precompile_calls:
Vec<(Address, Vec<(ReadPrecompileInput, ReadPrecompileResult)>)>,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct SerializedSignature { pub(crate) enum EvmBlock {
pub r: [u8; 32],
pub s: [u8; 32],
pub v: [u8; 8],
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) enum BlockInner {
Reth115(SealedBlock), Reth115(SealedBlock),
} }
/// A raw transaction. #[derive(Debug, Clone, Serialize, Deserialize)]
/// pub(crate) struct LegacyReceipt {
/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). tx_type: LegacyTxType,
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] success: bool,
pub(crate) enum TypedTransaction { cumulative_gas_used: u64,
/// Legacy transaction (type `0x0`). logs: Vec<Log>,
///
/// 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),
} }
impl TypedTransaction { #[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) fn to_reth(self) -> Transaction { enum LegacyTxType {
match self { Legacy = 0,
Self::Legacy(tx) => Transaction::Legacy(tx), Eip2930 = 1,
Self::Eip2930(tx) => Transaction::Eip2930(tx), Eip1559 = 2,
Self::Eip1559(tx) => Transaction::Eip1559(tx), Eip4844 = 3,
} Eip7702 = 4,
}
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) enum TxType { pub(crate) struct SystemTx {
/// Legacy transaction type. pub(crate) tx: Transaction,
Legacy, pub(crate) receipt: Option<LegacyReceipt>,
/// EIP-2930 transaction type.
Eip2930,
/// EIP-1559 transaction type.
Eip1559,
/// EIP-4844 transaction type.
Eip4844,
/// EIP-7702 transaction type.
Eip7702,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
pub(crate) struct Receipt { pub(crate) struct ReadPrecompileInput {
/// Receipt type. pub(crate) input: Bytes,
pub tx_type: TxType, pub(crate) gas_limit: u64,
/// 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<Log>,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct SystemTransaction { pub(crate) enum ReadPrecompileResult {
pub receipt: Receipt, Ok { gas_used: u64, bytes: Bytes },
pub tx: TypedTransaction, OutOfGas,
} Error,
UnexpectedError,
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Block {
pub block: BlockInner,
pub system_txs: Vec<SystemTransaction>,
} }

61
bin/reth/src/spot_meta.rs Normal file
View File

@ -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<EvmContract>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SpotMeta {
tokens: Vec<SpotToken>,
}
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<SpotMeta> {
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<BTreeMap<Address, SpotId>> {
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)
}