use alloy_consensus::Header; use alloy_genesis::{ChainConfig, Genesis}; use alloy_primitives::U256; use alloy_rlp::Decodable; use reqwest::blocking::get; use reth_chainspec::{ChainSpec, DEV_HARDFORKS}; use reth_primitives::SealedHeader; use std::collections::BTreeMap; use std::fs::File; use std::io::{Read, Write}; pub(crate) fn load_hl_testnet() -> ChainSpec { const TESTNET_GENESIS_URL: &str = "https://raw.githubusercontent.com/sprites0/hl-testnet-genesis/main/21304281.rlp"; fn download_testnet_genesis() -> Result<&'static str, Box> { let path = "/tmp/hl_testnet.rmp.lz4"; println!("Downloading testnet genesis"); let mut response = get(TESTNET_GENESIS_URL)?; if let Some(length) = response.content_length() { // Check if the file exists if let Ok(metadata) = std::fs::metadata(path) { if metadata.len() == length { println!("Already downloaded"); return Ok(path); } } } let mut file = File::create(path)?; let mut downloaded = 0; let total_size = response.content_length().unwrap_or(0); let mut buffer = vec![0; 0x100000]; loop { let size = response.read(buffer.as_mut_slice())?; if size == 0 { break; } file.write_all(&buffer[..size])?; downloaded += size as u64; println!( "Downloaded {} of {} bytes ({}%)", downloaded, total_size, (downloaded as f64 / total_size as f64 * 100.0).round() ); } Ok(path) } let path = download_testnet_genesis().expect("Failed to download testnet genesis"); let mut file = File::open(path).expect("Failed to open testnet genesis"); let mut buffer = Vec::new(); file.read_to_end(&mut buffer).expect("Failed to read testnet genesis"); let mut header = Header::decode(&mut &buffer[..]).expect("Failed to decode testnet genesis"); let config = ChainConfig { chain_id: 998, homestead_block: Some(0), dao_fork_block: Some(0), dao_fork_support: false, eip150_block: Some(0), eip155_block: Some(0), eip158_block: Some(0), byzantium_block: Some(0), constantinople_block: Some(0), petersburg_block: Some(0), istanbul_block: Some(0), muir_glacier_block: Some(0), berlin_block: Some(0), london_block: Some(0), arrow_glacier_block: Some(0), gray_glacier_block: Some(0), merge_netsplit_block: Some(0), shanghai_time: Some(0), cancun_time: Some(0), prague_time: Some(0), osaka_time: Some(0), terminal_total_difficulty: Some(U256::ZERO), terminal_total_difficulty_passed: true, ethash: None, clique: None, parlia: None, extra_fields: Default::default(), deposit_contract_address: None, blob_schedule: Default::default(), }; header.number = 0; let genesis_header = SealedHeader::new(header.clone(), header.hash_slow()); let genesis = Genesis { config, nonce: header.nonce.into(), timestamp: header.timestamp, extra_data: header.extra_data, gas_limit: header.gas_limit, difficulty: header.difficulty, mix_hash: header.mix_hash, coinbase: header.beneficiary, alloc: BTreeMap::default(), base_fee_per_gas: header.base_fee_per_gas.map(|x| x.into()), excess_blob_gas: header.excess_blob_gas, blob_gas_used: header.blob_gas_used, number: None, }; ChainSpec { chain: alloy_chains::Chain::from_id(998), genesis: genesis.into(), genesis_header, hardforks: DEV_HARDFORKS.clone(), prune_delete_limit: 10000, ..Default::default() } }