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