feat: Support highest_precompile_address

This commit is contained in:
sprites0
2025-07-19 21:16:54 +00:00
parent eb7c6b050c
commit b3becf9c7b
9 changed files with 115 additions and 66 deletions

View File

@ -6,7 +6,7 @@ use crate::{
node::{
evm::{executor::is_system_transaction, receipt_builder::RethReceiptBuilder},
primitives::{BlockBody, TransactionSigned},
types::ReadPrecompileMap,
types::HlExtras,
},
HlBlock, HlBlockBody, HlPrimitives,
};
@ -137,7 +137,8 @@ where
body: HlBlockBody {
inner: BlockBody { transactions, ommers: Default::default(), withdrawals },
sidecars: None,
read_precompile_calls: Some(ctx.read_precompile_calls.clone().into()),
read_precompile_calls: ctx.extras.read_precompile_calls.clone(),
highest_precompile_address: ctx.extras.highest_precompile_address,
},
})
}
@ -226,7 +227,7 @@ impl<R, Spec, EvmFactory> HlBlockExecutorFactory<R, Spec, EvmFactory> {
#[derive(Debug, Clone)]
pub struct HlBlockExecutionCtx<'a> {
ctx: EthBlockExecutionCtx<'a>,
pub read_precompile_calls: ReadPrecompileMap,
pub extras: HlExtras,
}
impl<R, Spec, EvmF> BlockExecutorFactory for HlBlockExecutorFactory<R, Spec, EvmF>
@ -372,6 +373,11 @@ where
&self,
block: &'a SealedBlock<BlockTy<Self::Primitives>>,
) -> ExecutionCtxFor<'a, Self> {
let block_body = block.body();
let extras = HlExtras {
read_precompile_calls: block_body.read_precompile_calls.clone(),
highest_precompile_address: block_body.highest_precompile_address,
};
HlBlockExecutionCtx {
ctx: EthBlockExecutionCtx {
parent_hash: block.header().parent_hash,
@ -379,11 +385,7 @@ where
ommers: &block.body().ommers,
withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
},
read_precompile_calls: block
.body()
.read_precompile_calls
.clone()
.map_or(ReadPrecompileMap::default(), |calls| calls.into()),
extras,
}
}
@ -400,7 +402,7 @@ where
withdrawals: attributes.withdrawals.map(Cow::Owned),
},
// TODO: hacky, double check if this is correct
read_precompile_calls: ReadPrecompileMap::default(),
extras: HlExtras::default(),
}
}
}

View File

@ -26,6 +26,7 @@ use revm::{
result::{ExecutionResult, ResultAndState},
TxEnv,
},
interpreter::instructions::utility::IntoU256,
precompile::{PrecompileError, PrecompileOutput, PrecompileResult},
primitives::HashMap,
state::Bytecode,
@ -254,30 +255,43 @@ where
precompiles_mut.apply_precompile(&address, |_| None);
}
}
for (address, precompile) in ctx.read_precompile_calls.iter() {
for (address, precompile) in
ctx.extras.read_precompile_calls.clone().unwrap_or_default().0.iter()
{
let precompile = precompile.clone();
precompiles_mut.apply_precompile(address, |_| {
let precompiles_map: HashMap<ReadPrecompileInput, ReadPrecompileResult> =
precompile.iter().map(|(input, result)| (input.clone(), result.clone())).collect();
Some(DynPrecompile::from(move |input: PrecompileInput| -> PrecompileResult {
run_precompile(&precompile, input.data, input.gas)
run_precompile(&precompiles_map, input.data, input.gas)
}))
});
}
// NOTE: Hotfix for the precompile issue (#17). Remove this once the issue is fixed.
if block_number >= U256::from(7000000) {
// NOTE: This is adapted from hyperliquid-dex/hyper-evm-sync#5
const WARM_PRECOMPILES_BLOCK_NUMBER: u64 = 8_197_684;
if block_number >= U256::from(WARM_PRECOMPILES_BLOCK_NUMBER) {
fill_all_precompiles(ctx, precompiles_mut);
}
}
fn address_to_u64(address: Address) -> u64 {
address.into_u256().try_into().unwrap()
}
fn fill_all_precompiles<'a>(ctx: &HlBlockExecutionCtx<'a>, precompiles_mut: &mut PrecompilesMap) {
for address in 0x800..=0x80D {
let lowest_address = 0x800;
let highest_address = ctx.extras.highest_precompile_address.map_or(0x80D, address_to_u64);
for address in lowest_address..=highest_address {
let address = Address::from(U160::from(address));
if !ctx.read_precompile_calls.contains_key(&address) {
precompiles_mut.apply_precompile(&address, |_| {
precompiles_mut.apply_precompile(&address, |f| {
if let Some(precompile) = f {
return Some(precompile);
}
Some(DynPrecompile::from(move |_: PrecompileInput| -> PrecompileResult {
Err(PrecompileError::OutOfGas)
}))
});
}
}
}

View File

@ -160,6 +160,7 @@ where
},
sidecars: None,
read_precompile_calls: None,
highest_precompile_address: None,
},
}
}

