mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 02:49:55 +00:00
Compare commits
1 Commits
node-build
...
f109130f88
| Author | SHA1 | Date | |
|---|---|---|---|
| f109130f88 |
12
src/main.rs
12
src/main.rs
@ -18,7 +18,9 @@ use reth_hl::{
|
||||
HlNode,
|
||||
cli::{Cli, HlNodeArgs},
|
||||
rpc::precompile::{HlBlockPrecompileApiServer, HlBlockPrecompileExt},
|
||||
spot_meta::init as spot_meta_init,
|
||||
storage::tables::Tables,
|
||||
types::set_spot_metadata_db,
|
||||
},
|
||||
};
|
||||
use tracing::info;
|
||||
@ -92,6 +94,16 @@ fn main() -> eyre::Result<()> {
|
||||
})
|
||||
.apply(|mut builder| {
|
||||
builder.db_mut().create_tables_for::<Tables>().expect("create tables");
|
||||
|
||||
let chain_id = builder.config().chain.inner.chain().id();
|
||||
let db = builder.db_mut().clone();
|
||||
|
||||
// Set database handle for on-demand persistence
|
||||
set_spot_metadata_db(db.clone());
|
||||
|
||||
// Load spot metadata from database and initialize cache
|
||||
spot_meta_init::load_spot_metadata_cache(&db, chain_id);
|
||||
|
||||
builder
|
||||
})
|
||||
.launch()
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
use crate::{
|
||||
chainspec::{HlChainSpec, parser::HlChainSpecParser},
|
||||
node::{
|
||||
HlNode, consensus::HlConsensus, evm::config::HlEvmConfig, migrate::Migrator,
|
||||
HlNode,
|
||||
consensus::HlConsensus,
|
||||
evm::config::HlEvmConfig,
|
||||
migrate::Migrator,
|
||||
spot_meta::init as spot_meta_init,
|
||||
storage::tables::Tables,
|
||||
},
|
||||
pseudo_peer::BlockSourceArgs,
|
||||
@ -20,10 +24,7 @@ use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::{common::EnvironmentArgs, launcher::FnLauncher};
|
||||
use reth_db::{DatabaseEnv, init_db, mdbx::init_db_for};
|
||||
use reth_tracing::FileWorkerGuard;
|
||||
use std::{
|
||||
fmt::{self},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{fmt::{self}, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
macro_rules! not_applicable {
|
||||
@ -194,7 +195,12 @@ where
|
||||
let data_dir = env.datadir.clone().resolve_datadir(env.chain.chain());
|
||||
let db_path = data_dir.db();
|
||||
init_db(db_path.clone(), env.db.database_args())?;
|
||||
init_db_for::<_, Tables>(db_path, env.db.database_args())?;
|
||||
init_db_for::<_, Tables>(db_path.clone(), env.db.database_args())?;
|
||||
|
||||
// Initialize spot metadata in database
|
||||
let chain_id = env.chain.chain().id();
|
||||
spot_meta_init::init_spot_metadata(db_path, env.db.database_args(), chain_id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
106
src/node/spot_meta/init.rs
Normal file
106
src/node/spot_meta/init.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::node::{
|
||||
spot_meta::{SpotId, erc20_contract_to_spot_token},
|
||||
storage::tables::{self, SPOT_METADATA_KEY},
|
||||
types::reth_compat,
|
||||
};
|
||||
use alloy_primitives::{Address, Bytes};
|
||||
use reth_db::{DatabaseEnv, cursor::{DbCursorRO, DbCursorRW}};
|
||||
use reth_db_api::{Database, transaction::{DbTx, DbTxMut}};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
/// Load spot metadata from database and initialize cache
|
||||
pub fn load_spot_metadata_cache(db: &Arc<DatabaseEnv>, chain_id: u64) {
|
||||
// Try to read from database
|
||||
let data = match db.view(|tx| -> Result<Option<Vec<u8>>, reth_db::DatabaseError> {
|
||||
let mut cursor = tx.cursor_read::<tables::SpotMetadata>()?;
|
||||
Ok(cursor.seek_exact(SPOT_METADATA_KEY)?.map(|(_, data)| data.to_vec()))
|
||||
}) {
|
||||
Ok(Ok(data)) => data,
|
||||
Ok(Err(e)) => {
|
||||
info!("Failed to read spot metadata from database: {}. Will fetch on-demand from API.", e);
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
info!("Database view error while loading spot metadata: {}. Will fetch on-demand from API.", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Check if data exists
|
||||
let Some(data) = data else {
|
||||
info!(
|
||||
"No spot metadata found in database for chain {}. Run 'init-state' to populate, or it will be fetched on-demand from API.",
|
||||
chain_id
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// Deserialize metadata
|
||||
let serializable_map = match rmp_serde::from_slice::<BTreeMap<Address, u64>>(&data) {
|
||||
Ok(map) => map,
|
||||
Err(e) => {
|
||||
info!("Failed to deserialize spot metadata: {}. Will fetch on-demand from API.", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Convert and initialize cache
|
||||
let metadata: BTreeMap<Address, SpotId> = serializable_map
|
||||
.into_iter()
|
||||
.map(|(addr, index)| (addr, SpotId { index }))
|
||||
.collect();
|
||||
|
||||
info!("Loaded spot metadata from database ({} entries)", metadata.len());
|
||||
reth_compat::initialize_spot_metadata_cache(metadata);
|
||||
}
|
||||
|
||||
/// Initialize spot metadata in database from API
|
||||
pub fn init_spot_metadata(
|
||||
db_path: impl AsRef<std::path::Path>,
|
||||
db_args: reth_db::mdbx::DatabaseArguments,
|
||||
chain_id: u64,
|
||||
) -> eyre::Result<()> {
|
||||
info!("Initializing spot metadata for chain {}", chain_id);
|
||||
|
||||
let db = Arc::new(reth_db::open_db(db_path.as_ref(), db_args)?);
|
||||
|
||||
// Check if spot metadata already exists
|
||||
let exists = db.view(|tx| -> Result<bool, reth_db::DatabaseError> {
|
||||
let mut cursor = tx.cursor_read::<tables::SpotMetadata>()?;
|
||||
Ok(cursor.seek_exact(SPOT_METADATA_KEY)?.is_some())
|
||||
})??;
|
||||
|
||||
if exists {
|
||||
info!("Spot metadata already exists in database");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Fetch from API
|
||||
let metadata = match erc20_contract_to_spot_token(chain_id) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
info!("Failed to fetch spot metadata from API: {}. Will be fetched on-demand.", e);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
// Serialize and store
|
||||
let serializable_map: BTreeMap<Address, u64> =
|
||||
metadata.iter().map(|(addr, spot)| (*addr, spot.index)).collect();
|
||||
|
||||
db.update(|tx| -> Result<(), reth_db::DatabaseError> {
|
||||
let mut cursor = tx.cursor_write::<tables::SpotMetadata>()?;
|
||||
cursor.upsert(
|
||||
SPOT_METADATA_KEY,
|
||||
&Bytes::from(
|
||||
rmp_serde::to_vec(&serializable_map)
|
||||
.expect("Failed to serialize spot metadata"),
|
||||
),
|
||||
)?;
|
||||
Ok(())
|
||||
})??;
|
||||
|
||||
info!("Successfully fetched and stored spot metadata for chain {}", chain_id);
|
||||
Ok(())
|
||||
}
|
||||
@ -5,6 +5,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use crate::chainspec::{MAINNET_CHAIN_ID, TESTNET_CHAIN_ID};
|
||||
|
||||
pub mod init;
|
||||
mod patch;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -25,7 +26,7 @@ pub struct SpotMeta {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct SpotId {
|
||||
pub struct SpotId {
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
|
||||
@ -2,10 +2,21 @@ use alloy_primitives::{BlockNumber, Bytes};
|
||||
use reth_db::{TableSet, TableType, TableViewer, table::TableInfo, tables};
|
||||
use std::fmt;
|
||||
|
||||
/// Static key used for spot metadata, as the database is unique to each chain.
|
||||
/// This may later serve as a versioning key to assist with future database migrations.
|
||||
pub const SPOT_METADATA_KEY: u64 = 0;
|
||||
|
||||
tables! {
|
||||
/// Read precompile calls for each block.
|
||||
table BlockReadPrecompileCalls {
|
||||
type Key = BlockNumber;
|
||||
type Value = Bytes;
|
||||
}
|
||||
|
||||
/// Spot metadata mapping (EVM address to spot token index).
|
||||
/// Uses a constant key since the database is chain-specific.
|
||||
table SpotMetadata {
|
||||
type Key = u64;
|
||||
type Value = Bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,9 @@ pub struct ReadPrecompileCalls(pub Vec<ReadPrecompileCall>);
|
||||
|
||||
pub(crate) mod reth_compat;
|
||||
|
||||
// Re-export spot metadata functions
|
||||
pub use reth_compat::{initialize_spot_metadata_cache, set_spot_metadata_db};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct HlExtras {
|
||||
pub read_precompile_calls: Option<ReadPrecompileCalls>,
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
//! Copy of reth codebase to preserve serialization compatibility
|
||||
use crate::chainspec::TESTNET_CHAIN_ID;
|
||||
use crate::node::storage::tables::{SPOT_METADATA_KEY, SpotMetadata};
|
||||
use alloy_consensus::{Header, Signed, TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy};
|
||||
use alloy_primitives::{Address, BlockHash, Signature, TxKind, U256};
|
||||
use alloy_primitives::{Address, BlockHash, Bytes, Signature, TxKind, U256};
|
||||
use reth_db::cursor::DbCursorRW;
|
||||
use reth_db_api::{Database, transaction::DbTxMut};
|
||||
use reth_primitives::TransactionSigned as RethTxSigned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, LazyLock, RwLock},
|
||||
sync::{Arc, LazyLock, Mutex, RwLock},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
@ -82,33 +85,77 @@ pub struct SealedBlock {
|
||||
pub body: BlockBody,
|
||||
}
|
||||
|
||||
fn system_tx_to_reth_transaction(transaction: &SystemTx, chain_id: u64) -> TxSigned {
|
||||
static EVM_MAP: LazyLock<Arc<RwLock<BTreeMap<Address, SpotId>>>> =
|
||||
LazyLock::new(|| Arc::new(RwLock::new(BTreeMap::new())));
|
||||
{
|
||||
let Transaction::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.read().unwrap().get(&to) {
|
||||
break spot.to_s();
|
||||
}
|
||||
static EVM_MAP: LazyLock<Arc<RwLock<BTreeMap<Address, SpotId>>>> =
|
||||
LazyLock::new(|| Arc::new(RwLock::new(BTreeMap::new())));
|
||||
|
||||
info!("Contract not found: {to:?} from spot mapping, fetching again...");
|
||||
*EVM_MAP.write().unwrap() = erc20_contract_to_spot_token(chain_id).unwrap();
|
||||
}
|
||||
};
|
||||
let signature = Signature::new(U256::from(0x1), s, true);
|
||||
TxSigned::Default(RethTxSigned::Legacy(Signed::new_unhashed(tx.clone(), signature)))
|
||||
// Optional database handle for persisting on-demand fetches
|
||||
static DB_HANDLE: LazyLock<Mutex<Option<Arc<reth_db::DatabaseEnv>>>> =
|
||||
LazyLock::new(|| Mutex::new(None));
|
||||
|
||||
/// Set the database handle for persisting spot metadata
|
||||
pub fn set_spot_metadata_db(db: Arc<reth_db::DatabaseEnv>) {
|
||||
*DB_HANDLE.lock().unwrap() = Some(db);
|
||||
}
|
||||
|
||||
/// Initialize the spot metadata cache with data loaded from database.
|
||||
/// This should be called during node initialization.
|
||||
pub fn initialize_spot_metadata_cache(metadata: BTreeMap<Address, SpotId>) {
|
||||
*EVM_MAP.write().unwrap() = metadata;
|
||||
}
|
||||
|
||||
/// Persist spot metadata to database if handle is available
|
||||
fn persist_spot_metadata_to_db(metadata: &BTreeMap<Address, SpotId>) {
|
||||
if let Some(db) = DB_HANDLE.lock().unwrap().as_ref() {
|
||||
let result = db.update(|tx| -> Result<(), reth_db::DatabaseError> {
|
||||
let mut cursor = tx.cursor_write::<SpotMetadata>()?;
|
||||
|
||||
// Serialize to BTreeMap<Address, u64>
|
||||
let serializable_map: BTreeMap<Address, u64> =
|
||||
metadata.iter().map(|(addr, spot)| (*addr, spot.index)).collect();
|
||||
|
||||
cursor.upsert(
|
||||
SPOT_METADATA_KEY,
|
||||
&Bytes::from(
|
||||
rmp_serde::to_vec(&serializable_map)
|
||||
.expect("Failed to serialize spot metadata"),
|
||||
),
|
||||
)?;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(_) => info!("Persisted spot metadata to database"),
|
||||
Err(e) => info!("Failed to persist spot metadata to database: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn system_tx_to_reth_transaction(transaction: &SystemTx, chain_id: u64) -> TxSigned {
|
||||
let Transaction::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.read().unwrap().get(&to) {
|
||||
break spot.to_s();
|
||||
}
|
||||
|
||||
// Cache miss - fetch from API, update cache, and persist to database
|
||||
info!("Contract not found: {to:?} from spot mapping, fetching from API...");
|
||||
let metadata = erc20_contract_to_spot_token(chain_id).unwrap();
|
||||
*EVM_MAP.write().unwrap() = metadata.clone();
|
||||
persist_spot_metadata_to_db(&metadata);
|
||||
}
|
||||
};
|
||||
let signature = Signature::new(U256::from(0x1), s, true);
|
||||
TxSigned::Default(RethTxSigned::Legacy(Signed::new_unhashed(tx.clone(), signature)))
|
||||
}
|
||||
|
||||
impl SealedBlock {
|
||||
pub fn to_reth_block(
|
||||
&self,
|
||||
|
||||
Reference in New Issue
Block a user