feat: hl-node compliant mode

hl-node compliant mode removes all system transactions from rpc responses like eth_getBlock, eth_getLogs and eth_subscribe.
This commit is contained in:
sprites0
2025-07-02 11:47:23 +00:00
parent 28f6c1e6be
commit bc49c22094
3 changed files with 62 additions and 8 deletions

View File

@ -31,6 +31,15 @@ struct HyperliquidExtArgs {
/// Forward eth_call and eth_estimateGas to the upstream RPC.
#[arg(long)]
pub forward_call: bool,
/// Enable hl-node compliant mode.
///
/// This option
/// 1. filters out system transactions from block transaction list.
/// 2. filters out logs that are not from the block's transactions.
/// 3. filters out logs and transactions from subscription.
#[arg(long, default_value = "false")]
pub hl_node_compliant: bool,
}
fn main() {
@ -46,6 +55,11 @@ fn main() {
if let Err(err) = Cli::<EthereumChainSpecParser, HyperliquidExtArgs>::parse().run(
|builder, ext_args| async move {
if ext_args.hl_node_compliant {
info!(target: "reth::cli", "hl-node compliant mode enabled");
std::env::set_var("HL_NODE_COMPLIANT", "true");
}
let ingest_dir = builder.config().ingest_dir.clone().expect("ingest dir not set");
let local_ingest_dir = builder.config().local_ingest_dir.clone();
info!(target: "reth::cli", "Launching node");

View File

@ -27,9 +27,14 @@ where
let mut all_logs = Vec::new();
// Tracks the index of a log in the entire block.
let mut log_index: u64 = 0;
let is_in_hl_node_compliant_mode = is_in_hl_node_compliant_mode();
// Iterate over transaction hashes and receipts and append matching logs.
for (receipt_idx, (tx_hash, receipt)) in tx_hashes_and_receipts.into_iter().enumerate() {
for log in receipt.logs() {
if is_in_hl_node_compliant_mode && receipt.cumulative_gas_used() == 0 {
continue;
}
if log_matches_filter(block_num_hash, log, filter) {
let log = Log {
inner: log.clone(),
@ -59,6 +64,10 @@ pub enum ProviderOrBlock<'a, P: BlockReader> {
Block(Arc<RecoveredBlock<ProviderBlock<P>>>),
}
fn is_in_hl_node_compliant_mode() -> bool {
std::env::var("HL_NODE_COMPLIANT").is_ok()
}
/// Appends all matching logs of a block's receipts.
/// If the log matches, look up the corresponding transaction hash.
pub fn append_matching_block_logs<P>(
@ -81,12 +90,18 @@ where
// prevents re-querying the block body indices.
let mut loaded_first_tx_num = None;
let is_in_hl_node_compliant_mode = is_in_hl_node_compliant_mode();
// Iterate over receipts and append matching logs.
for (receipt_idx, receipt) in receipts.iter().enumerate() {
// The transaction hash of the current receipt.
let mut transaction_hash = None;
for log in receipt.logs() {
if is_in_hl_node_compliant_mode && receipt.cumulative_gas_used() == 0 {
continue;
}
if log_matches_filter(block_num_hash, log, filter) {
// if this is the first match in the receipt's logs, look up the transaction hash
if transaction_hash.is_none() {
@ -146,13 +161,13 @@ pub fn log_matches_filter(
log: &alloy_primitives::Log,
params: &FilteredParams,
) -> bool {
if params.filter.is_some() &&
(!params.filter_block_range(block.number) ||
!params.filter_block_hash(block.hash) ||
!params.filter_address(&log.address) ||
!params.filter_topics(log.topics()))
if params.filter.is_some()
&& (!params.filter_block_range(block.number)
|| !params.filter_block_hash(block.hash)
|| !params.filter_address(&log.address)
|| !params.filter_topics(log.topics()))
{
return false
return false;
}
true
}

View File

@ -1,7 +1,7 @@
//! Compatibility functions for rpc `Block` type.
use crate::transaction::TransactionCompat;
use alloy_consensus::{BlockHeader, Sealable};
use alloy_consensus::{BlockHeader, Sealable, Transaction as _};
use alloy_primitives::U256;
use alloy_rpc_types_eth::{
Block, BlockTransactions, BlockTransactionsKind, Header, TransactionInfo,
@ -38,7 +38,18 @@ pub fn from_block_with_tx_hashes<T, B>(block: RecoveredBlock<B>) -> Block<T, Hea
where
B: BlockTrait,
{
let transactions = block.body().transaction_hashes_iter().copied().collect();
let transactions = block
.body()
.transactions_iter()
.filter(move |&tx| {
if is_in_hl_node_compliant_mode() {
return !matches!(tx.gas_price(), Some(0));
}
true
})
.map(|tx| *tx.tx_hash())
.collect();
let rlp_length = block.rlp_length();
let (header, body) = block.into_sealed_block().split_sealed_header_body();
from_block_with_transactions::<T, B>(
@ -68,8 +79,18 @@ where
let block_length = block.rlp_length();
let block_hash = Some(block.hash());
let is_in_hl_node_compliant_mode = is_in_hl_node_compliant_mode();
let transactions = block
.transactions_recovered()
.filter(move |tx| {
if is_in_hl_node_compliant_mode {
let gas_price = tx.clone_tx().gas_price();
return !matches!(gas_price, Some(0));
}
true
})
.enumerate()
.map(|(idx, tx)| {
let tx_info = TransactionInfo {
@ -93,6 +114,10 @@ where
))
}
fn is_in_hl_node_compliant_mode() -> bool {
std::env::var("HL_NODE_COMPLIANT").is_ok()
}
#[inline]
fn from_block_with_transactions<T, B: BlockTrait>(
block_length: usize,