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::{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<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>(
engine_api_client: &HttpClient<AuthClientService<HttpBackend>>,
payload: EthBuiltPayload,
@ -95,7 +54,7 @@ async fn submit_payload<Engine: PayloadTypes + EngineTypes>(
}
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 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<serialized::Block> = rmp_serde::from_read(&mut decoder).unwrap();
let blocks: Vec<BlockAndReceipts> = 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 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 {
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)
if let Some(spot) = evm_map.get(&to) {
break spot.to_s();
}
}
None => {
info!("Contract not found: {:?}, fetching again...", to);
evm_map = to_evm_map(
&fetch_spot_meta(
node.chain_spec().chain_id() == 998,
)
.await?,
info!(
"Contract not found: {:?} from spot mapping, fetching again...",
to
);
continue;
evm_map =
erc20_contract_to_spot_token(node.chain_spec().chain_id())
.await?;
}
}
}
}
},
_ => unreachable!(),
};
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,

View File

@ -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;

View File

@ -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<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)]
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<Log>,
}
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<LegacyReceipt>,
}
#[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<Log>,
#[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<SystemTransaction>,
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) enum ReadPrecompileResult {
Ok { gas_used: u64, bytes: Bytes },
OutOfGas,
Error,
UnexpectedError,
}

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)
}