feat: Custom TransactionSigned

Like OpTransactionSigned, introduce a new tx type. Not a trivial change but might be beneficial for potential system tx changes e.g., in case it's separated into another tx type
This commit is contained in:
sprites0
2025-06-27 06:32:39 +00:00
parent b702f3c480
commit 2c6e989ad0
20 changed files with 1102 additions and 198 deletions

View File

@ -1,9 +1,9 @@
use alloy_consensus::Transaction as AlloyTransaction;
use alloy_primitives::address;
use crate::node::primitives::TransactionSigned;
use alloy_consensus::Transaction as _;
use alloy_rpc_types::AccessList;
use auto_impl::auto_impl;
use reth_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv, TransactionEnv};
use reth_primitives::TransactionSigned;
use reth_primitives_traits::SignerRecoverable;
use revm::{
context::TxEnv,
context_interface::transaction::Transaction,
@ -118,21 +118,10 @@ impl<T: revm::context::Transaction> IntoTxEnv<Self> for HlTxEnv<T> {
}
}
fn s_to_address(s: U256) -> Address {
if s == U256::ONE {
return address!("2222222222222222222222222222222222222222");
}
let mut buf = [0u8; 20];
buf.copy_from_slice(&s.to_be_bytes::<32>()[12..]);
Address::from_slice(&buf)
}
impl FromRecoveredTx<TransactionSigned> for HlTxEnv<TxEnv> {
fn from_recovered_tx(tx: &TransactionSigned, sender: Address) -> Self {
if let Some(gas_price) = tx.gas_price() {
if gas_price == 0 {
return Self::new(TxEnv::from_recovered_tx(tx, s_to_address(tx.signature().s())));
}
if tx.gas_price().is_some() && tx.gas_price().unwrap() == 0 {
return Self::new(TxEnv::from_recovered_tx(tx, tx.recover_signer().unwrap()));
}
Self::new(TxEnv::from_recovered_tx(tx, sender))
@ -141,7 +130,7 @@ impl FromRecoveredTx<TransactionSigned> for HlTxEnv<TxEnv> {
impl FromTxWithEncoded<TransactionSigned> for HlTxEnv<TxEnv> {
fn from_encoded_tx(tx: &TransactionSigned, sender: Address, _encoded: Bytes) -> Self {
let base = match tx.clone().into_typed_transaction() {
let base = match tx.clone().0.into_typed_transaction() {
reth_primitives::Transaction::Legacy(tx) => TxEnv::from_recovered_tx(&tx, sender),
reth_primitives::Transaction::Eip2930(tx) => TxEnv::from_recovered_tx(&tx, sender),
reth_primitives::Transaction::Eip1559(tx) => TxEnv::from_recovered_tx(&tx, sender),

View File

@ -1,6 +1,4 @@
use crate::{hardforks::HlHardforks, node::HlNode, HlBlock, HlBlockBody, HlPrimitives};
use alloy_consensus::BlockHeader as _;
use alloy_eips::eip7685::Requests;
use reth::{
api::FullNodeTypes,
beacon_consensus::EthBeaconConsensus,
@ -10,11 +8,9 @@ use reth::{
validate_against_parent_4844, validate_against_parent_hash_number,
},
};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_primitives::{
gas_spent_by_transactions, GotExpected, Receipt, RecoveredBlock, SealedBlock, SealedHeader,
};
use reth_primitives_traits::{Block, BlockHeader, Receipt as ReceiptTrait};
use reth_chainspec::EthChainSpec;
use reth_primitives::{Receipt, RecoveredBlock, SealedBlock, SealedHeader};
use reth_primitives_traits::BlockHeader;
use reth_provider::BlockExecutionResult;
use std::sync::Arc;
@ -67,11 +63,8 @@ pub fn validate_against_parent_timestamp<H: BlockHeader>(
}
impl<ChainSpec: EthChainSpec + HlHardforks> HeaderValidator for HlConsensus<ChainSpec> {
fn validate_header(&self, _header: &SealedHeader) -> Result<(), ConsensusError> {
// TODO: doesn't work because of extradata check
// self.inner.validate_header(header)
Ok(())
fn validate_header(&self, header: &SealedHeader) -> Result<(), ConsensusError> {
self.inner.validate_header(header)
}
fn validate_header_against_parent(
@ -142,71 +135,17 @@ impl<ChainSpec: EthChainSpec + HlHardforks> Consensus<HlBlock> for HlConsensus<C
mod reth_copy;
pub fn validate_block_post_execution<B, R, ChainSpec>(
block: &RecoveredBlock<B>,
chain_spec: &ChainSpec,
receipts: &[R],
requests: &Requests,
) -> Result<(), ConsensusError>
where
B: Block,
R: ReceiptTrait,
ChainSpec: EthereumHardforks,
{
use reth_copy::verify_receipts;
// Copy of reth's validate_block_post_execution
// Differences:
// - Filter out system transactions for receipts check
// Check if gas used matches the value set in header.
let cumulative_gas_used =
receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0);
if block.header().gas_used() != cumulative_gas_used {
return Err(ConsensusError::BlockGasUsed {
gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
gas_spent_by_tx: gas_spent_by_transactions(receipts),
});
}
// Before Byzantium, receipts contained state root that would mean that expensive
// operation as hashing that is required for state root got calculated in every
// transaction This was replaced with is_success flag.
// See more about EIP here: https://eips.ethereum.org/EIPS/eip-658
if chain_spec.is_byzantium_active_at_block(block.header().number()) {
let receipts_for_root =
receipts.iter().filter(|&r| r.cumulative_gas_used() != 0).cloned().collect::<Vec<_>>();
if let Err(error) = verify_receipts(
block.header().receipts_root(),
block.header().logs_bloom(),
&receipts_for_root,
) {
tracing::debug!(%error, ?receipts, "receipts verification failed");
return Err(error);
}
}
// Validate that the header requests hash matches the calculated requests hash
if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
let Some(header_requests_hash) = block.header().requests_hash() else {
return Err(ConsensusError::RequestsHashMissing);
};
let requests_hash = requests.requests_hash();
if requests_hash != header_requests_hash {
return Err(ConsensusError::BodyRequestsHashDiff(
GotExpected::new(requests_hash, header_requests_hash).into(),
));
}
}
Ok(())
}
impl<ChainSpec: EthChainSpec + HlHardforks> FullConsensus<HlPrimitives> for HlConsensus<ChainSpec> {
fn validate_block_post_execution(
&self,
block: &RecoveredBlock<HlBlock>,
result: &BlockExecutionResult<Receipt>,
) -> Result<(), ConsensusError> {
validate_block_post_execution(block, &self.chain_spec, &result.receipts, &result.requests)
reth_copy::validate_block_post_execution(
block,
&self.chain_spec,
&result.receipts,
&result.requests,
)
}
}

View File

@ -1,14 +1,74 @@
//! Copy of reth codebase.
use alloy_consensus::{proofs::calculate_receipt_root, TxReceipt};
use alloy_consensus::{proofs::calculate_receipt_root, BlockHeader, TxReceipt};
use alloy_eips::eip7685::Requests;
use alloy_primitives::{Bloom, B256};
use reth::consensus::ConsensusError;
use reth_primitives::GotExpected;
use reth_primitives_traits::Receipt;
use reth_chainspec::EthereumHardforks;
use reth_primitives::{gas_spent_by_transactions, GotExpected, RecoveredBlock};
use reth_primitives_traits::{Block, Receipt as ReceiptTrait};
pub fn validate_block_post_execution<B, R, ChainSpec>(
block: &RecoveredBlock<B>,
chain_spec: &ChainSpec,
receipts: &[R],
requests: &Requests,
) -> Result<(), ConsensusError>
where
B: Block,
R: ReceiptTrait,
ChainSpec: EthereumHardforks,
{
// Copy of reth's validate_block_post_execution
// Differences:
// - Filter out system transactions for receipts check
// Check if gas used matches the value set in header.
let cumulative_gas_used =
receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0);
if block.header().gas_used() != cumulative_gas_used {
return Err(ConsensusError::BlockGasUsed {
gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
gas_spent_by_tx: gas_spent_by_transactions(receipts),
});
}
// Before Byzantium, receipts contained state root that would mean that expensive
// operation as hashing that is required for state root got calculated in every
// transaction This was replaced with is_success flag.
// See more about EIP here: https://eips.ethereum.org/EIPS/eip-658
if chain_spec.is_byzantium_active_at_block(block.header().number()) {
let receipts_for_root =
receipts.iter().filter(|&r| r.cumulative_gas_used() != 0).cloned().collect::<Vec<_>>();
if let Err(error) = verify_receipts(
block.header().receipts_root(),
block.header().logs_bloom(),
&receipts_for_root,
) {
tracing::debug!(%error, ?receipts, "receipts verification failed");
return Err(error);
}
}
// Validate that the header requests hash matches the calculated requests hash
if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
let Some(header_requests_hash) = block.header().requests_hash() else {
return Err(ConsensusError::RequestsHashMissing);
};
let requests_hash = requests.requests_hash();
if requests_hash != header_requests_hash {
return Err(ConsensusError::BodyRequestsHashDiff(
GotExpected::new(requests_hash, header_requests_hash).into(),
));
}
}
Ok(())
}
/// Calculate the receipts root, and compare it against the expected receipts root and logs
/// bloom.
pub(super) fn verify_receipts<R: Receipt>(
pub(super) fn verify_receipts<R: ReceiptTrait>(
expected_receipts_root: B256,
expected_logs_bloom: Bloom,
receipts: &[R],

View File

@ -3,7 +3,11 @@ use crate::{
chainspec::HlChainSpec,
evm::{spec::HlSpecId, transaction::HlTxEnv},
hardforks::HlHardforks,
node::{evm::executor::is_system_transaction, types::ReadPrecompileMap},
node::{
evm::{executor::is_system_transaction, receipt_builder::RethReceiptBuilder},
primitives::{BlockBody, TransactionSigned},
types::ReadPrecompileMap,
},
HlBlock, HlBlockBody, HlPrimitives,
};
use alloy_consensus::{BlockHeader, Header, Transaction as _, TxReceipt, EMPTY_OMMER_ROOT_HASH};
@ -11,7 +15,6 @@ use alloy_eips::merge::BEACON_NONCE;
use alloy_primitives::{Log, U256};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_ethereum_forks::EthereumHardfork;
use reth_ethereum_primitives::BlockBody;
use reth_evm::{
block::{BlockExecutionError, BlockExecutorFactory, BlockExecutorFor},
eth::{receipt_builder::ReceiptBuilder, EthBlockExecutionCtx},
@ -20,10 +23,8 @@ use reth_evm::{
ConfigureEvm, EvmEnv, EvmFactory, ExecutionCtxFor, FromRecoveredTx, FromTxWithEncoded,
IntoTxEnv, NextBlockEnvAttributes,
};
use reth_evm_ethereum::{EthBlockAssembler, RethReceiptBuilder};
use reth_primitives::{
logs_bloom, BlockTy, HeaderTy, Receipt, SealedBlock, SealedHeader, TransactionSigned,
};
use reth_evm_ethereum::EthBlockAssembler;
use reth_primitives::{logs_bloom, BlockTy, HeaderTy, Receipt, SealedBlock, SealedHeader};
use reth_primitives_traits::proofs;
use reth_provider::BlockExecutionResult;
use reth_revm::State;

View File

@ -2,7 +2,10 @@ use super::{config::HlBlockExecutionCtx, patch::patch_mainnet_after_tx};
use crate::{
evm::transaction::HlTxEnv,
hardforks::HlHardforks,
node::types::{ReadPrecompileInput, ReadPrecompileResult},
node::{
primitives::TransactionSigned,
types::{ReadPrecompileInput, ReadPrecompileResult},
},
};
use alloy_consensus::{Transaction, TxReceipt};
use alloy_eips::{eip7685::Requests, Encodable2718};
@ -16,7 +19,6 @@ use reth_evm::{
precompiles::{DynPrecompile, PrecompilesMap},
Database, Evm, FromRecoveredTx, FromTxWithEncoded, IntoTxEnv, OnStateHook, RecoveredTx,
};
use reth_primitives::TransactionSigned;
use reth_provider::BlockExecutionResult;
use reth_revm::State;
use revm::{
@ -98,23 +100,7 @@ where
{
/// Creates a new HlBlockExecutor.
pub fn new(mut evm: EVM, ctx: HlBlockExecutionCtx<'a>, spec: Spec, receipt_builder: R) -> Self {
let precompiles_mut = evm.precompiles_mut();
// For all precompile addresses just in case it's populated and not cleared
// Clear 0x00...08xx addresses
let addresses = precompiles_mut.addresses().cloned().collect::<Vec<_>>();
for address in addresses {
if address.starts_with(&[0u8; 18]) && address[19] == 8 {
precompiles_mut.apply_precompile(&address, |_| None);
}
}
for (address, precompile) in ctx.read_precompile_calls.iter() {
let precompile = precompile.clone();
precompiles_mut.apply_precompile(address, |_| {
Some(DynPrecompile::from(move |data: &[u8], gas: u64| {
run_precompile(&precompile, data, gas)
}))
});
}
apply_precompiles(&mut evm, &ctx);
Self { spec, evm, gas_used: 0, receipts: vec![], receipt_builder, ctx }
}
}
@ -127,6 +113,7 @@ where
Tx: FromRecoveredTx<R::Transaction>
+ FromRecoveredTx<TransactionSigned>
+ FromTxWithEncoded<TransactionSigned>,
Precompiles = PrecompilesMap,
>,
Spec: EthereumHardforks + HlHardforks + EthChainSpec + Hardforks,
R: ReceiptBuilder<Transaction = TransactionSigned, Receipt: TxReceipt>,
@ -140,6 +127,7 @@ where
type Evm = E;
fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
apply_precompiles(&mut self.evm, &self.ctx);
Ok(())
}
@ -223,3 +211,27 @@ where
&self.evm
}
}
fn apply_precompiles<'a, DB, EVM>(evm: &mut EVM, ctx: &HlBlockExecutionCtx<'a>)
where
EVM: Evm<DB = &'a mut State<DB>, Precompiles = PrecompilesMap>,
DB: Database + 'a,
{
let precompiles_mut = evm.precompiles_mut();
// For all precompile addresses just in case it's populated and not cleared
// Clear 0x00...08xx addresses
let addresses = precompiles_mut.addresses().cloned().collect::<Vec<_>>();
for address in addresses {
if address.starts_with(&[0u8; 18]) && address[19] == 8 {
precompiles_mut.apply_precompile(&address, |_| None);
}
}
for (address, precompile) in ctx.read_precompile_calls.iter() {
let precompile = precompile.clone();
precompiles_mut.apply_precompile(address, |_| {
Some(DynPrecompile::from(move |data: &[u8], gas: u64| {
run_precompile(&precompile, data, gas)
}))
});
}
}

View File

@ -29,6 +29,7 @@ pub mod config;
mod executor;
mod factory;
mod patch;
pub mod receipt_builder;
/// HL EVM implementation.
///

View File

@ -0,0 +1,30 @@
use crate::node::primitives::TransactionSigned;
use alloy_evm::eth::receipt_builder::{ReceiptBuilder, ReceiptBuilderCtx};
use reth_evm::Evm;
use reth_primitives::Receipt;
/// A builder that operates on Reth primitive types, specifically [`TransactionSigned`] and
/// [`Receipt`].
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct RethReceiptBuilder;
impl ReceiptBuilder for RethReceiptBuilder {
type Transaction = TransactionSigned;
type Receipt = Receipt;
fn build_receipt<E: Evm>(
&self,
ctx: ReceiptBuilderCtx<'_, Self::Transaction, E>,
) -> Self::Receipt {
let ReceiptBuilderCtx { tx, result, cumulative_gas_used, .. } = ctx;
Receipt {
tx_type: tx.tx_type(),
// Success flag was added in `EIP-658: Embedding transaction status code in
// receipts`.
success: result.is_success(),
cumulative_gas_used,
logs: result.into_logs(),
}
}
}

View File

@ -1,7 +1,8 @@
use crate::{
chainspec::HlChainSpec,
node::{
primitives::{HlBlock, HlBlockBody, HlPrimitives},
pool::HlPoolBuilder,
primitives::{BlockBody, HlBlock, HlBlockBody, HlPrimitives, TransactionSigned},
rpc::{
engine_api::{
builder::HlEngineApiBuilder, payload::HlPayloadTypes,
@ -24,8 +25,6 @@ use reth::{
},
};
use reth_engine_primitives::BeaconConsensusEngineHandle;
use reth_node_ethereum::node::EthereumPoolBuilder;
use reth_primitives::BlockBody;
use reth_trie_db::MerklePatriciaTrie;
use std::sync::Arc;
use tokio::sync::{oneshot, Mutex};
@ -59,12 +58,14 @@ impl HlNode {
}
}
mod pool;
impl HlNode {
pub fn components<Node>(
&self,
) -> ComponentsBuilder<
Node,
EthereumPoolBuilder,
HlPoolBuilder,
HlPayloadServiceBuilder,
HlNetworkBuilder,
HlExecutorBuilder,
@ -75,7 +76,7 @@ impl HlNode {
{
ComponentsBuilder::default()
.node_types::<Node>()
.pool(EthereumPoolBuilder::default())
.pool(HlPoolBuilder)
.executor(HlExecutorBuilder::default())
.payload(HlPayloadServiceBuilder::default())
.network(HlNetworkBuilder { engine_handle_rx: self.engine_handle_rx.clone() })
@ -97,7 +98,7 @@ where
{
type ComponentsBuilder = ComponentsBuilder<
N,
EthereumPoolBuilder,
HlPoolBuilder,
HlPayloadServiceBuilder,
HlNetworkBuilder,
HlExecutorBuilder,
@ -131,7 +132,7 @@ where
inner: BlockBody {
transactions: transactions
.into_transactions()
.map(|tx| tx.inner.into_inner().into())
.map(|tx| TransactionSigned(tx.inner.into_inner().into()))
.collect(),
ommers: Default::default(),
withdrawals,

View File

@ -64,7 +64,6 @@ where
to_network: UnboundedSender<ImportEvent>,
/// Pending block imports.
pending_imports: FuturesUnordered<ImportFut>,
height: u64,
}
impl<Provider> ImportService<Provider>
@ -77,7 +76,6 @@ where
engine: BeaconConsensusEngineHandle<HlPayloadTypes>,
from_network: UnboundedReceiver<IncomingBlock>,
to_network: UnboundedSender<ImportEvent>,
height: u64,
) -> Self {
Self {
engine,
@ -85,7 +83,6 @@ where
from_network,
to_network,
pending_imports: FuturesUnordered::new(),
height,
}
}
@ -172,23 +169,10 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let prev_height = this.height;
// Receive new blocks from network
while let Some(block) = collect_block(this.height) {
if this.height > prev_height + 1000 {
break;
}
let peer_id = PeerId::random();
let reth_block = block.to_reth_block();
let td = U128::from(reth_block.header().difficulty());
let msg = NewBlockMessage {
hash: reth_block.header().hash_slow(),
block: Arc::new(HlNewBlock(NewBlock { block: reth_block, td })),
};
this.on_new_block(msg, peer_id);
this.height += 1;
while let Poll::Ready(Some((block, peer_id))) = this.from_network.poll_recv(cx) {
this.on_new_block(block, peer_id);
}
// Process completed imports and send events to network
@ -366,7 +350,7 @@ mod tests {
let handle = ImportHandle::new(to_import, import_outcome);
let service = ImportService::new(consensus, engine_handle, from_network, to_network, 1);
let service = ImportService::new(consensus, engine_handle, from_network, to_network);
tokio::spawn(Box::pin(async move {
service.await.unwrap();
}));

View File

@ -11,7 +11,6 @@ use crate::{
HlBlock,
};
use alloy_rlp::{Decodable, Encodable};
use reth_provider::BlockNumReader;
// use handshake::HlHandshake;
use reth::{
api::{FullNodeTypes, TxTy},
@ -37,12 +36,14 @@ pub struct HlNewBlock(pub NewBlock<HlBlock>);
mod rlp {
use super::*;
use crate::HlBlockBody;
use alloy_consensus::{BlobTransactionSidecar, BlockBody, Header};
use crate::{
node::primitives::{BlockBody, TransactionSigned},
HlBlockBody,
};
use alloy_consensus::{BlobTransactionSidecar, Header};
use alloy_primitives::U128;
use alloy_rlp::{RlpDecodable, RlpEncodable};
use alloy_rpc_types::Withdrawals;
use reth_primitives::TransactionSigned;
use std::borrow::Cow;
#[derive(RlpEncodable, RlpDecodable)]
@ -170,8 +171,6 @@ impl HlNetworkBuilder {
let handle = ImportHandle::new(to_import, import_outcome);
let consensus = Arc::new(HlConsensus { provider: ctx.provider().clone() });
let number = ctx.provider().last_block_number().unwrap_or(1);
let number = std::cmp::max(number, 1);
ctx.task_executor().spawn_critical("block import", async move {
let handle = engine_handle_rx
@ -182,12 +181,13 @@ impl HlNetworkBuilder {
.await
.unwrap();
ImportService::new(consensus, handle, from_network, to_network, number).await.unwrap();
ImportService::new(consensus, handle, from_network, to_network).await.unwrap();
});
let network_builder = network_builder
.boot_nodes(boot_nodes())
.set_head(ctx.head())
.with_pow()
.block_import(Box::new(HlBlockImport::new(handle)));
// .discovery(discv4)
// .eth_rlpx_handshake(Arc::new(HlHandshake::default()));

436
src/node/pool.rs Normal file
View File

@ -0,0 +1,436 @@
//! A placeholder for the transaction pool.
//!
//! We'll not use transaction pool in HL, since this node does not write to HL consensus;
//! just a minimum implementation to make the node compile would suffice.
//!
//! Ethereum transaction pool only supports TransactionSigned (EthereumTxEnvelope<TxEip4844>),
//! hence this placeholder for the transaction pool.
use crate::node::{primitives::TransactionSigned, HlNode};
use alloy_consensus::{
error::ValueError, EthereumTxEnvelope, Transaction as TransactionTrait, TxEip4844,
};
use alloy_eips::{
eip4844::BlobAndProofV2, eip7594::BlobTransactionSidecarVariant, eip7702::SignedAuthorization,
Typed2718,
};
use alloy_primitives::{Address, Bytes, ChainId, TxHash, TxKind, B256, U256};
use alloy_rpc_types::AccessList;
use alloy_rpc_types_engine::BlobAndProofV1;
use reth::{
api::FullNodeTypes,
builder::components::PoolBuilder,
transaction_pool::{PoolResult, PoolSize, PoolTransaction, TransactionOrigin, TransactionPool},
};
use reth_eth_wire::HandleMempoolData;
use reth_ethereum_primitives::PooledTransactionVariant;
use reth_primitives::Recovered;
use reth_primitives_traits::InMemorySize;
use reth_transaction_pool::{
error::InvalidPoolTransactionError, AllPoolTransactions, AllTransactionsEvents, BestTransactions, BestTransactionsAttributes, BlobStoreError, BlockInfo, EthPoolTransaction, GetPooledTransactionLimit, NewBlobSidecar, NewTransactionEvent, PropagatedTransactions, TransactionEvents, TransactionListenerKind, ValidPoolTransaction
};
use std::{collections::HashSet, sync::Arc};
use tokio::sync::mpsc::{self, Receiver};
pub struct HlPoolBuilder;
impl<Node> PoolBuilder<Node> for HlPoolBuilder
where
Node: FullNodeTypes<Types = HlNode>,
{
type Pool = HlTransactionPool;
async fn build_pool(
self,
_ctx: &reth::builder::BuilderContext<Node>,
) -> eyre::Result<Self::Pool> {
Ok(HlTransactionPool)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HlPooledTransaction;
impl InMemorySize for HlPooledTransaction {
fn size(&self) -> usize {
0
}
}
impl TransactionTrait for HlPooledTransaction {
fn chain_id(&self) -> Option<ChainId> {
unreachable!()
}
fn nonce(&self) -> u64 {
unreachable!()
}
fn gas_limit(&self) -> u64 {
unreachable!()
}
fn gas_price(&self) -> Option<u128> {
unreachable!()
}
fn max_fee_per_gas(&self) -> u128 {
unreachable!()
}
fn max_priority_fee_per_gas(&self) -> Option<u128> {
unreachable!()
}
fn max_fee_per_blob_gas(&self) -> Option<u128> {
unreachable!()
}
fn priority_fee_or_price(&self) -> u128 {
unreachable!()
}
fn effective_gas_price(&self, _base_fee: Option<u64>) -> u128 {
unreachable!()
}
fn is_dynamic_fee(&self) -> bool {
unreachable!()
}
fn kind(&self) -> TxKind {
unreachable!()
}
fn is_create(&self) -> bool {
unreachable!()
}
fn value(&self) -> U256 {
unreachable!()
}
fn input(&self) -> &Bytes {
unreachable!()
}
fn access_list(&self) -> Option<&AccessList> {
unreachable!()
}
fn blob_versioned_hashes(&self) -> Option<&[B256]> {
unreachable!()
}
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
unreachable!()
}
}
impl Typed2718 for HlPooledTransaction {
fn ty(&self) -> u8 {
unreachable!()
}
}
impl PoolTransaction for HlPooledTransaction {
type TryFromConsensusError = ValueError<EthereumTxEnvelope<TxEip4844>>;
type Consensus = TransactionSigned;
type Pooled = PooledTransactionVariant;
fn try_from_consensus(
_tx: Recovered<Self::Consensus>,
) -> Result<Self, Self::TryFromConsensusError> {
unreachable!()
}
fn clone_into_consensus(&self) -> Recovered<Self::Consensus> {
unreachable!()
}
fn into_consensus(self) -> Recovered<Self::Consensus> {
unreachable!()
}
fn from_pooled(_tx: Recovered<Self::Pooled>) -> Self {
unreachable!()
}
fn hash(&self) -> &TxHash {
unreachable!()
}
fn sender(&self) -> Address {
unreachable!()
}
fn sender_ref(&self) -> &Address {
unreachable!()
}
fn cost(&self) -> &U256 {
unreachable!()
}
fn encoded_length(&self) -> usize {
0
}
fn ensure_max_init_code_size(
&self,
_max_init_code_size: usize,
) -> Result<(), InvalidPoolTransactionError> {
Ok(())
}
}
impl EthPoolTransaction for HlPooledTransaction {
fn take_blob(&mut self) -> reth_transaction_pool::EthBlobTransactionSidecar {
unreachable!()
}
fn try_into_pooled_eip4844(
self,
_sidecar: Arc<alloy_eips::eip7594::BlobTransactionSidecarVariant>,
) -> Option<Recovered<Self::Pooled>> {
None
}
fn try_from_eip4844(
_tx: Recovered<Self::Consensus>,
_sidecar: alloy_eips::eip7594::BlobTransactionSidecarVariant,
) -> Option<Self> {
None
}
fn validate_blob(
&self,
_blob: &alloy_eips::eip7594::BlobTransactionSidecarVariant,
_settings: &alloy_eips::eip4844::env_settings::KzgSettings,
) -> Result<(), alloy_consensus::BlobTransactionValidationError> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HlTransactionPool;
impl TransactionPool for HlTransactionPool {
type Transaction = HlPooledTransaction;
fn pool_size(&self) -> PoolSize {
PoolSize::default()
}
fn block_info(&self) -> BlockInfo {
BlockInfo::default()
}
async fn add_transaction_and_subscribe(
&self,
_origin: TransactionOrigin,
_transaction: Self::Transaction,
) -> PoolResult<TransactionEvents> {
unreachable!()
}
async fn add_transaction(
&self,
_origin: TransactionOrigin,
_transaction: Self::Transaction,
) -> PoolResult<TxHash> {
Ok(TxHash::default())
}
async fn add_transactions(
&self,
_origin: TransactionOrigin,
_transactions: Vec<Self::Transaction>,
) -> Vec<PoolResult<TxHash>> {
vec![]
}
fn transaction_event_listener(&self, _tx_hash: TxHash) -> Option<TransactionEvents> {
None
}
fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction> {
unreachable!()
}
fn pending_transactions_listener_for(
&self,
_kind: TransactionListenerKind,
) -> Receiver<TxHash> {
mpsc::channel(1).1
}
fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar> {
mpsc::channel(1).1
}
fn new_transactions_listener_for(
&self,
_kind: TransactionListenerKind,
) -> Receiver<NewTransactionEvent<Self::Transaction>> {
mpsc::channel(1).1
}
fn pooled_transaction_hashes(&self) -> Vec<TxHash> {
vec![]
}
fn pooled_transaction_hashes_max(&self, _max: usize) -> Vec<TxHash> {
vec![]
}
fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn pooled_transactions_max(
&self,
_max: usize,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn get_pooled_transaction_elements(
&self,
_tx_hashes: Vec<TxHash>,
_limit: GetPooledTransactionLimit,
) -> Vec<<Self::Transaction as PoolTransaction>::Pooled> {
vec![]
}
fn get_pooled_transaction_element(
&self,
_tx_hash: TxHash,
) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>> {
None
}
fn best_transactions(
&self,
) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
Box::new(std::iter::empty())
}
fn best_transactions_with_attributes(
&self,
_best_transactions_attributes: BestTransactionsAttributes,
) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
Box::new(std::iter::empty())
}
fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn pending_transactions_max(
&self,
_max: usize,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction> {
AllPoolTransactions::default()
}
fn remove_transactions(
&self,
_hashes: Vec<TxHash>,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn remove_transactions_and_descendants(
&self,
_hashes: Vec<TxHash>,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn remove_transactions_by_sender(
&self,
_sender: Address,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn retain_unknown<A>(&self, _announcement: &mut A)
where
A: HandleMempoolData,
{
// do nothing
}
fn get(&self, _tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
None
}
fn get_all(&self, _txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn on_propagated(&self, _txs: PropagatedTransactions) {
// do nothing
}
fn get_transactions_by_sender(
&self,
_sender: Address,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn get_pending_transactions_with_predicate(
&self,
_predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn get_pending_transactions_by_sender(
&self,
_sender: Address,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
vec![]
}
fn get_queued_transactions_by_sender(
&self,
_sender: Address,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
unreachable!()
}
fn get_highest_transaction_by_sender(
&self,
_sender: Address,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
None
}
fn get_highest_consecutive_transaction_by_sender(
&self,
_sender: Address,
_on_chain_nonce: u64,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
None
}
fn get_transaction_by_sender_and_nonce(
&self,
_sender: Address,
_nonce: u64,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
None
}
fn get_transactions_by_origin(
&self,
_origin: TransactionOrigin,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
unreachable!()
}
fn get_pending_transactions_by_origin(
&self,
_origin: TransactionOrigin,
) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
unreachable!()
}
fn unique_senders(&self) -> HashSet<Address> {
unreachable!()
}
fn get_blob(
&self,
_tx_hash: TxHash,
) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
unreachable!()
}
fn get_all_blobs(
&self,
_tx_hashes: Vec<TxHash>,
) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError> {
unreachable!()
}
fn get_all_blobs_exact(
&self,
_tx_hashes: Vec<TxHash>,
) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
unreachable!()
}
fn get_blobs_for_versioned_hashes_v1(
&self,
_versioned_hashes: &[B256],
) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
unreachable!()
}
fn get_blobs_for_versioned_hashes_v2(
&self,
_versioned_hashes: &[B256],
) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError> {
unreachable!()
}
}

View File

@ -1,14 +1,17 @@
#![allow(clippy::owned_cow)]
use alloy_consensus::{BlobTransactionSidecar, Header};
use alloy_rlp::{Encodable, RlpDecodable, RlpEncodable};
use reth_ethereum_primitives::{BlockBody, Receipt};
use reth_primitives::{NodePrimitives, TransactionSigned};
use reth_ethereum_primitives::Receipt;
use reth_primitives::NodePrimitives;
use reth_primitives_traits::{Block, BlockBody as BlockBodyTrait, InMemorySize};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use crate::node::types::{ReadPrecompileCall, ReadPrecompileCalls};
pub mod tx_wrapper;
pub use tx_wrapper::{BlockBody, TransactionSigned};
/// Primitive types for HyperEVM.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
@ -46,11 +49,13 @@ pub struct HlBlockBody {
impl InMemorySize for HlBlockBody {
fn size(&self) -> usize {
self.inner.size() +
self.sidecars
self.inner.size()
+ self
.sidecars
.as_ref()
.map_or(0, |s| s.capacity() * core::mem::size_of::<BlobTransactionSidecar>()) +
self.read_precompile_calls
.map_or(0, |s| s.capacity() * core::mem::size_of::<BlobTransactionSidecar>())
+ self
.read_precompile_calls
.as_ref()
.map_or(0, |s| s.0.capacity() * core::mem::size_of::<ReadPrecompileCall>())
}
@ -79,6 +84,16 @@ impl BlockBodyTrait for HlBlockBody {
fn ommers(&self) -> Option<&[Self::OmmerHeader]> {
self.inner.ommers()
}
fn calculate_tx_root(&self) -> alloy_primitives::B256 {
alloy_consensus::proofs::calculate_transaction_root(
&self
.transactions()
.iter()
.filter(|tx| !tx.is_system_transaction())
.collect::<Vec<_>>(),
)
}
}
/// Block for HL

View File

@ -0,0 +1,398 @@
//! HlNodePrimitives::TransactionSigned; it's the same as ethereum transaction type,
//! except that it supports pseudo signer for system transactions.
use std::hash::Hasher;
use alloy_consensus::{
crypto::RecoveryError, EthereumTxEnvelope, Signed, Transaction as TransactionTrait, TxEip1559,
TxEip2930, TxEip4844, TxEip4844WithSidecar, TxEip7702, TxEnvelope, TxLegacy, TxType,
TypedTransaction,
};
use alloy_eips::{
eip2718::Eip2718Result, eip7594::BlobTransactionSidecarVariant, eip7702::SignedAuthorization,
Decodable2718, Encodable2718, Typed2718,
};
use alloy_primitives::{address, Address, Bytes, TxHash, TxKind, Uint, B256, U256};
use alloy_rpc_types::AccessList;
use alloy_signer::Signature;
use reth_codecs::alloy::transaction::FromTxCompact;
use reth_db::{
table::{Compress, Decompress},
DatabaseError,
};
use reth_evm::FromRecoveredTx;
use reth_primitives::Recovered;
use reth_primitives_traits::{
serde_bincode_compat::SerdeBincodeCompat, InMemorySize, SignedTransaction, SignerRecoverable,
};
use revm::context::TxEnv;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
type InnerType = alloy_consensus::EthereumTxEnvelope<TxEip4844>;
#[derive(Debug, Clone, Eq)]
pub struct TransactionSigned(pub InnerType);
fn s_to_address(s: U256) -> Address {
if s == U256::ONE {
return address!("2222222222222222222222222222222222222222");
}
let mut buf = [0u8; 20];
buf[0..20].copy_from_slice(&s.to_be_bytes::<32>()[12..32]);
Address::from_slice(&buf)
}
impl SignerRecoverable for TransactionSigned {
fn recover_signer(&self) -> Result<Address, RecoveryError> {
if self.is_system_transaction() {
return Ok(s_to_address(self.signature().s()));
}
self.0.recover_signer()
}
fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
if self.is_system_transaction() {
return Ok(s_to_address(self.signature().s()));
}
self.0.recover_signer_unchecked()
}
}
impl SignedTransaction for TransactionSigned {
fn tx_hash(&self) -> &TxHash {
self.0.tx_hash()
}
fn recover_signer_unchecked_with_buf(
&self,
buf: &mut Vec<u8>,
) -> Result<Address, RecoveryError> {
if self.is_system_transaction() {
return Ok(s_to_address(self.signature().s()));
}
self.0.recover_signer_unchecked_with_buf(buf)
}
}
// ------------------------------------------------------------
// NOTE: All lines below are just wrappers for the inner type.
// ------------------------------------------------------------
impl Serialize for TransactionSigned {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TransactionSigned {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(InnerType::deserialize(deserializer)?))
}
}
macro_rules! impl_from_signed {
($($tx:ident),*) => {
$(
impl From<Signed<$tx>> for TransactionSigned {
fn from(value: Signed<$tx>) -> Self {
Self(value.into())
}
}
)*
};
}
impl_from_signed!(TxLegacy, TxEip2930, TxEip1559, TxEip7702, TypedTransaction);
impl InMemorySize for TransactionSigned {
#[inline]
fn size(&self) -> usize {
self.0.size()
}
}
impl alloy_rlp::Encodable for TransactionSigned {
fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
self.0.encode(out);
}
fn length(&self) -> usize {
self.0.length()
}
}
impl alloy_rlp::Decodable for TransactionSigned {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Ok(Self(TxEnvelope::decode(buf)?.into()))
}
}
impl Encodable2718 for TransactionSigned {
fn type_flag(&self) -> Option<u8> {
self.0.type_flag()
}
fn encode_2718_len(&self) -> usize {
self.0.encode_2718_len()
}
fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
self.0.encode_2718(out)
}
}
impl Decodable2718 for TransactionSigned {
fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
Ok(Self(TxEnvelope::typed_decode(ty, buf)?.into()))
}
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
Ok(Self(TxEnvelope::fallback_decode(buf)?.into()))
}
}
impl Typed2718 for TransactionSigned {
fn ty(&self) -> u8 {
self.0.ty()
}
}
impl PartialEq for TransactionSigned {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl core::hash::Hash for TransactionSigned {
fn hash<H: Hasher>(&self, state: &mut H) {
core::hash::Hash::hash(&self.0, state);
}
}
impl TransactionTrait for TransactionSigned {
fn chain_id(&self) -> Option<u64> {
self.0.chain_id()
}
fn nonce(&self) -> u64 {
self.0.nonce()
}
fn gas_limit(&self) -> u64 {
self.0.gas_limit()
}
fn gas_price(&self) -> Option<u128> {
self.0.gas_price()
}
fn max_fee_per_gas(&self) -> u128 {
self.0.max_fee_per_gas()
}
fn max_priority_fee_per_gas(&self) -> Option<u128> {
self.0.max_priority_fee_per_gas()
}
fn max_fee_per_blob_gas(&self) -> Option<u128> {
self.0.max_fee_per_blob_gas()
}
fn priority_fee_or_price(&self) -> u128 {
self.0.priority_fee_or_price()
}
fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
self.0.effective_gas_price(base_fee)
}
fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
self.0.effective_tip_per_gas(base_fee)
}
fn is_dynamic_fee(&self) -> bool {
self.0.is_dynamic_fee()
}
fn kind(&self) -> TxKind {
self.0.kind()
}
fn is_create(&self) -> bool {
self.0.is_create()
}
fn value(&self) -> Uint<256, 4> {
self.0.value()
}
fn input(&self) -> &Bytes {
self.0.input()
}
fn access_list(&self) -> Option<&AccessList> {
self.0.access_list()
}
fn blob_versioned_hashes(&self) -> Option<&[B256]> {
self.0.blob_versioned_hashes()
}
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
self.0.authorization_list()
}
}
impl reth_codecs::Compact for TransactionSigned {
fn to_compact<B>(&self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
self.0.to_compact(buf)
}
fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
let (tx, hash) = InnerType::from_compact(buf, _len);
(Self(tx), hash)
}
}
pub fn convert_recovered(value: Recovered<TransactionSigned>) -> Recovered<InnerType> {
let (tx, signer) = value.into_parts();
Recovered::new_unchecked(tx.0, signer)
}
impl FromRecoveredTx<TransactionSigned> for TxEnv {
fn from_recovered_tx(tx: &TransactionSigned, sender: Address) -> Self {
TxEnv::from_recovered_tx(&tx.0, sender)
}
}
impl FromTxCompact for TransactionSigned {
type TxType = TxType;
fn from_tx_compact(buf: &[u8], tx_type: Self::TxType, signature: Signature) -> (Self, &[u8])
where
Self: Sized,
{
let (tx, buf) = InnerType::from_tx_compact(buf, tx_type, signature);
(Self(tx), buf)
}
}
impl reth_codecs::alloy::transaction::Envelope for TransactionSigned {
fn signature(&self) -> &Signature {
self.0.signature()
}
fn tx_type(&self) -> Self::TxType {
self.0.tx_type()
}
}
impl TransactionSigned {
pub const fn signature(&self) -> &Signature {
self.0.signature()
}
pub const fn tx_type(&self) -> TxType {
self.0.tx_type()
}
pub fn is_system_transaction(&self) -> bool {
self.gas_price().is_some() && self.gas_price().unwrap() == 0
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct BincodeCompatTxCustom(pub TransactionSigned);
impl SerdeBincodeCompat for TransactionSigned {
type BincodeRepr<'a> = BincodeCompatTxCustom;
fn as_repr(&self) -> Self::BincodeRepr<'_> {
BincodeCompatTxCustom(self.clone())
}
fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
repr.0
}
}
pub type BlockBody = alloy_consensus::BlockBody<TransactionSigned>;
impl From<TransactionSigned> for EthereumTxEnvelope<TxEip4844> {
fn from(value: TransactionSigned) -> Self {
value.0
}
}
impl TryFrom<TransactionSigned> for EthereumTxEnvelope<TxEip4844WithSidecar> {
type Error = <InnerType as TryInto<EthereumTxEnvelope<TxEip4844WithSidecar>>>::Error;
fn try_from(value: TransactionSigned) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl TryFrom<TransactionSigned>
for EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarVariant>>
{
type Error = <InnerType as TryInto<
EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarVariant>>,
>>::Error;
fn try_from(value: TransactionSigned) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl From<EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarVariant>>>
for TransactionSigned
{
fn from(
value: EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarVariant>>,
) -> Self {
Self(value.into())
}
}
impl Compress for TransactionSigned {
type Compressed = Vec<u8>;
fn compress(self) -> Self::Compressed {
self.0.compress()
}
fn compress_to_buf<B: bytes::BufMut + AsMut<[u8]>>(&self, buf: &mut B) {
self.0.compress_to_buf(buf);
}
}
impl Decompress for TransactionSigned {
fn decompress(value: &[u8]) -> Result<Self, DatabaseError> {
Ok(Self(InnerType::decompress(value)?))
}
}
pub fn convert_to_eth_block_body(value: BlockBody) -> alloy_consensus::BlockBody<InnerType> {
alloy_consensus::BlockBody {
transactions: value.transactions.into_iter().map(|tx| tx.0).collect(),
ommers: value.ommers,
withdrawals: value.withdrawals,
}
}
pub fn convert_to_hl_block_body(value: alloy_consensus::BlockBody<InnerType>) -> BlockBody {
BlockBody {
transactions: value.transactions.into_iter().map(TransactionSigned).collect(),
ommers: value.ommers,
withdrawals: value.withdrawals,
}
}

View File

@ -1,6 +1,7 @@
use crate::{
chainspec::HlChainSpec,
node::{
primitives::TransactionSigned,
rpc::{HlEthApi, HlNodeCore},
HlBlock, HlPrimitives,
},
@ -10,7 +11,7 @@ use alloy_primitives::B256;
use reth::{
api::NodeTypes,
builder::FullNodeComponents,
primitives::{Receipt, SealedHeader, TransactionMeta, TransactionSigned},
primitives::{Receipt, SealedHeader, TransactionMeta},
providers::{BlockReaderIdExt, ProviderHeader, ReceiptProvider, TransactionsProvider},
rpc::{
eth::EthApiTypes,
@ -64,7 +65,7 @@ where
.enumerate()
.map(|(idx, (tx, receipt))| {
let meta = TransactionMeta {
tx_hash: *tx.tx_hash(),
tx_hash: *tx.0.tx_hash(),
index: idx as u64,
block_hash,
block_number,
@ -72,7 +73,7 @@ where
excess_blob_gas,
timestamp,
};
EthReceiptBuilder::new(tx, meta, receipt, &receipts, blob_params)
EthReceiptBuilder::new(&tx.0, meta, receipt, &receipts, blob_params)
.map(|builder| builder.build())
})
.collect::<Result<Vec<_>, Self::Error>>()
@ -164,6 +165,6 @@ where
.ok_or(EthApiError::HeaderNotFound(hash.into()))?;
let blob_params = self.provider().chain_spec().blob_params_at_timestamp(meta.timestamp);
Ok(EthReceiptBuilder::new(&tx, meta, &receipt, &all_receipts, blob_params)?.build())
Ok(EthReceiptBuilder::new(&tx.0, meta, &receipt, &all_receipts, blob_params)?.build())
}
}

View File

@ -1,10 +1,16 @@
use super::HlNodeCore;
use crate::{node::rpc::HlEthApi, HlPrimitives};
use crate::{
node::{
primitives::{tx_wrapper::convert_recovered, TransactionSigned},
rpc::HlEthApi,
},
HlPrimitives,
};
use alloy_network::{Ethereum, Network};
use alloy_primitives::{Bytes, Signature, B256};
use reth::{
builder::FullNodeComponents,
primitives::{Receipt, Recovered, TransactionSigned},
primitives::{Receipt, Recovered},
providers::ReceiptProvider,
rpc::{
eth::helpers::types::EthRpcConverter,
@ -18,6 +24,7 @@ use reth_rpc_eth_api::{
helpers::{EthSigner, EthTransactions, LoadTransaction, SpawnBlocking},
FromEthApiError, FullEthApiTypes, RpcNodeCore, RpcNodeCoreExt, TransactionCompat,
};
impl<N> LoadTransaction for HlEthApi<N>
where
Self: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt,
@ -41,7 +48,7 @@ where
tx_info: TransactionInfo,
) -> Result<Self::Transaction, Self::Error> {
let builder = EthRpcConverter::default();
builder.fill(tx, tx_info)
builder.fill(convert_recovered(tx), tx_info)
}
fn build_simulate_v1_transaction(
@ -54,7 +61,10 @@ where
// Create an empty signature for the transaction.
let signature = Signature::new(Default::default(), Default::default(), false);
Ok(TransactionSigned::new_unhashed(tx.into(), signature))
Ok(TransactionSigned(reth_primitives::TransactionSigned::new_unhashed(
tx.into(),
signature,
)))
}
}

View File

@ -1,4 +1,10 @@
use crate::{node::types::ReadPrecompileCalls, HlBlock, HlBlockBody, HlPrimitives};
use crate::{
node::{
primitives::tx_wrapper::{convert_to_eth_block_body, convert_to_hl_block_body},
types::ReadPrecompileCalls,
},
HlBlock, HlBlockBody, HlPrimitives,
};
use alloy_consensus::BlockHeader;
use alloy_primitives::Bytes;
use reth_chainspec::EthereumHardforks;
@ -87,7 +93,7 @@ where
for (block_number, body) in bodies {
match body {
Some(HlBlockBody { inner, sidecars: _, read_precompile_calls: rpc }) => {
eth_bodies.push((block_number, Some(inner)));
eth_bodies.push((block_number, Some(convert_to_eth_block_body(inner))));
read_precompile_calls.push((block_number, rpc));
}
None => {
@ -128,14 +134,22 @@ where
inputs: Vec<ReadBodyInput<'_, Self::Block>>,
) -> ProviderResult<Vec<HlBlockBody>> {
let read_precompile_calls = self.read_precompile_calls(provider, &inputs)?;
let eth_bodies = self.0.read_block_bodies(provider, inputs)?;
let eth_bodies = self.0.read_block_bodies(
provider,
inputs
.into_iter()
.map(|(header, transactions)| {
(header, transactions.into_iter().map(|tx| tx.0).collect())
})
.collect(),
)?;
// NOTE: sidecars are not used in HyperEVM yet.
Ok(eth_bodies
.into_iter()
.zip(read_precompile_calls)
.map(|(inner, read_precompile_calls)| HlBlockBody {
inner,
inner: convert_to_hl_block_body(inner),
sidecars: None,
read_precompile_calls,
})

View File

@ -2,7 +2,7 @@
//!
//! Changes:
//! - ReadPrecompileCalls supports RLP encoding / decoding
use alloy_primitives::{Address, Bytes, Log};
use alloy_primitives::{Address, Bytes, Log, B256};
use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
use bytes::BufMut;
use revm::primitives::HashMap;
@ -69,6 +69,16 @@ impl BlockAndReceipts {
MAINNET_CHAIN_ID,
)
}
pub fn hash(&self) -> B256 {
let EvmBlock::Reth115(block) = &self.block;
block.header.hash
}
pub fn number(&self) -> u64 {
let EvmBlock::Reth115(block) = &self.block;
block.header.header.number
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -1,15 +1,17 @@
//! Copy of reth codebase to preserve serialization compatibility
use alloy_consensus::{Header, Signed, TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy};
use alloy_primitives::{Address, BlockHash, Signature, TxKind, U256};
use reth_primitives::TransactionSigned as RethTxSigned;
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
sync::{Arc, LazyLock, Mutex},
sync::{Arc, LazyLock, RwLock},
};
use tracing::info;
use crate::{
node::{
primitives::TransactionSigned as TxSigned,
spot_meta::{erc20_contract_to_spot_token, SpotId},
types::{ReadPrecompileCalls, SystemTx},
},
@ -42,23 +44,23 @@ pub struct TransactionSigned {
transaction: Transaction,
}
impl TransactionSigned {
fn to_reth_transaction(&self) -> reth_primitives::TransactionSigned {
fn to_reth_transaction(&self) -> TxSigned {
match self.transaction.clone() {
Transaction::Legacy(tx) => {
reth_primitives::TransactionSigned::Legacy(Signed::new_unhashed(tx, self.signature))
TxSigned(RethTxSigned::Legacy(Signed::new_unhashed(tx, self.signature)))
}
Transaction::Eip2930(tx) => {
TxSigned(RethTxSigned::Eip2930(Signed::new_unhashed(tx, self.signature)))
}
Transaction::Eip1559(tx) => {
TxSigned(RethTxSigned::Eip1559(Signed::new_unhashed(tx, self.signature)))
}
Transaction::Eip4844(tx) => {
TxSigned(RethTxSigned::Eip4844(Signed::new_unhashed(tx, self.signature)))
}
Transaction::Eip7702(tx) => {
TxSigned(RethTxSigned::Eip7702(Signed::new_unhashed(tx, self.signature)))
}
Transaction::Eip2930(tx) => reth_primitives::TransactionSigned::Eip2930(
Signed::new_unhashed(tx, self.signature),
),
Transaction::Eip1559(tx) => reth_primitives::TransactionSigned::Eip1559(
Signed::new_unhashed(tx, self.signature),
),
Transaction::Eip4844(tx) => reth_primitives::TransactionSigned::Eip4844(
Signed::new_unhashed(tx, self.signature),
),
Transaction::Eip7702(tx) => reth_primitives::TransactionSigned::Eip7702(
Signed::new_unhashed(tx, self.signature),
),
}
}
}
@ -67,24 +69,21 @@ type BlockBody = alloy_consensus::BlockBody<TransactionSigned, Header>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SealedHeader {
hash: BlockHash,
header: Header,
pub hash: BlockHash,
pub header: Header,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SealedBlock {
/// Sealed Header.
header: SealedHeader,
pub header: SealedHeader,
/// the block's body.
body: BlockBody,
pub body: BlockBody,
}
fn system_tx_to_reth_transaction(
transaction: &SystemTx,
chain_id: u64,
) -> reth_primitives::TransactionSigned {
static EVM_MAP: LazyLock<Arc<Mutex<BTreeMap<Address, SpotId>>>> =
LazyLock::new(|| Arc::new(Mutex::new(BTreeMap::new())));
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");
@ -96,16 +95,16 @@ fn system_tx_to_reth_transaction(
U256::from(0x1)
} else {
loop {
if let Some(spot) = EVM_MAP.lock().unwrap().get(&to) {
if let Some(spot) = EVM_MAP.read().unwrap().get(&to) {
break spot.to_s();
}
info!("Contract not found: {:?} from spot mapping, fetching again...", to);
*EVM_MAP.lock().unwrap() = erc20_contract_to_spot_token(chain_id).unwrap();
*EVM_MAP.write().unwrap() = erc20_contract_to_spot_token(chain_id).unwrap();
}
};
let signature = Signature::new(U256::from(0x1), s, true);
reth_primitives::TransactionSigned::Legacy(Signed::new_unhashed(tx.clone(), signature))
TxSigned(RethTxSigned::Legacy(Signed::new_unhashed(tx.clone(), signature)))
}
}