View File

@ -401,6 +401,7 @@ mod tests {
},
sidecars: None,
read_precompile_calls: None,
highest_precompile_address: None,
},
};
let new_block = HlNewBlock(NewBlock { block, td: U128::from(1) });

View File

@ -42,7 +42,7 @@ mod rlp {
HlBlockBody,
};
use alloy_consensus::{BlobTransactionSidecar, Header};
use alloy_primitives::U128;
use alloy_primitives::{Address, U128};
use alloy_rlp::{RlpDecodable, RlpEncodable};
use alloy_rpc_types::Withdrawals;
use std::borrow::Cow;
@ -63,6 +63,7 @@ mod rlp {
td: U128,
sidecars: Option<Cow<'a, Vec<BlobTransactionSidecar>>>,
read_precompile_calls: Option<Cow<'a, ReadPrecompileCalls>>,
highest_precompile_address: Option<Cow<'a, Address>>,
}
impl<'a> From<&'a HlNewBlock> for HlNewBlockHelper<'a> {
@ -76,6 +77,7 @@ mod rlp {
inner: BlockBody { transactions, ommers, withdrawals },
sidecars,
read_precompile_calls,
highest_precompile_address,
},
},
td,
@ -91,6 +93,7 @@ mod rlp {
td: *td,
sidecars: sidecars.as_ref().map(Cow::Borrowed),
read_precompile_calls: read_precompile_calls.as_ref().map(Cow::Borrowed),
highest_precompile_address: highest_precompile_address.as_ref().map(Cow::Borrowed),
}
}
}
@ -112,6 +115,7 @@ mod rlp {
td,
sidecars,
read_precompile_calls,
highest_precompile_address,
} = HlNewBlockHelper::decode(buf)?;
Ok(HlNewBlock(NewBlock {
@ -125,6 +129,8 @@ mod rlp {
},
sidecars: sidecars.map(|s| s.into_owned()),
read_precompile_calls: read_precompile_calls.map(|s| s.into_owned()),
highest_precompile_address: highest_precompile_address
.map(|s| s.into_owned()),
},
},
td,
@ -231,11 +237,7 @@ where
ctx.task_executor().spawn_critical("pseudo peer", async move {
let block_source = block_source_config.create_cached_block_source().await;
start_pseudo_peer(
chain_spec,
local_node_record.to_string(),
block_source,
)
start_pseudo_peer(chain_spec, local_node_record.to_string(), block_source)
.await
.unwrap();
});

View File

@ -1,5 +1,6 @@
#![allow(clippy::owned_cow)]
use alloy_consensus::{BlobTransactionSidecar, Header};
use alloy_primitives::Address;
use alloy_rlp::{Encodable, RlpDecodable, RlpEncodable};
use reth_ethereum_primitives::Receipt;
use reth_primitives::NodePrimitives;
@ -45,6 +46,7 @@ pub struct HlBlockBody {
pub inner: BlockBody,
pub sidecars: Option<Vec<BlobTransactionSidecar>>,
pub read_precompile_calls: Option<ReadPrecompileCalls>,
pub highest_precompile_address: Option<Address>,
}
impl InMemorySize for HlBlockBody {
@ -135,6 +137,7 @@ impl Block for HlBlock {
withdrawals: body.inner.withdrawals.as_ref().map(Cow::Borrowed),
sidecars: body.sidecars.as_ref().map(Cow::Borrowed),
read_precompile_calls: body.read_precompile_calls.as_ref().map(Cow::Borrowed),
highest_precompile_address: body.highest_precompile_address.as_ref().map(Cow::Borrowed),
}
.length()
}
@ -153,6 +156,7 @@ mod rlp {
withdrawals: Option<Cow<'a, Withdrawals>>,
sidecars: Option<Cow<'a, Vec<BlobTransactionSidecar>>>,
read_precompile_calls: Option<Cow<'a, ReadPrecompileCalls>>,
highest_precompile_address: Option<Cow<'a, Address>>,
}
#[derive(RlpEncodable, RlpDecodable)]
@ -164,6 +168,7 @@ mod rlp {
pub(crate) withdrawals: Option<Cow<'a, Withdrawals>>,
pub(crate) sidecars: Option<Cow<'a, Vec<BlobTransactionSidecar>>>,
pub(crate) read_precompile_calls: Option<Cow<'a, ReadPrecompileCalls>>,
pub(crate) highest_precompile_address: Option<Cow<'a, Address>>,
}
impl<'a> From<&'a HlBlockBody> for BlockBodyHelper<'a> {
@ -172,6 +177,7 @@ mod rlp {
inner: BlockBody { transactions, ommers, withdrawals },
sidecars,
read_precompile_calls,
highest_precompile_address,
} = value;
Self {
@ -180,6 +186,7 @@ mod rlp {
withdrawals: withdrawals.as_ref().map(Cow::Borrowed),
sidecars: sidecars.as_ref().map(Cow::Borrowed),
read_precompile_calls: read_precompile_calls.as_ref().map(Cow::Borrowed),
highest_precompile_address: highest_precompile_address.as_ref().map(Cow::Borrowed),
}
}
}
@ -193,6 +200,7 @@ mod rlp {
inner: BlockBody { transactions, ommers, withdrawals },
sidecars,
read_precompile_calls,
highest_precompile_address,
},
} = value;
@ -203,6 +211,7 @@ mod rlp {
withdrawals: withdrawals.as_ref().map(Cow::Borrowed),
sidecars: sidecars.as_ref().map(Cow::Borrowed),
read_precompile_calls: read_precompile_calls.as_ref().map(Cow::Borrowed),
highest_precompile_address: highest_precompile_address.as_ref().map(Cow::Borrowed),
}
}
}
@ -225,6 +234,7 @@ mod rlp {
withdrawals,
sidecars,
read_precompile_calls,
highest_precompile_address,
} = BlockBodyHelper::decode(buf)?;
Ok(Self {
inner: BlockBody {
@ -234,6 +244,7 @@ mod rlp {
},
sidecars: sidecars.map(|s| s.into_owned()),
read_precompile_calls: read_precompile_calls.map(|s| s.into_owned()),
highest_precompile_address: highest_precompile_address.map(|s| s.into_owned()),
})
}
}
@ -257,6 +268,7 @@ mod rlp {
withdrawals,
sidecars,
read_precompile_calls,
highest_precompile_address,
} = BlockHelper::decode(buf)?;
Ok(Self {
header: header.into_owned(),
@ -268,6 +280,7 @@ mod rlp {
},
sidecars: sidecars.map(|s| s.into_owned()),
read_precompile_calls: read_precompile_calls.map(|s| s.into_owned()),
highest_precompile_address: highest_precompile_address.map(|s| s.into_owned()),
},
})
}
@ -283,6 +296,7 @@ pub mod serde_bincode_compat {
inner: BincodeReprFor<'a, BlockBody>,
sidecars: Option<Cow<'a, Vec<BlobTransactionSidecar>>>,
read_precompile_calls: Option<Cow<'a, ReadPrecompileCalls>>,
highest_precompile_address: Option<Cow<'a, Address>>,
}
#[derive(Debug, Serialize, Deserialize)]
@ -299,15 +313,25 @@ pub mod serde_bincode_compat {
inner: self.inner.as_repr(),
sidecars: self.sidecars.as_ref().map(Cow::Borrowed),
read_precompile_calls: self.read_precompile_calls.as_ref().map(Cow::Borrowed),
highest_precompile_address: self
.highest_precompile_address
.as_ref()
.map(Cow::Borrowed),
}
}
fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
let HlBlockBodyBincode { inner, sidecars, read_precompile_calls } = repr;
let HlBlockBodyBincode {
inner,
sidecars,
read_precompile_calls,
highest_precompile_address,
} = repr;
Self {
inner: BlockBody::from_repr(inner),
sidecars: sidecars.map(|s| s.into_owned()),
read_precompile_calls: read_precompile_calls.map(|s| s.into_owned()),
highest_precompile_address: highest_precompile_address.map(|s| s.into_owned()),
}
}
}

