mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
refactor: Sync with hyper-evm-sync before changes
This commit is contained in:
@ -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 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,
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
61
bin/reth/src/spot_meta.rs
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user