View File

@ -1,7 +1,7 @@
use crate::{
node::{
primitives::tx_wrapper::{convert_to_eth_block_body, convert_to_hl_block_body},
types::ReadPrecompileCalls,
types::HlExtras,
},
HlBlock, HlBlockBody, HlPrimitives,
};
@ -29,23 +29,20 @@ impl HlStorage {
fn write_precompile_calls<Provider>(
&self,
provider: &Provider,
inputs: Vec<(u64, Option<ReadPrecompileCalls>)>,
inputs: Vec<(u64, HlExtras)>,
) -> ProviderResult<()>
where
Provider: DBProvider<Tx: DbTxMut>,
{
let mut precompile_calls_cursor =
provider.tx_ref().cursor_write::<tables::BlockReadPrecompileCalls>()?;
let mut precompile_calls_cursor: <<Provider as DBProvider>::Tx as DbTxMut>::CursorMut<
tables::BlockReadPrecompileCalls,
> = provider.tx_ref().cursor_write::<tables::BlockReadPrecompileCalls>()?;
for (block_number, read_precompile_calls) in inputs {
let Some(read_precompile_calls) = read_precompile_calls else {
continue;
};
for (block_number, extras) in inputs {
precompile_calls_cursor.append(
block_number,
&Bytes::copy_from_slice(
&rmp_serde::to_vec(&read_precompile_calls)
.expect("Failed to serialize read precompile calls"),
&rmp_serde::to_vec(&extras).expect("Failed to serialize read precompile calls"),
),
)?;
}
@ -57,11 +54,11 @@ impl HlStorage {
&self,
provider: &Provider,
inputs: &[ReadBodyInput<'_, HlBlock>],
) -> ProviderResult<Vec<Option<ReadPrecompileCalls>>>
) -> ProviderResult<Vec<HlExtras>>
where
Provider: DBProvider<Tx: DbTx>,
{
let mut read_precompile_calls = Vec::with_capacity(inputs.len());
let mut extras: Vec<HlExtras> = Vec::with_capacity(inputs.len());
let mut precompile_calls_cursor =
provider.tx_ref().cursor_read::<tables::BlockReadPrecompileCalls>()?;
@ -69,11 +66,12 @@ impl HlStorage {
let precompile_calls = precompile_calls_cursor
.seek_exact(header.number())?
.map(|(_, calls)| calls)
.map(|calls| rmp_serde::from_slice(&calls).unwrap());
read_precompile_calls.push(precompile_calls);
.map(|calls| rmp_serde::from_slice(&calls).unwrap())
.unwrap_or_default();
extras.push(precompile_calls);
}
Ok(read_precompile_calls)
Ok(extras)
}
}
@ -92,13 +90,27 @@ where
for (block_number, body) in bodies {
match body {
Some(HlBlockBody { inner, sidecars: _, read_precompile_calls: rpc }) => {
Some(HlBlockBody {
inner,
sidecars: _,
read_precompile_calls: rpc,
highest_precompile_address,
}) => {
eth_bodies.push((block_number, Some(convert_to_eth_block_body(inner))));
read_precompile_calls.push((block_number, rpc));
read_precompile_calls.push((
block_number,
HlExtras { read_precompile_calls: rpc, highest_precompile_address },
));
}
None => {
eth_bodies.push((block_number, None));
read_precompile_calls.push((block_number, None));
read_precompile_calls.push((
block_number,
HlExtras {
read_precompile_calls: Default::default(),
highest_precompile_address: None,
},
));
}
}
}
@ -148,10 +160,11 @@ where
Ok(eth_bodies
.into_iter()
.zip(read_precompile_calls)
.map(|(inner, read_precompile_calls)| HlBlockBody {
.map(|(inner, extra)| HlBlockBody {
inner: convert_to_hl_block_body(inner),
sidecars: None,
read_precompile_calls,
read_precompile_calls: extra.read_precompile_calls,
highest_precompile_address: extra.highest_precompile_address,
})
.collect())
}

View File

@ -5,7 +5,6 @@
use alloy_primitives::{Address, Bytes, Log, B256};
use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
use bytes::BufMut;
use revm::primitives::HashMap;
use serde::{Deserialize, Serialize};
use crate::{node::spot_meta::MAINNET_CHAIN_ID, HlBlock};
@ -14,24 +13,13 @@ pub type ReadPrecompileCall = (Address, Vec<(ReadPrecompileInput, ReadPrecompile
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
pub struct ReadPrecompileCalls(pub Vec<ReadPrecompileCall>);
pub type ReadPrecompileMap = HashMap<Address, HashMap<ReadPrecompileInput, ReadPrecompileResult>>;
mod reth_compat;
impl From<ReadPrecompileCalls> for ReadPrecompileMap {
fn from(calls: ReadPrecompileCalls) -> Self {
calls.0.into_iter().map(|(address, calls)| (address, calls.into_iter().collect())).collect()
}
}
impl From<ReadPrecompileMap> for ReadPrecompileCalls {
fn from(map: ReadPrecompileMap) -> Self {
Self(
map.into_iter()
.map(|(address, calls)| (address, calls.into_iter().collect()))
.collect(),
)
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HlExtras {
pub read_precompile_calls: Option<ReadPrecompileCalls>,
pub highest_precompile_address: Option<Address>,
}
impl Encodable for ReadPrecompileCalls {
@ -58,6 +46,7 @@ pub struct BlockAndReceipts {
pub system_txs: Vec<SystemTx>,
#[serde(default)]
pub read_precompile_calls: ReadPrecompileCalls,
pub highest_precompile_address: Option<Address>,
}
impl BlockAndReceipts {
@ -65,6 +54,7 @@ impl BlockAndReceipts {
let EvmBlock::Reth115(block) = self.block;
block.to_reth_block(
self.read_precompile_calls.clone(),
self.highest_precompile_address,
self.system_txs.clone(),
MAINNET_CHAIN_ID,
)

View File

@ -112,6 +112,7 @@ impl SealedBlock {
pub fn to_reth_block(
&self,
read_precompile_calls: ReadPrecompileCalls,
highest_precompile_address: Option<Address>,
system_txs: Vec<super::SystemTx>,
chain_id: u64,
) -> HlBlock {
@ -126,6 +127,7 @@ impl SealedBlock {
},
sidecars: None,
read_precompile_calls: Some(read_precompile_calls),
highest_precompile_address,
};
HlBlock { header: self.header.header.clone(), body: block_body }