mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
chore(op-reth): move op-dependent examples into crates/optimism/examples/ (#21495)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
@@ -1,71 +0,0 @@
|
||||
[package]
|
||||
name = "example-custom-node"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-codecs.workspace = true
|
||||
reth-network-peers.workspace = true
|
||||
reth-node-builder.workspace = true
|
||||
reth-optimism-forks.workspace = true
|
||||
reth-optimism-flashblocks.workspace = true
|
||||
reth-db-api.workspace = true
|
||||
reth-op = { workspace = true, features = ["node", "pool", "rpc"] }
|
||||
reth-payload-builder.workspace = true
|
||||
reth-rpc-api.workspace = true
|
||||
reth-engine-primitives.workspace = true
|
||||
reth-rpc-engine-api.workspace = true
|
||||
reth-ethereum = { workspace = true, features = ["node-api", "network", "evm", "pool", "trie", "storage-api", "provider"] }
|
||||
|
||||
# revm
|
||||
revm.workspace = true
|
||||
revm-primitives.workspace = true
|
||||
|
||||
# alloy
|
||||
alloy-consensus = { workspace = true, features = ["serde"] }
|
||||
alloy-eips.workspace = true
|
||||
alloy-evm.workspace = true
|
||||
alloy-genesis.workspace = true
|
||||
alloy-op-evm.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
alloy-rlp.workspace = true
|
||||
alloy-serde.workspace = true
|
||||
alloy-network.workspace = true
|
||||
alloy-rpc-types-engine.workspace = true
|
||||
alloy-rpc-types-eth.workspace = true
|
||||
op-alloy-consensus.workspace = true
|
||||
op-alloy-rpc-types-engine.workspace = true
|
||||
op-alloy-rpc-types.workspace = true
|
||||
op-revm.workspace = true
|
||||
|
||||
# misc
|
||||
async-trait.workspace = true
|
||||
derive_more.workspace = true
|
||||
eyre.workspace = true
|
||||
jsonrpsee.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
modular-bitfield.workspace = true
|
||||
|
||||
[features]
|
||||
arbitrary = [
|
||||
"alloy-consensus/arbitrary",
|
||||
"alloy-eips/arbitrary",
|
||||
"alloy-primitives/arbitrary",
|
||||
"alloy-serde/arbitrary",
|
||||
"op-alloy-consensus/arbitrary",
|
||||
"op-alloy-rpc-types-engine/arbitrary",
|
||||
"reth-codecs/arbitrary",
|
||||
"reth-op/arbitrary",
|
||||
"revm-primitives/arbitrary",
|
||||
"revm/arbitrary",
|
||||
"reth-ethereum/arbitrary",
|
||||
"alloy-rpc-types-engine/arbitrary",
|
||||
"reth-db-api/arbitrary",
|
||||
"alloy-rpc-types-eth/arbitrary",
|
||||
"op-alloy-rpc-types/arbitrary",
|
||||
]
|
||||
default = []
|
||||
@@ -1,117 +0,0 @@
|
||||
use crate::primitives::CustomHeader;
|
||||
use alloy_genesis::Genesis;
|
||||
use reth_ethereum::{
|
||||
chainspec::{EthChainSpec, EthereumHardforks, Hardfork, Hardforks},
|
||||
primitives::SealedHeader,
|
||||
};
|
||||
use reth_network_peers::NodeRecord;
|
||||
use reth_op::chainspec::OpChainSpec;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomChainSpec {
|
||||
inner: OpChainSpec,
|
||||
genesis_header: SealedHeader<CustomHeader>,
|
||||
}
|
||||
|
||||
impl CustomChainSpec {
|
||||
pub const fn inner(&self) -> &OpChainSpec {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Hardforks for CustomChainSpec {
|
||||
fn fork<H: Hardfork>(&self, fork: H) -> reth_ethereum::chainspec::ForkCondition {
|
||||
self.inner.fork(fork)
|
||||
}
|
||||
|
||||
fn forks_iter(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&dyn Hardfork, reth_ethereum::chainspec::ForkCondition)> {
|
||||
self.inner.forks_iter()
|
||||
}
|
||||
|
||||
fn fork_id(&self, head: &reth_ethereum::chainspec::Head) -> reth_ethereum::chainspec::ForkId {
|
||||
self.inner.fork_id(head)
|
||||
}
|
||||
|
||||
fn latest_fork_id(&self) -> reth_ethereum::chainspec::ForkId {
|
||||
self.inner.latest_fork_id()
|
||||
}
|
||||
|
||||
fn fork_filter(
|
||||
&self,
|
||||
head: reth_ethereum::chainspec::Head,
|
||||
) -> reth_ethereum::chainspec::ForkFilter {
|
||||
self.inner.fork_filter(head)
|
||||
}
|
||||
}
|
||||
|
||||
impl EthChainSpec for CustomChainSpec {
|
||||
type Header = CustomHeader;
|
||||
|
||||
fn chain(&self) -> reth_ethereum::chainspec::Chain {
|
||||
self.inner.chain()
|
||||
}
|
||||
|
||||
fn base_fee_params_at_timestamp(
|
||||
&self,
|
||||
timestamp: u64,
|
||||
) -> reth_ethereum::chainspec::BaseFeeParams {
|
||||
self.inner.base_fee_params_at_timestamp(timestamp)
|
||||
}
|
||||
|
||||
fn blob_params_at_timestamp(&self, timestamp: u64) -> Option<alloy_eips::eip7840::BlobParams> {
|
||||
self.inner.blob_params_at_timestamp(timestamp)
|
||||
}
|
||||
|
||||
fn deposit_contract(&self) -> Option<&reth_ethereum::chainspec::DepositContract> {
|
||||
self.inner.deposit_contract()
|
||||
}
|
||||
|
||||
fn genesis_hash(&self) -> revm_primitives::B256 {
|
||||
self.genesis_header.hash()
|
||||
}
|
||||
|
||||
fn prune_delete_limit(&self) -> usize {
|
||||
self.inner.prune_delete_limit()
|
||||
}
|
||||
|
||||
fn display_hardforks(&self) -> Box<dyn std::fmt::Display> {
|
||||
self.inner.display_hardforks()
|
||||
}
|
||||
|
||||
fn genesis_header(&self) -> &Self::Header {
|
||||
&self.genesis_header
|
||||
}
|
||||
|
||||
fn genesis(&self) -> &Genesis {
|
||||
self.inner.genesis()
|
||||
}
|
||||
|
||||
fn bootnodes(&self) -> Option<Vec<NodeRecord>> {
|
||||
self.inner.bootnodes()
|
||||
}
|
||||
|
||||
fn final_paris_total_difficulty(&self) -> Option<revm_primitives::U256> {
|
||||
self.inner.get_final_paris_total_difficulty()
|
||||
}
|
||||
}
|
||||
|
||||
impl EthereumHardforks for CustomChainSpec {
|
||||
fn ethereum_fork_activation(
|
||||
&self,
|
||||
fork: reth_ethereum::chainspec::EthereumHardfork,
|
||||
) -> reth_ethereum::chainspec::ForkCondition {
|
||||
self.inner.ethereum_fork_activation(fork)
|
||||
}
|
||||
}
|
||||
|
||||
impl OpHardforks for CustomChainSpec {
|
||||
fn op_fork_activation(
|
||||
&self,
|
||||
fork: reth_optimism_forks::OpHardfork,
|
||||
) -> reth_ethereum::chainspec::ForkCondition {
|
||||
self.inner.op_fork_activation(fork)
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
use crate::{
|
||||
chainspec::CustomChainSpec,
|
||||
evm::CustomEvmConfig,
|
||||
primitives::{CustomHeader, CustomNodePrimitives, CustomTransaction},
|
||||
CustomNode,
|
||||
};
|
||||
use alloy_eips::eip2718::WithEncoded;
|
||||
use alloy_primitives::Bytes;
|
||||
use op_alloy_rpc_types_engine::{OpExecutionData, OpExecutionPayload};
|
||||
use reth_engine_primitives::EngineApiValidator;
|
||||
use reth_ethereum::{
|
||||
node::api::{
|
||||
validate_version_specific_fields, AddOnsContext, BuiltPayload, BuiltPayloadExecutedBlock,
|
||||
EngineApiMessageVersion, EngineObjectValidationError, ExecutionPayload, FullNodeComponents,
|
||||
NewPayloadError, NodePrimitives, PayloadAttributes, PayloadBuilderAttributes,
|
||||
PayloadOrAttributes, PayloadTypes, PayloadValidator,
|
||||
},
|
||||
primitives::SealedBlock,
|
||||
storage::StateProviderFactory,
|
||||
trie::{KeccakKeyHasher, KeyHasher},
|
||||
};
|
||||
use reth_node_builder::{rpc::PayloadValidatorBuilder, InvalidPayloadAttributesError};
|
||||
use reth_op::node::{
|
||||
engine::OpEngineValidator, payload::OpAttributes, OpBuiltPayload, OpEngineTypes,
|
||||
OpPayloadAttributes, OpPayloadBuilderAttributes,
|
||||
};
|
||||
use revm_primitives::U256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct CustomPayloadTypes;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CustomExecutionData {
|
||||
pub inner: OpExecutionData,
|
||||
pub extension: u64,
|
||||
}
|
||||
|
||||
impl ExecutionPayload for CustomExecutionData {
|
||||
fn parent_hash(&self) -> revm_primitives::B256 {
|
||||
self.inner.parent_hash()
|
||||
}
|
||||
|
||||
fn block_hash(&self) -> revm_primitives::B256 {
|
||||
self.inner.block_hash()
|
||||
}
|
||||
|
||||
fn block_number(&self) -> u64 {
|
||||
self.inner.block_number()
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> Option<&Vec<alloy_eips::eip4895::Withdrawal>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn block_access_list(&self) -> Option<&Bytes> {
|
||||
None
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<revm_primitives::B256> {
|
||||
self.inner.parent_beacon_block_root()
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.inner.timestamp()
|
||||
}
|
||||
|
||||
fn gas_used(&self) -> u64 {
|
||||
self.inner.gas_used()
|
||||
}
|
||||
|
||||
fn transaction_count(&self) -> usize {
|
||||
self.inner.payload.as_v1().transactions.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&reth_optimism_flashblocks::FlashBlockCompleteSequence> for CustomExecutionData {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(
|
||||
sequence: &reth_optimism_flashblocks::FlashBlockCompleteSequence,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let inner = OpExecutionData::try_from(sequence)?;
|
||||
Ok(Self { inner, extension: sequence.last().diff.gas_used })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CustomPayloadAttributes {
|
||||
#[serde(flatten)]
|
||||
inner: OpPayloadAttributes,
|
||||
extension: u64,
|
||||
}
|
||||
|
||||
impl PayloadAttributes for CustomPayloadAttributes {
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.inner.timestamp()
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> Option<&Vec<alloy_eips::eip4895::Withdrawal>> {
|
||||
self.inner.withdrawals()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<revm_primitives::B256> {
|
||||
self.inner.parent_beacon_block_root()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomPayloadBuilderAttributes {
|
||||
pub inner: OpPayloadBuilderAttributes<CustomTransaction>,
|
||||
pub extension: u64,
|
||||
}
|
||||
|
||||
impl PayloadBuilderAttributes for CustomPayloadBuilderAttributes {
|
||||
type RpcPayloadAttributes = CustomPayloadAttributes;
|
||||
type Error = alloy_rlp::Error;
|
||||
|
||||
fn try_new(
|
||||
parent: revm_primitives::B256,
|
||||
rpc_payload_attributes: Self::RpcPayloadAttributes,
|
||||
version: u8,
|
||||
) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let CustomPayloadAttributes { inner, extension } = rpc_payload_attributes;
|
||||
|
||||
Ok(Self { inner: OpPayloadBuilderAttributes::try_new(parent, inner, version)?, extension })
|
||||
}
|
||||
|
||||
fn payload_id(&self) -> alloy_rpc_types_engine::PayloadId {
|
||||
self.inner.payload_id()
|
||||
}
|
||||
|
||||
fn parent(&self) -> revm_primitives::B256 {
|
||||
self.inner.parent()
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.inner.timestamp()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<revm_primitives::B256> {
|
||||
self.inner.parent_beacon_block_root()
|
||||
}
|
||||
|
||||
fn suggested_fee_recipient(&self) -> revm_primitives::Address {
|
||||
self.inner.suggested_fee_recipient()
|
||||
}
|
||||
|
||||
fn prev_randao(&self) -> revm_primitives::B256 {
|
||||
self.inner.prev_randao()
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> &alloy_eips::eip4895::Withdrawals {
|
||||
self.inner.withdrawals()
|
||||
}
|
||||
}
|
||||
|
||||
impl OpAttributes for CustomPayloadBuilderAttributes {
|
||||
type Transaction = CustomTransaction;
|
||||
|
||||
fn no_tx_pool(&self) -> bool {
|
||||
self.inner.no_tx_pool
|
||||
}
|
||||
|
||||
fn sequencer_transactions(&self) -> &[WithEncoded<Self::Transaction>] {
|
||||
&self.inner.transactions
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomBuiltPayload(pub OpBuiltPayload<CustomNodePrimitives>);
|
||||
|
||||
impl BuiltPayload for CustomBuiltPayload {
|
||||
type Primitives = CustomNodePrimitives;
|
||||
|
||||
fn block(&self) -> &SealedBlock<<Self::Primitives as NodePrimitives>::Block> {
|
||||
self.0.block()
|
||||
}
|
||||
|
||||
fn fees(&self) -> U256 {
|
||||
self.0.fees()
|
||||
}
|
||||
|
||||
fn executed_block(&self) -> Option<BuiltPayloadExecutedBlock<Self::Primitives>> {
|
||||
self.0.executed_block()
|
||||
}
|
||||
|
||||
fn requests(&self) -> Option<alloy_eips::eip7685::Requests> {
|
||||
self.0.requests()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CustomBuiltPayload>
|
||||
for alloy_consensus::Block<<CustomNodePrimitives as NodePrimitives>::SignedTx>
|
||||
{
|
||||
fn from(value: CustomBuiltPayload) -> Self {
|
||||
value.0.into_sealed_block().into_block().map_header(|header| header.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl PayloadTypes for CustomPayloadTypes {
|
||||
type ExecutionData = CustomExecutionData;
|
||||
type BuiltPayload = OpBuiltPayload<CustomNodePrimitives>;
|
||||
type PayloadAttributes = CustomPayloadAttributes;
|
||||
type PayloadBuilderAttributes = CustomPayloadBuilderAttributes;
|
||||
|
||||
fn block_to_payload(
|
||||
block: SealedBlock<
|
||||
<<Self::BuiltPayload as BuiltPayload>::Primitives as NodePrimitives>::Block,
|
||||
>,
|
||||
) -> Self::ExecutionData {
|
||||
let extension = block.header().extension;
|
||||
let block_hash = block.hash();
|
||||
let block = block.into_block().map_header(|header| header.inner);
|
||||
let (payload, sidecar) = OpExecutionPayload::from_block_unchecked(block_hash, &block);
|
||||
CustomExecutionData { inner: OpExecutionData { payload, sidecar }, extension }
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom engine validator
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomEngineValidator<P> {
|
||||
inner: OpEngineValidator<P, CustomTransaction, CustomChainSpec>,
|
||||
}
|
||||
|
||||
impl<P> CustomEngineValidator<P>
|
||||
where
|
||||
P: Send + Sync + Unpin + 'static,
|
||||
{
|
||||
/// Instantiates a new validator.
|
||||
pub fn new<KH: KeyHasher>(chain_spec: Arc<CustomChainSpec>, provider: P) -> Self {
|
||||
Self { inner: OpEngineValidator::new::<KH>(chain_spec, provider) }
|
||||
}
|
||||
|
||||
/// Returns the chain spec used by the validator.
|
||||
#[inline]
|
||||
fn chain_spec(&self) -> &CustomChainSpec {
|
||||
self.inner.chain_spec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> PayloadValidator<CustomPayloadTypes> for CustomEngineValidator<P>
|
||||
where
|
||||
P: StateProviderFactory + Send + Sync + Unpin + 'static,
|
||||
{
|
||||
type Block = crate::primitives::block::Block;
|
||||
|
||||
fn validate_payload_attributes_against_header(
|
||||
&self,
|
||||
_attr: &CustomPayloadAttributes,
|
||||
_header: &<Self::Block as reth_ethereum::primitives::Block>::Header,
|
||||
) -> Result<(), InvalidPayloadAttributesError> {
|
||||
// skip default timestamp validation
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_payload_to_block(
|
||||
&self,
|
||||
payload: CustomExecutionData,
|
||||
) -> Result<SealedBlock<Self::Block>, NewPayloadError> {
|
||||
let sealed_block = PayloadValidator::<OpEngineTypes>::convert_payload_to_block(
|
||||
&self.inner,
|
||||
payload.inner,
|
||||
)?;
|
||||
let (header, body) = sealed_block.split_sealed_header_body();
|
||||
let header = CustomHeader { inner: header.into_header(), extension: payload.extension };
|
||||
let body = body.map_ommers(|_| CustomHeader::default());
|
||||
Ok(SealedBlock::<Self::Block>::from_parts_unhashed(header, body))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> EngineApiValidator<CustomPayloadTypes> for CustomEngineValidator<P>
|
||||
where
|
||||
P: StateProviderFactory + Send + Sync + Unpin + 'static,
|
||||
{
|
||||
fn validate_version_specific_fields(
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
payload_or_attrs: PayloadOrAttributes<'_, CustomExecutionData, CustomPayloadAttributes>,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
validate_version_specific_fields(self.chain_spec(), version, payload_or_attrs)
|
||||
}
|
||||
|
||||
fn ensure_well_formed_attributes(
|
||||
&self,
|
||||
version: EngineApiMessageVersion,
|
||||
attributes: &CustomPayloadAttributes,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
validate_version_specific_fields(
|
||||
self.chain_spec(),
|
||||
version,
|
||||
PayloadOrAttributes::<CustomExecutionData, _>::PayloadAttributes(attributes),
|
||||
)?;
|
||||
|
||||
// custom validation logic - ensure that the custom field is not zero
|
||||
// if attributes.extension == 0 {
|
||||
// return Err(EngineObjectValidationError::invalid_params(
|
||||
// CustomError::CustomFieldIsNotZero,
|
||||
// ))
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom error type used in payload attributes validation
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CustomError {
|
||||
#[error("Custom field is not zero")]
|
||||
CustomFieldIsNotZero,
|
||||
}
|
||||
|
||||
/// Custom engine validator builder
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct CustomEngineValidatorBuilder;
|
||||
|
||||
impl<N> PayloadValidatorBuilder<N> for CustomEngineValidatorBuilder
|
||||
where
|
||||
N: FullNodeComponents<Types = CustomNode, Evm = CustomEvmConfig>,
|
||||
{
|
||||
type Validator = CustomEngineValidator<N::Provider>;
|
||||
|
||||
async fn build(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
|
||||
Ok(CustomEngineValidator::new::<KeccakKeyHasher>(
|
||||
ctx.config.chain.clone(),
|
||||
ctx.node.provider().clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
use crate::{
|
||||
engine::{CustomExecutionData, CustomPayloadAttributes, CustomPayloadTypes},
|
||||
primitives::CustomNodePrimitives,
|
||||
CustomNode,
|
||||
};
|
||||
use alloy_rpc_types_engine::{
|
||||
ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc, RpcModule};
|
||||
use reth_ethereum::node::api::{
|
||||
AddOnsContext, ConsensusEngineHandle, EngineApiMessageVersion, FullNodeComponents,
|
||||
};
|
||||
use reth_node_builder::rpc::EngineApiBuilder;
|
||||
use reth_op::node::OpBuiltPayload;
|
||||
use reth_payload_builder::PayloadStore;
|
||||
use reth_rpc_api::IntoEngineApiRpcModule;
|
||||
use reth_rpc_engine_api::EngineApiError;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CustomExecutionPayloadInput {}
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
pub struct CustomExecutionPayloadEnvelope {
|
||||
execution_payload: ExecutionPayloadV3,
|
||||
extension: u64,
|
||||
}
|
||||
|
||||
impl From<OpBuiltPayload<CustomNodePrimitives>> for CustomExecutionPayloadEnvelope {
|
||||
fn from(value: OpBuiltPayload<CustomNodePrimitives>) -> Self {
|
||||
let sealed_block = value.into_sealed_block();
|
||||
let hash = sealed_block.hash();
|
||||
let extension = sealed_block.header().extension;
|
||||
let block = sealed_block.into_block();
|
||||
|
||||
Self {
|
||||
execution_payload: ExecutionPayloadV3::from_block_unchecked(hash, &block),
|
||||
extension,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rpc(server, namespace = "engine")]
|
||||
pub trait CustomEngineApi {
|
||||
#[method(name = "newPayload")]
|
||||
async fn new_payload(&self, payload: CustomExecutionData) -> RpcResult<PayloadStatus>;
|
||||
|
||||
#[method(name = "forkchoiceUpdated")]
|
||||
async fn fork_choice_updated(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<CustomPayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated>;
|
||||
|
||||
#[method(name = "getPayload")]
|
||||
async fn get_payload(&self, payload_id: PayloadId)
|
||||
-> RpcResult<CustomExecutionPayloadEnvelope>;
|
||||
}
|
||||
|
||||
pub struct CustomEngineApi {
|
||||
inner: Arc<CustomEngineApiInner>,
|
||||
}
|
||||
|
||||
struct CustomEngineApiInner {
|
||||
beacon_consensus: ConsensusEngineHandle<CustomPayloadTypes>,
|
||||
payload_store: PayloadStore<CustomPayloadTypes>,
|
||||
}
|
||||
|
||||
impl CustomEngineApiInner {
|
||||
fn new(
|
||||
beacon_consensus: ConsensusEngineHandle<CustomPayloadTypes>,
|
||||
payload_store: PayloadStore<CustomPayloadTypes>,
|
||||
) -> Self {
|
||||
Self { beacon_consensus, payload_store }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CustomEngineApiServer for CustomEngineApi {
|
||||
async fn new_payload(&self, payload: CustomExecutionData) -> RpcResult<PayloadStatus> {
|
||||
Ok(self
|
||||
.inner
|
||||
.beacon_consensus
|
||||
.new_payload(payload)
|
||||
.await
|
||||
.map_err(EngineApiError::NewPayload)?)
|
||||
}
|
||||
|
||||
async fn fork_choice_updated(
|
||||
&self,
|
||||
fork_choice_state: ForkchoiceState,
|
||||
payload_attributes: Option<CustomPayloadAttributes>,
|
||||
) -> RpcResult<ForkchoiceUpdated> {
|
||||
Ok(self
|
||||
.inner
|
||||
.beacon_consensus
|
||||
.fork_choice_updated(fork_choice_state, payload_attributes, EngineApiMessageVersion::V3)
|
||||
.await
|
||||
.map_err(EngineApiError::ForkChoiceUpdate)?)
|
||||
}
|
||||
|
||||
async fn get_payload(
|
||||
&self,
|
||||
payload_id: PayloadId,
|
||||
) -> RpcResult<CustomExecutionPayloadEnvelope> {
|
||||
Ok(self
|
||||
.inner
|
||||
.payload_store
|
||||
.resolve(payload_id)
|
||||
.await
|
||||
.ok_or(EngineApiError::UnknownPayload)?
|
||||
.map_err(|_| EngineApiError::UnknownPayload)?
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoEngineApiRpcModule for CustomEngineApi
|
||||
where
|
||||
Self: CustomEngineApiServer,
|
||||
{
|
||||
fn into_rpc_module(self) -> RpcModule<()> {
|
||||
self.into_rpc().remove_context()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct CustomEngineApiBuilder {}
|
||||
|
||||
impl<N> EngineApiBuilder<N> for CustomEngineApiBuilder
|
||||
where
|
||||
N: FullNodeComponents<Types = CustomNode>,
|
||||
{
|
||||
type EngineApi = CustomEngineApi;
|
||||
|
||||
async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
|
||||
Ok(CustomEngineApi {
|
||||
inner: Arc::new(CustomEngineApiInner::new(
|
||||
ctx.beacon_engine_handle.clone(),
|
||||
PayloadStore::new(ctx.node.payload_builder_handle().clone()),
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
use crate::evm::{CustomTxEnv, PaymentTxEnv};
|
||||
use alloy_evm::{precompiles::PrecompilesMap, Database, Evm, EvmEnv, EvmFactory};
|
||||
use alloy_op_evm::{OpEvm, OpEvmFactory};
|
||||
use alloy_primitives::{Address, Bytes};
|
||||
use op_revm::{
|
||||
precompiles::OpPrecompiles, L1BlockInfo, OpContext, OpHaltReason, OpSpecId, OpTransaction,
|
||||
OpTransactionError,
|
||||
};
|
||||
use reth_ethereum::evm::revm::{
|
||||
context::{result::ResultAndState, BlockEnv, CfgEnv},
|
||||
handler::PrecompileProvider,
|
||||
interpreter::InterpreterResult,
|
||||
Context, Inspector, Journal,
|
||||
};
|
||||
use revm::{context_interface::result::EVMError, inspector::NoOpInspector};
|
||||
use std::error::Error;
|
||||
|
||||
/// EVM context contains data that EVM needs for execution of [`CustomTxEnv`].
|
||||
pub type CustomContext<DB> =
|
||||
Context<BlockEnv, OpTransaction<PaymentTxEnv>, CfgEnv<OpSpecId>, DB, Journal<DB>, L1BlockInfo>;
|
||||
|
||||
pub struct CustomEvm<DB: Database, I, P = OpPrecompiles> {
|
||||
inner: OpEvm<DB, I, P>,
|
||||
}
|
||||
|
||||
impl<DB: Database, I, P> CustomEvm<DB, I, P> {
|
||||
pub fn new(op: OpEvm<DB, I, P>) -> Self {
|
||||
Self { inner: op }
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, I, P> Evm for CustomEvm<DB, I, P>
|
||||
where
|
||||
DB: Database,
|
||||
I: Inspector<OpContext<DB>>,
|
||||
P: PrecompileProvider<OpContext<DB>, Output = InterpreterResult>,
|
||||
{
|
||||
type DB = DB;
|
||||
type Tx = CustomTxEnv;
|
||||
type Error = EVMError<DB::Error, OpTransactionError>;
|
||||
type HaltReason = OpHaltReason;
|
||||
type Spec = OpSpecId;
|
||||
type BlockEnv = BlockEnv;
|
||||
type Precompiles = P;
|
||||
type Inspector = I;
|
||||
|
||||
fn block(&self) -> &BlockEnv {
|
||||
self.inner.block()
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> u64 {
|
||||
self.inner.chain_id()
|
||||
}
|
||||
|
||||
fn transact_raw(
|
||||
&mut self,
|
||||
tx: Self::Tx,
|
||||
) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
|
||||
match tx {
|
||||
CustomTxEnv::Op(tx) => self.inner.transact_raw(tx),
|
||||
CustomTxEnv::Payment(..) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn transact_system_call(
|
||||
&mut self,
|
||||
caller: Address,
|
||||
contract: Address,
|
||||
data: Bytes,
|
||||
) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
|
||||
self.inner.transact_system_call(caller, contract, data)
|
||||
}
|
||||
|
||||
fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
|
||||
self.inner.finish()
|
||||
}
|
||||
|
||||
fn set_inspector_enabled(&mut self, enabled: bool) {
|
||||
self.inner.set_inspector_enabled(enabled)
|
||||
}
|
||||
|
||||
fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
|
||||
self.inner.components()
|
||||
}
|
||||
|
||||
fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
|
||||
self.inner.components_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub struct CustomEvmFactory(pub OpEvmFactory);
|
||||
|
||||
impl CustomEvmFactory {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl EvmFactory for CustomEvmFactory {
|
||||
type Evm<DB: Database, I: Inspector<OpContext<DB>>> = CustomEvm<DB, I, Self::Precompiles>;
|
||||
type Context<DB: Database> = OpContext<DB>;
|
||||
type Tx = CustomTxEnv;
|
||||
type Error<DBError: Error + Send + Sync + 'static> = EVMError<DBError, OpTransactionError>;
|
||||
type HaltReason = OpHaltReason;
|
||||
type Spec = OpSpecId;
|
||||
type BlockEnv = BlockEnv;
|
||||
type Precompiles = PrecompilesMap;
|
||||
|
||||
fn create_evm<DB: Database>(
|
||||
&self,
|
||||
db: DB,
|
||||
input: EvmEnv<Self::Spec>,
|
||||
) -> Self::Evm<DB, NoOpInspector> {
|
||||
CustomEvm::new(self.0.create_evm(db, input))
|
||||
}
|
||||
|
||||
fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
|
||||
&self,
|
||||
db: DB,
|
||||
input: EvmEnv<Self::Spec>,
|
||||
inspector: I,
|
||||
) -> Self::Evm<DB, I> {
|
||||
CustomEvm::new(self.0.create_evm_with_inspector(db, input, inspector))
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
use crate::{
|
||||
chainspec::CustomChainSpec,
|
||||
evm::executor::CustomBlockExecutionCtx,
|
||||
primitives::{Block, CustomHeader, CustomTransaction},
|
||||
};
|
||||
use alloy_evm::block::{BlockExecutionError, BlockExecutorFactory};
|
||||
use reth_ethereum::{
|
||||
evm::primitives::execute::{BlockAssembler, BlockAssemblerInput},
|
||||
primitives::Receipt,
|
||||
};
|
||||
use reth_op::{node::OpBlockAssembler, DepositReceipt};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CustomBlockAssembler {
|
||||
block_assembler: OpBlockAssembler<CustomChainSpec>,
|
||||
}
|
||||
|
||||
impl CustomBlockAssembler {
|
||||
pub const fn new(chain_spec: Arc<CustomChainSpec>) -> Self {
|
||||
Self { block_assembler: OpBlockAssembler::new(chain_spec) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> BlockAssembler<F> for CustomBlockAssembler
|
||||
where
|
||||
F: for<'a> BlockExecutorFactory<
|
||||
ExecutionCtx<'a> = CustomBlockExecutionCtx,
|
||||
Transaction = CustomTransaction,
|
||||
Receipt: Receipt + DepositReceipt,
|
||||
>,
|
||||
{
|
||||
type Block = Block;
|
||||
|
||||
fn assemble_block(
|
||||
&self,
|
||||
input: BlockAssemblerInput<'_, '_, F, CustomHeader>,
|
||||
) -> Result<Self::Block, BlockExecutionError> {
|
||||
Ok(self.block_assembler.assemble_block(input)?.map_header(From::from))
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
use crate::{chainspec::CustomChainSpec, evm::CustomEvmConfig, primitives::CustomNodePrimitives};
|
||||
use reth_ethereum::node::api::FullNodeTypes;
|
||||
use reth_node_builder::{components::ExecutorBuilder, BuilderContext, NodeTypes};
|
||||
use std::{future, future::Future};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct CustomExecutorBuilder;
|
||||
|
||||
impl<Node: FullNodeTypes> ExecutorBuilder<Node> for CustomExecutorBuilder
|
||||
where
|
||||
Node::Types: NodeTypes<ChainSpec = CustomChainSpec, Primitives = CustomNodePrimitives>,
|
||||
{
|
||||
type EVM = CustomEvmConfig;
|
||||
|
||||
fn build_evm(
|
||||
self,
|
||||
ctx: &BuilderContext<Node>,
|
||||
) -> impl Future<Output = eyre::Result<Self::EVM>> + Send {
|
||||
future::ready(Ok(CustomEvmConfig::new(ctx.chain_spec())))
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
use crate::{
|
||||
chainspec::CustomChainSpec,
|
||||
engine::{CustomExecutionData, CustomPayloadBuilderAttributes},
|
||||
evm::{alloy::CustomEvmFactory, executor::CustomBlockExecutionCtx, CustomBlockAssembler},
|
||||
primitives::{Block, CustomHeader, CustomNodePrimitives, CustomTransaction},
|
||||
};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::{eip2718::WithEncoded, Decodable2718};
|
||||
use alloy_evm::EvmEnv;
|
||||
use alloy_op_evm::OpBlockExecutionCtx;
|
||||
use alloy_rpc_types_engine::PayloadError;
|
||||
use op_alloy_rpc_types_engine::flashblock::OpFlashblockPayloadBase;
|
||||
use op_revm::OpSpecId;
|
||||
use reth_engine_primitives::ExecutableTxIterator;
|
||||
use reth_ethereum::{
|
||||
chainspec::EthChainSpec,
|
||||
node::api::{BuildNextEnv, ConfigureEvm, PayloadBuilderError},
|
||||
primitives::{SealedBlock, SealedHeader},
|
||||
};
|
||||
use reth_node_builder::{ConfigureEngineEvm, NewPayloadError};
|
||||
use reth_op::{
|
||||
chainspec::OpHardforks,
|
||||
evm::primitives::{EvmEnvFor, ExecutionCtxFor},
|
||||
node::{OpEvmConfig, OpNextBlockEnvAttributes, OpRethReceiptBuilder},
|
||||
primitives::SignedTransaction,
|
||||
};
|
||||
use reth_rpc_api::eth::helpers::pending_block::BuildPendingEnv;
|
||||
use revm_primitives::Bytes;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomEvmConfig {
|
||||
pub(super) inner: OpEvmConfig,
|
||||
pub(super) block_assembler: CustomBlockAssembler,
|
||||
pub(super) custom_evm_factory: CustomEvmFactory,
|
||||
}
|
||||
|
||||
impl CustomEvmConfig {
|
||||
pub fn new(chain_spec: Arc<CustomChainSpec>) -> Self {
|
||||
Self {
|
||||
inner: OpEvmConfig::new(
|
||||
Arc::new(chain_spec.inner().clone()),
|
||||
OpRethReceiptBuilder::default(),
|
||||
),
|
||||
block_assembler: CustomBlockAssembler::new(chain_spec),
|
||||
custom_evm_factory: CustomEvmFactory::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigureEvm for CustomEvmConfig {
|
||||
type Primitives = CustomNodePrimitives;
|
||||
type Error = <OpEvmConfig as ConfigureEvm>::Error;
|
||||
type NextBlockEnvCtx = CustomNextBlockEnvAttributes;
|
||||
type BlockExecutorFactory = Self;
|
||||
type BlockAssembler = CustomBlockAssembler;
|
||||
|
||||
fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
|
||||
self
|
||||
}
|
||||
|
||||
fn block_assembler(&self) -> &Self::BlockAssembler {
|
||||
&self.block_assembler
|
||||
}
|
||||
|
||||
fn evm_env(&self, header: &CustomHeader) -> Result<EvmEnv<OpSpecId>, Self::Error> {
|
||||
self.inner.evm_env(header)
|
||||
}
|
||||
|
||||
fn next_evm_env(
|
||||
&self,
|
||||
parent: &CustomHeader,
|
||||
attributes: &CustomNextBlockEnvAttributes,
|
||||
) -> Result<EvmEnv<OpSpecId>, Self::Error> {
|
||||
self.inner.next_evm_env(parent, &attributes.inner)
|
||||
}
|
||||
|
||||
fn context_for_block(
|
||||
&self,
|
||||
block: &SealedBlock<Block>,
|
||||
) -> Result<CustomBlockExecutionCtx, Self::Error> {
|
||||
Ok(CustomBlockExecutionCtx {
|
||||
inner: OpBlockExecutionCtx {
|
||||
parent_hash: block.header().parent_hash(),
|
||||
parent_beacon_block_root: block.header().parent_beacon_block_root(),
|
||||
extra_data: block.header().extra_data().clone(),
|
||||
},
|
||||
extension: block.extension,
|
||||
})
|
||||
}
|
||||
|
||||
fn context_for_next_block(
|
||||
&self,
|
||||
parent: &SealedHeader<CustomHeader>,
|
||||
attributes: Self::NextBlockEnvCtx,
|
||||
) -> Result<CustomBlockExecutionCtx, Self::Error> {
|
||||
Ok(CustomBlockExecutionCtx {
|
||||
inner: OpBlockExecutionCtx {
|
||||
parent_hash: parent.hash(),
|
||||
parent_beacon_block_root: attributes.inner.parent_beacon_block_root,
|
||||
extra_data: attributes.inner.extra_data,
|
||||
},
|
||||
extension: attributes.extension,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigureEngineEvm<CustomExecutionData> for CustomEvmConfig {
|
||||
fn evm_env_for_payload(
|
||||
&self,
|
||||
payload: &CustomExecutionData,
|
||||
) -> Result<EvmEnvFor<Self>, Self::Error> {
|
||||
self.inner.evm_env_for_payload(&payload.inner)
|
||||
}
|
||||
|
||||
fn context_for_payload<'a>(
|
||||
&self,
|
||||
payload: &'a CustomExecutionData,
|
||||
) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
|
||||
Ok(CustomBlockExecutionCtx {
|
||||
inner: self.inner.context_for_payload(&payload.inner)?,
|
||||
extension: payload.extension,
|
||||
})
|
||||
}
|
||||
|
||||
fn tx_iterator_for_payload(
|
||||
&self,
|
||||
payload: &CustomExecutionData,
|
||||
) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
|
||||
let transactions = payload.inner.payload.transactions().clone();
|
||||
let convert = |encoded: Bytes| {
|
||||
let tx = CustomTransaction::decode_2718_exact(encoded.as_ref())
|
||||
.map_err(Into::into)
|
||||
.map_err(PayloadError::Decode)?;
|
||||
let signer = tx.try_recover().map_err(NewPayloadError::other)?;
|
||||
Ok::<_, NewPayloadError>(WithEncoded::new(encoded, tx.with_signer(signer)))
|
||||
};
|
||||
Ok((transactions, convert))
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional parameters required for executing next block of custom transactions.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomNextBlockEnvAttributes {
|
||||
inner: OpNextBlockEnvAttributes,
|
||||
extension: u64,
|
||||
}
|
||||
|
||||
impl From<OpFlashblockPayloadBase> for CustomNextBlockEnvAttributes {
|
||||
fn from(value: OpFlashblockPayloadBase) -> Self {
|
||||
Self { inner: value.into(), extension: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildPendingEnv<CustomHeader> for CustomNextBlockEnvAttributes {
|
||||
fn build_pending_env(parent: &SealedHeader<CustomHeader>) -> Self {
|
||||
Self {
|
||||
inner: OpNextBlockEnvAttributes::build_pending_env(parent),
|
||||
extension: parent.extension,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, ChainSpec> BuildNextEnv<CustomPayloadBuilderAttributes, H, ChainSpec>
|
||||
for CustomNextBlockEnvAttributes
|
||||
where
|
||||
H: BlockHeader,
|
||||
ChainSpec: EthChainSpec + OpHardforks,
|
||||
{
|
||||
fn build_next_env(
|
||||
attributes: &CustomPayloadBuilderAttributes,
|
||||
parent: &SealedHeader<H>,
|
||||
chain_spec: &ChainSpec,
|
||||
) -> Result<Self, PayloadBuilderError> {
|
||||
let inner =
|
||||
OpNextBlockEnvAttributes::build_next_env(&attributes.inner, parent, chain_spec)?;
|
||||
|
||||
Ok(CustomNextBlockEnvAttributes { inner, extension: attributes.extension })
|
||||
}
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
use crate::primitives::{CustomTransaction, TxPayment};
|
||||
use alloy_eips::{eip2930::AccessList, Typed2718};
|
||||
use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv};
|
||||
use alloy_op_evm::block::OpTxEnv;
|
||||
use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
|
||||
use op_alloy_consensus::OpTxEnvelope;
|
||||
use op_revm::OpTransaction;
|
||||
use reth_ethereum::evm::{primitives::TransactionEnv, revm::context::TxEnv};
|
||||
|
||||
/// An Optimism transaction extended by [`PaymentTxEnv`] that can be fed to [`Evm`].
|
||||
///
|
||||
/// [`Evm`]: alloy_evm::Evm
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CustomTxEnv {
|
||||
Op(OpTransaction<TxEnv>),
|
||||
Payment(PaymentTxEnv),
|
||||
}
|
||||
|
||||
/// A transaction environment is a set of information related to an Ethereum transaction that can be
|
||||
/// fed to [`Evm`] for execution.
|
||||
///
|
||||
/// [`Evm`]: alloy_evm::Evm
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PaymentTxEnv(pub TxEnv);
|
||||
|
||||
impl revm::context::Transaction for CustomTxEnv {
|
||||
type AccessListItem<'a>
|
||||
= <TxEnv as revm::context::Transaction>::AccessListItem<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
type Authorization<'a>
|
||||
= <TxEnv as revm::context::Transaction>::Authorization<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn tx_type(&self) -> u8 {
|
||||
match self {
|
||||
Self::Op(tx) => tx.tx_type(),
|
||||
Self::Payment(tx) => tx.tx_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn caller(&self) -> Address {
|
||||
match self {
|
||||
Self::Op(tx) => tx.caller(),
|
||||
Self::Payment(tx) => tx.caller(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
match self {
|
||||
Self::Op(tx) => tx.gas_limit(),
|
||||
Self::Payment(tx) => tx.gas_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&self) -> U256 {
|
||||
match self {
|
||||
Self::Op(tx) => tx.value(),
|
||||
Self::Payment(tx) => tx.value(),
|
||||
}
|
||||
}
|
||||
|
||||
fn input(&self) -> &Bytes {
|
||||
match self {
|
||||
Self::Op(tx) => tx.input(),
|
||||
Self::Payment(tx) => tx.input(),
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
match self {
|
||||
Self::Op(tx) => revm::context::Transaction::nonce(tx),
|
||||
Self::Payment(tx) => revm::context::Transaction::nonce(tx),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> TxKind {
|
||||
match self {
|
||||
Self::Op(tx) => tx.kind(),
|
||||
Self::Payment(tx) => tx.kind(),
|
||||
}
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> Option<u64> {
|
||||
match self {
|
||||
Self::Op(tx) => tx.chain_id(),
|
||||
Self::Payment(tx) => tx.chain_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_price(&self) -> u128 {
|
||||
match self {
|
||||
Self::Op(tx) => tx.gas_price(),
|
||||
Self::Payment(tx) => tx.gas_price(),
|
||||
}
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
|
||||
Some(match self {
|
||||
Self::Op(tx) => tx.base.access_list.iter(),
|
||||
Self::Payment(tx) => tx.0.access_list.iter(),
|
||||
})
|
||||
}
|
||||
|
||||
fn blob_versioned_hashes(&self) -> &[B256] {
|
||||
match self {
|
||||
Self::Op(tx) => tx.blob_versioned_hashes(),
|
||||
Self::Payment(tx) => tx.blob_versioned_hashes(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> u128 {
|
||||
match self {
|
||||
Self::Op(tx) => tx.max_fee_per_blob_gas(),
|
||||
Self::Payment(tx) => tx.max_fee_per_blob_gas(),
|
||||
}
|
||||
}
|
||||
|
||||
fn authorization_list_len(&self) -> usize {
|
||||
match self {
|
||||
Self::Op(tx) => tx.authorization_list_len(),
|
||||
Self::Payment(tx) => tx.authorization_list_len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
|
||||
match self {
|
||||
Self::Op(tx) => tx.base.authorization_list.iter(),
|
||||
Self::Payment(tx) => tx.0.authorization_list.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
match self {
|
||||
Self::Op(tx) => tx.max_priority_fee_per_gas(),
|
||||
Self::Payment(tx) => tx.max_priority_fee_per_gas(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl revm::context::Transaction for PaymentTxEnv {
|
||||
type AccessListItem<'a>
|
||||
= <TxEnv as revm::context::Transaction>::AccessListItem<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
type Authorization<'a>
|
||||
= <TxEnv as revm::context::Transaction>::Authorization<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn tx_type(&self) -> u8 {
|
||||
self.0.tx_type()
|
||||
}
|
||||
|
||||
fn caller(&self) -> Address {
|
||||
self.0.caller()
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.0.gas_limit()
|
||||
}
|
||||
|
||||
fn value(&self) -> U256 {
|
||||
self.0.value()
|
||||
}
|
||||
|
||||
fn input(&self) -> &Bytes {
|
||||
self.0.input()
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
revm::context::Transaction::nonce(&self.0)
|
||||
}
|
||||
|
||||
fn kind(&self) -> TxKind {
|
||||
self.0.kind()
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> Option<u64> {
|
||||
self.0.chain_id()
|
||||
}
|
||||
|
||||
fn gas_price(&self) -> u128 {
|
||||
self.0.gas_price()
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
|
||||
self.0.access_list()
|
||||
}
|
||||
|
||||
fn blob_versioned_hashes(&self) -> &[B256] {
|
||||
self.0.blob_versioned_hashes()
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> u128 {
|
||||
self.0.max_fee_per_blob_gas()
|
||||
}
|
||||
|
||||
fn authorization_list_len(&self) -> usize {
|
||||
self.0.authorization_list_len()
|
||||
}
|
||||
|
||||
fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
|
||||
self.0.authorization_list()
|
||||
}
|
||||
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
self.0.max_priority_fee_per_gas()
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionEnv for PaymentTxEnv {
|
||||
fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||
self.0.set_gas_limit(gas_limit);
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
self.0.nonce()
|
||||
}
|
||||
|
||||
fn set_nonce(&mut self, nonce: u64) {
|
||||
self.0.set_nonce(nonce);
|
||||
}
|
||||
|
||||
fn set_access_list(&mut self, access_list: AccessList) {
|
||||
self.0.set_access_list(access_list);
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionEnv for CustomTxEnv {
|
||||
fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||
match self {
|
||||
Self::Op(tx) => tx.set_gas_limit(gas_limit),
|
||||
Self::Payment(tx) => tx.set_gas_limit(gas_limit),
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
match self {
|
||||
Self::Op(tx) => tx.nonce(),
|
||||
Self::Payment(tx) => tx.nonce(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_nonce(&mut self, nonce: u64) {
|
||||
match self {
|
||||
Self::Op(tx) => tx.set_nonce(nonce),
|
||||
Self::Payment(tx) => tx.set_nonce(nonce),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_access_list(&mut self, access_list: AccessList) {
|
||||
match self {
|
||||
Self::Op(tx) => tx.set_access_list(access_list),
|
||||
Self::Payment(tx) => tx.set_access_list(access_list),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRecoveredTx<TxPayment> for TxEnv {
|
||||
fn from_recovered_tx(tx: &TxPayment, caller: Address) -> Self {
|
||||
let TxPayment {
|
||||
chain_id,
|
||||
nonce,
|
||||
gas_limit,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
to,
|
||||
value,
|
||||
} = tx;
|
||||
Self {
|
||||
tx_type: tx.ty(),
|
||||
caller,
|
||||
gas_limit: *gas_limit,
|
||||
gas_price: *max_fee_per_gas,
|
||||
gas_priority_fee: Some(*max_priority_fee_per_gas),
|
||||
kind: TxKind::Call(*to),
|
||||
value: *value,
|
||||
nonce: *nonce,
|
||||
chain_id: Some(*chain_id),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTxWithEncoded<TxPayment> for TxEnv {
|
||||
fn from_encoded_tx(tx: &TxPayment, sender: Address, _encoded: Bytes) -> Self {
|
||||
Self::from_recovered_tx(tx, sender)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRecoveredTx<OpTxEnvelope> for CustomTxEnv {
|
||||
fn from_recovered_tx(tx: &OpTxEnvelope, sender: Address) -> Self {
|
||||
Self::Op(OpTransaction::from_recovered_tx(tx, sender))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTxWithEncoded<OpTxEnvelope> for CustomTxEnv {
|
||||
fn from_encoded_tx(tx: &OpTxEnvelope, sender: Address, encoded: Bytes) -> Self {
|
||||
Self::Op(OpTransaction::from_encoded_tx(tx, sender, encoded))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRecoveredTx<CustomTransaction> for CustomTxEnv {
|
||||
fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self {
|
||||
match tx {
|
||||
CustomTransaction::Op(tx) => Self::from_recovered_tx(tx, sender),
|
||||
CustomTransaction::Payment(tx) => {
|
||||
Self::Payment(PaymentTxEnv(TxEnv::from_recovered_tx(tx.tx(), sender)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTxWithEncoded<CustomTransaction> for CustomTxEnv {
|
||||
fn from_encoded_tx(tx: &CustomTransaction, sender: Address, encoded: Bytes) -> Self {
|
||||
match tx {
|
||||
CustomTransaction::Op(tx) => Self::from_encoded_tx(tx, sender, encoded),
|
||||
CustomTransaction::Payment(tx) => {
|
||||
Self::Payment(PaymentTxEnv(TxEnv::from_encoded_tx(tx.tx(), sender, encoded)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoTxEnv<Self> for CustomTxEnv {
|
||||
fn into_tx_env(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl OpTxEnv for CustomTxEnv {
|
||||
fn encoded_bytes(&self) -> Option<&Bytes> {
|
||||
match self {
|
||||
Self::Op(tx) => tx.encoded_bytes(),
|
||||
Self::Payment(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
use crate::{
|
||||
evm::{
|
||||
alloy::{CustomEvm, CustomEvmFactory},
|
||||
CustomEvmConfig, CustomTxEnv,
|
||||
},
|
||||
primitives::CustomTransaction,
|
||||
};
|
||||
use alloy_consensus::transaction::Recovered;
|
||||
use alloy_evm::{
|
||||
block::{
|
||||
BlockExecutionError, BlockExecutionResult, BlockExecutor, BlockExecutorFactory,
|
||||
BlockExecutorFor, ExecutableTx, OnStateHook,
|
||||
},
|
||||
precompiles::PrecompilesMap,
|
||||
Database, Evm, RecoveredTx,
|
||||
};
|
||||
use alloy_op_evm::{block::OpTxResult, OpBlockExecutionCtx, OpBlockExecutor};
|
||||
use reth_ethereum::evm::primitives::InspectorFor;
|
||||
use reth_op::{chainspec::OpChainSpec, node::OpRethReceiptBuilder, OpReceipt, OpTxType};
|
||||
use revm::database::State;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct CustomBlockExecutor<Evm> {
|
||||
inner: OpBlockExecutor<Evm, OpRethReceiptBuilder, Arc<OpChainSpec>>,
|
||||
}
|
||||
|
||||
impl<'db, DB, E> BlockExecutor for CustomBlockExecutor<E>
|
||||
where
|
||||
DB: Database + 'db,
|
||||
E: Evm<DB = &'db mut State<DB>, Tx = CustomTxEnv>,
|
||||
{
|
||||
type Transaction = CustomTransaction;
|
||||
type Receipt = OpReceipt;
|
||||
type Evm = E;
|
||||
type Result = OpTxResult<E::HaltReason, OpTxType>;
|
||||
|
||||
fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
|
||||
self.inner.apply_pre_execution_changes()
|
||||
}
|
||||
|
||||
fn receipts(&self) -> &[Self::Receipt] {
|
||||
self.inner.receipts()
|
||||
}
|
||||
|
||||
fn execute_transaction_without_commit(
|
||||
&mut self,
|
||||
tx: impl ExecutableTx<Self>,
|
||||
) -> Result<Self::Result, BlockExecutionError> {
|
||||
let tx = tx.into_parts().1;
|
||||
match tx.tx() {
|
||||
CustomTransaction::Op(op_tx) => self
|
||||
.inner
|
||||
.execute_transaction_without_commit(Recovered::new_unchecked(op_tx, *tx.signer())),
|
||||
CustomTransaction::Payment(..) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_transaction(&mut self, output: Self::Result) -> Result<u64, BlockExecutionError> {
|
||||
self.inner.commit_transaction(output)
|
||||
}
|
||||
|
||||
fn finish(self) -> Result<(Self::Evm, BlockExecutionResult<OpReceipt>), BlockExecutionError> {
|
||||
self.inner.finish()
|
||||
}
|
||||
|
||||
fn set_state_hook(&mut self, _hook: Option<Box<dyn OnStateHook>>) {
|
||||
self.inner.set_state_hook(_hook)
|
||||
}
|
||||
|
||||
fn evm_mut(&mut self) -> &mut Self::Evm {
|
||||
self.inner.evm_mut()
|
||||
}
|
||||
|
||||
fn evm(&self) -> &Self::Evm {
|
||||
self.inner.evm()
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockExecutorFactory for CustomEvmConfig {
|
||||
type EvmFactory = CustomEvmFactory;
|
||||
type ExecutionCtx<'a> = CustomBlockExecutionCtx;
|
||||
type Transaction = CustomTransaction;
|
||||
type Receipt = OpReceipt;
|
||||
|
||||
fn evm_factory(&self) -> &Self::EvmFactory {
|
||||
&self.custom_evm_factory
|
||||
}
|
||||
|
||||
fn create_executor<'a, DB, I>(
|
||||
&'a self,
|
||||
evm: CustomEvm<&'a mut State<DB>, I, PrecompilesMap>,
|
||||
ctx: CustomBlockExecutionCtx,
|
||||
) -> impl BlockExecutorFor<'a, Self, DB, I>
|
||||
where
|
||||
DB: Database + 'a,
|
||||
I: InspectorFor<Self, &'a mut State<DB>> + 'a,
|
||||
{
|
||||
CustomBlockExecutor {
|
||||
inner: OpBlockExecutor::new(
|
||||
evm,
|
||||
ctx.inner,
|
||||
self.inner.chain_spec().clone(),
|
||||
*self.inner.executor_factory.receipt_builder(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional parameters for executing custom transactions.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomBlockExecutionCtx {
|
||||
pub inner: OpBlockExecutionCtx,
|
||||
pub extension: u64,
|
||||
}
|
||||
|
||||
impl From<CustomBlockExecutionCtx> for OpBlockExecutionCtx {
|
||||
fn from(value: CustomBlockExecutionCtx) -> Self {
|
||||
value.inner
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
mod alloy;
|
||||
mod assembler;
|
||||
mod builder;
|
||||
mod config;
|
||||
mod env;
|
||||
mod executor;
|
||||
|
||||
pub use alloy::{CustomContext, CustomEvm};
|
||||
pub use assembler::CustomBlockAssembler;
|
||||
pub use builder::CustomExecutorBuilder;
|
||||
pub use config::CustomEvmConfig;
|
||||
pub use env::{CustomTxEnv, PaymentTxEnv};
|
||||
pub use executor::CustomBlockExecutor;
|
||||
@@ -1,86 +0,0 @@
|
||||
//! This example shows how to implement a custom node.
|
||||
//!
|
||||
//! A node consists of:
|
||||
//! - primitives: block,header,transactions
|
||||
//! - components: network,pool,evm
|
||||
//! - engine: advances the node
|
||||
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{
|
||||
engine::{CustomEngineValidatorBuilder, CustomPayloadTypes},
|
||||
engine_api::CustomEngineApiBuilder,
|
||||
evm::CustomExecutorBuilder,
|
||||
pool::CustomPooledTransaction,
|
||||
primitives::CustomTransaction,
|
||||
rpc::CustomRpcTypes,
|
||||
};
|
||||
use chainspec::CustomChainSpec;
|
||||
use primitives::CustomNodePrimitives;
|
||||
use reth_ethereum::node::api::{FullNodeTypes, NodeTypes};
|
||||
use reth_node_builder::{
|
||||
components::{BasicPayloadServiceBuilder, ComponentsBuilder},
|
||||
Node, NodeAdapter,
|
||||
};
|
||||
use reth_op::{
|
||||
node::{
|
||||
node::{OpConsensusBuilder, OpNetworkBuilder, OpPayloadBuilder, OpPoolBuilder},
|
||||
txpool, OpAddOns, OpNode,
|
||||
},
|
||||
rpc::OpEthApiBuilder,
|
||||
};
|
||||
|
||||
pub mod chainspec;
|
||||
pub mod engine;
|
||||
pub mod engine_api;
|
||||
pub mod evm;
|
||||
pub mod pool;
|
||||
pub mod primitives;
|
||||
pub mod rpc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomNode {
|
||||
inner: OpNode,
|
||||
}
|
||||
|
||||
impl NodeTypes for CustomNode {
|
||||
type Primitives = CustomNodePrimitives;
|
||||
type ChainSpec = CustomChainSpec;
|
||||
type Storage = <OpNode as NodeTypes>::Storage;
|
||||
type Payload = CustomPayloadTypes;
|
||||
}
|
||||
|
||||
impl<N> Node<N> for CustomNode
|
||||
where
|
||||
N: FullNodeTypes<Types = Self>,
|
||||
{
|
||||
type ComponentsBuilder = ComponentsBuilder<
|
||||
N,
|
||||
OpPoolBuilder<txpool::OpPooledTransaction<CustomTransaction, CustomPooledTransaction>>,
|
||||
BasicPayloadServiceBuilder<OpPayloadBuilder>,
|
||||
OpNetworkBuilder,
|
||||
CustomExecutorBuilder,
|
||||
OpConsensusBuilder,
|
||||
>;
|
||||
|
||||
type AddOns = OpAddOns<
|
||||
NodeAdapter<N>,
|
||||
OpEthApiBuilder<CustomRpcTypes>,
|
||||
CustomEngineValidatorBuilder,
|
||||
CustomEngineApiBuilder,
|
||||
>;
|
||||
|
||||
fn components_builder(&self) -> Self::ComponentsBuilder {
|
||||
ComponentsBuilder::default()
|
||||
.node_types::<N>()
|
||||
.pool(OpPoolBuilder::default())
|
||||
.executor(CustomExecutorBuilder::default())
|
||||
.payload(BasicPayloadServiceBuilder::new(OpPayloadBuilder::new(false)))
|
||||
.network(OpNetworkBuilder::new(false, false))
|
||||
.consensus(OpConsensusBuilder::default())
|
||||
}
|
||||
|
||||
fn add_ons(&self) -> Self::AddOns {
|
||||
self.inner.add_ons_builder().build()
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
use crate::primitives::{CustomTransaction, TxPayment};
|
||||
use alloy_consensus::{
|
||||
crypto::RecoveryError,
|
||||
error::ValueError,
|
||||
transaction::{SignerRecoverable, TxHashRef},
|
||||
Signed, TransactionEnvelope,
|
||||
};
|
||||
use alloy_primitives::{Address, Sealed, B256};
|
||||
use op_alloy_consensus::{OpPooledTransaction, OpTransaction, TxDeposit};
|
||||
use reth_ethereum::primitives::{
|
||||
serde_bincode_compat::RlpBincode, InMemorySize, SignedTransaction,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, TransactionEnvelope)]
|
||||
#[envelope(tx_type_name = CustomPooledTxType)]
|
||||
pub enum CustomPooledTransaction {
|
||||
/// A regular Optimism transaction as defined by [`OpPooledTransaction`].
|
||||
#[envelope(flatten)]
|
||||
Op(OpPooledTransaction),
|
||||
/// A [`TxPayment`] tagged with type 0x2A (decimal 42).
|
||||
#[envelope(ty = 42)]
|
||||
Payment(Signed<TxPayment>),
|
||||
}
|
||||
|
||||
impl From<CustomPooledTransaction> for CustomTransaction {
|
||||
fn from(tx: CustomPooledTransaction) -> Self {
|
||||
match tx {
|
||||
CustomPooledTransaction::Op(tx) => Self::Op(tx.into()),
|
||||
CustomPooledTransaction::Payment(tx) => Self::Payment(tx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<CustomTransaction> for CustomPooledTransaction {
|
||||
type Error = ValueError<CustomTransaction>;
|
||||
|
||||
fn try_from(tx: CustomTransaction) -> Result<Self, Self::Error> {
|
||||
match tx {
|
||||
CustomTransaction::Op(op) => Ok(Self::Op(
|
||||
OpPooledTransaction::try_from(op).map_err(|op| op.map(CustomTransaction::Op))?,
|
||||
)),
|
||||
CustomTransaction::Payment(payment) => Ok(Self::Payment(payment)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RlpBincode for CustomPooledTransaction {}
|
||||
|
||||
impl OpTransaction for CustomPooledTransaction {
|
||||
fn is_deposit(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn as_deposit(&self) -> Option<&Sealed<TxDeposit>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl SignerRecoverable for CustomPooledTransaction {
|
||||
fn recover_signer(&self) -> Result<Address, RecoveryError> {
|
||||
match self {
|
||||
CustomPooledTransaction::Op(tx) => SignerRecoverable::recover_signer(tx),
|
||||
CustomPooledTransaction::Payment(tx) => SignerRecoverable::recover_signer(tx),
|
||||
}
|
||||
}
|
||||
|
||||
fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
|
||||
match self {
|
||||
CustomPooledTransaction::Op(tx) => SignerRecoverable::recover_signer_unchecked(tx),
|
||||
CustomPooledTransaction::Payment(tx) => SignerRecoverable::recover_signer_unchecked(tx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TxHashRef for CustomPooledTransaction {
|
||||
fn tx_hash(&self) -> &B256 {
|
||||
match self {
|
||||
CustomPooledTransaction::Op(tx) => tx.tx_hash(),
|
||||
CustomPooledTransaction::Payment(tx) => tx.hash(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignedTransaction for CustomPooledTransaction {}
|
||||
|
||||
impl InMemorySize for CustomPooledTransaction {
|
||||
fn size(&self) -> usize {
|
||||
match self {
|
||||
CustomPooledTransaction::Op(tx) => InMemorySize::size(tx),
|
||||
CustomPooledTransaction::Payment(tx) => InMemorySize::size(tx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
use crate::primitives::{CustomHeader, CustomTransaction};
|
||||
|
||||
/// The Block type of this node
|
||||
pub type Block = alloy_consensus::Block<CustomTransaction, CustomHeader>;
|
||||
|
||||
/// The body type of this node
|
||||
pub type BlockBody = alloy_consensus::BlockBody<CustomTransaction, CustomHeader>;
|
||||
@@ -1,184 +0,0 @@
|
||||
use alloy_consensus::Header;
|
||||
use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, Sealable, B256, B64, U256};
|
||||
use alloy_rlp::{Encodable, RlpDecodable, RlpEncodable};
|
||||
use reth_codecs::Compact;
|
||||
use reth_ethereum::primitives::{serde_bincode_compat::RlpBincode, BlockHeader, InMemorySize};
|
||||
use revm_primitives::keccak256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The header type of this node
|
||||
///
|
||||
/// This type extends the regular ethereum header with an extension.
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
derive_more::AsRef,
|
||||
derive_more::Deref,
|
||||
Default,
|
||||
RlpEncodable,
|
||||
RlpDecodable,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CustomHeader {
|
||||
/// The regular eth header
|
||||
#[as_ref]
|
||||
#[deref]
|
||||
#[serde(flatten)]
|
||||
pub inner: Header,
|
||||
/// The extended header
|
||||
pub extension: u64,
|
||||
}
|
||||
|
||||
impl From<Header> for CustomHeader {
|
||||
fn from(value: Header) -> Self {
|
||||
CustomHeader { inner: value, extension: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Self> for CustomHeader {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealable for CustomHeader {
|
||||
fn hash_slow(&self) -> B256 {
|
||||
let mut out = Vec::new();
|
||||
self.encode(&mut out);
|
||||
keccak256(&out)
|
||||
}
|
||||
}
|
||||
|
||||
impl alloy_consensus::BlockHeader for CustomHeader {
|
||||
fn parent_hash(&self) -> B256 {
|
||||
self.inner.parent_hash()
|
||||
}
|
||||
|
||||
fn ommers_hash(&self) -> B256 {
|
||||
self.inner.ommers_hash()
|
||||
}
|
||||
|
||||
fn beneficiary(&self) -> Address {
|
||||
self.inner.beneficiary()
|
||||
}
|
||||
|
||||
fn state_root(&self) -> B256 {
|
||||
self.inner.state_root()
|
||||
}
|
||||
|
||||
fn transactions_root(&self) -> B256 {
|
||||
self.inner.transactions_root()
|
||||
}
|
||||
|
||||
fn receipts_root(&self) -> B256 {
|
||||
self.inner.receipts_root()
|
||||
}
|
||||
|
||||
fn withdrawals_root(&self) -> Option<B256> {
|
||||
self.inner.withdrawals_root()
|
||||
}
|
||||
|
||||
fn logs_bloom(&self) -> Bloom {
|
||||
self.inner.logs_bloom()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.inner.difficulty()
|
||||
}
|
||||
|
||||
fn number(&self) -> BlockNumber {
|
||||
self.inner.number()
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.inner.gas_limit()
|
||||
}
|
||||
|
||||
fn gas_used(&self) -> u64 {
|
||||
self.inner.gas_used()
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.inner.timestamp()
|
||||
}
|
||||
|
||||
fn mix_hash(&self) -> Option<B256> {
|
||||
self.inner.mix_hash()
|
||||
}
|
||||
|
||||
fn nonce(&self) -> Option<B64> {
|
||||
self.inner.nonce()
|
||||
}
|
||||
|
||||
fn base_fee_per_gas(&self) -> Option<u64> {
|
||||
self.inner.base_fee_per_gas()
|
||||
}
|
||||
|
||||
fn blob_gas_used(&self) -> Option<u64> {
|
||||
self.inner.blob_gas_used()
|
||||
}
|
||||
|
||||
fn excess_blob_gas(&self) -> Option<u64> {
|
||||
self.inner.excess_blob_gas()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.inner.parent_beacon_block_root()
|
||||
}
|
||||
|
||||
fn requests_hash(&self) -> Option<B256> {
|
||||
self.inner.requests_hash()
|
||||
}
|
||||
|
||||
fn extra_data(&self) -> &Bytes {
|
||||
self.inner.extra_data()
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for CustomHeader {
|
||||
fn size(&self) -> usize {
|
||||
self.inner.size() + self.extension.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl reth_codecs::Compact for CustomHeader {
|
||||
fn to_compact<B>(&self, buf: &mut B) -> usize
|
||||
where
|
||||
B: alloy_rlp::bytes::BufMut + AsMut<[u8]>,
|
||||
{
|
||||
let identifier = self.inner.to_compact(buf);
|
||||
self.extension.to_compact(buf);
|
||||
|
||||
identifier
|
||||
}
|
||||
|
||||
fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
let (eth_header, buf) = Compact::from_compact(buf, identifier);
|
||||
let (extension, buf) = Compact::from_compact(buf, buf.len());
|
||||
(Self { inner: eth_header, extension }, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl reth_db_api::table::Compress for CustomHeader {
|
||||
type Compressed = Vec<u8>;
|
||||
|
||||
fn compress_to_buf<B: alloy_primitives::bytes::BufMut + AsMut<[u8]>>(&self, buf: &mut B) {
|
||||
let _ = Compact::to_compact(self, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl reth_db_api::table::Decompress for CustomHeader {
|
||||
fn decompress(value: &[u8]) -> Result<Self, reth_db_api::DatabaseError> {
|
||||
let (obj, _) = Compact::from_compact(value, value.len());
|
||||
Ok(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHeader for CustomHeader {}
|
||||
|
||||
impl RlpBincode for CustomHeader {}
|
||||
@@ -1,27 +0,0 @@
|
||||
//! Contains the primitive types of this node.
|
||||
|
||||
pub mod header;
|
||||
pub use header::*;
|
||||
pub mod block;
|
||||
pub use block::*;
|
||||
pub mod tx;
|
||||
pub use tx::*;
|
||||
|
||||
pub mod tx_type;
|
||||
pub use tx_type::*;
|
||||
pub mod tx_custom;
|
||||
pub use tx_custom::*;
|
||||
|
||||
use reth_ethereum::primitives::NodePrimitives;
|
||||
use reth_op::OpReceipt;
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct CustomNodePrimitives;
|
||||
|
||||
impl NodePrimitives for CustomNodePrimitives {
|
||||
type Block = Block;
|
||||
type BlockHeader = CustomHeader;
|
||||
type BlockBody = BlockBody;
|
||||
type SignedTx = CustomTransaction;
|
||||
type Receipt = OpReceipt;
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
use super::TxPayment;
|
||||
use alloy_consensus::{
|
||||
crypto::RecoveryError,
|
||||
transaction::{SignerRecoverable, TxHashRef},
|
||||
Signed, TransactionEnvelope,
|
||||
};
|
||||
use alloy_eips::Encodable2718;
|
||||
use alloy_primitives::{Sealed, Signature, B256};
|
||||
use alloy_rlp::BufMut;
|
||||
use op_alloy_consensus::{OpTxEnvelope, TxDeposit};
|
||||
use reth_codecs::{
|
||||
alloy::transaction::{CompactEnvelope, FromTxCompact, ToTxCompact},
|
||||
Compact,
|
||||
};
|
||||
use reth_ethereum::primitives::{serde_bincode_compat::RlpBincode, InMemorySize};
|
||||
use reth_op::{primitives::SignedTransaction, OpTransaction};
|
||||
use revm_primitives::Address;
|
||||
|
||||
/// Either [`OpTxEnvelope`] or [`TxPayment`].
|
||||
#[derive(Debug, Clone, TransactionEnvelope)]
|
||||
#[envelope(tx_type_name = TxTypeCustom)]
|
||||
pub enum CustomTransaction {
|
||||
/// A regular Optimism transaction as defined by [`OpTxEnvelope`].
|
||||
#[envelope(flatten)]
|
||||
Op(OpTxEnvelope),
|
||||
/// A [`TxPayment`] tagged with type 0x2A (decimal 42).
|
||||
#[envelope(ty = 42)]
|
||||
Payment(Signed<TxPayment>),
|
||||
}
|
||||
|
||||
impl RlpBincode for CustomTransaction {}
|
||||
|
||||
impl reth_codecs::alloy::transaction::Envelope for CustomTransaction {
|
||||
fn signature(&self) -> &Signature {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => reth_codecs::alloy::transaction::Envelope::signature(tx),
|
||||
CustomTransaction::Payment(tx) => tx.signature(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_type(&self) -> Self::TxType {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => TxTypeCustom::Op(tx.tx_type()),
|
||||
CustomTransaction::Payment(_) => TxTypeCustom::Payment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTxCompact for CustomTransaction {
|
||||
type TxType = TxTypeCustom;
|
||||
|
||||
fn from_tx_compact(buf: &[u8], tx_type: Self::TxType, signature: Signature) -> (Self, &[u8])
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match tx_type {
|
||||
TxTypeCustom::Op(tx_type) => {
|
||||
let (tx, buf) = OpTxEnvelope::from_tx_compact(buf, tx_type, signature);
|
||||
(Self::Op(tx), buf)
|
||||
}
|
||||
TxTypeCustom::Payment => {
|
||||
let (tx, buf) = TxPayment::from_compact(buf, buf.len());
|
||||
let tx = Signed::new_unhashed(tx, signature);
|
||||
(Self::Payment(tx), buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTxCompact for CustomTransaction {
|
||||
fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>)) {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => tx.to_tx_compact(buf),
|
||||
CustomTransaction::Payment(tx) => {
|
||||
tx.tx().to_compact(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compact for CustomTransaction {
|
||||
fn to_compact<B>(&self, buf: &mut B) -> usize
|
||||
where
|
||||
B: BufMut + AsMut<[u8]>,
|
||||
{
|
||||
<Self as CompactEnvelope>::to_compact(self, buf)
|
||||
}
|
||||
|
||||
fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
|
||||
<Self as CompactEnvelope>::from_compact(buf, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl OpTransaction for CustomTransaction {
|
||||
fn is_deposit(&self) -> bool {
|
||||
match self {
|
||||
CustomTransaction::Op(op) => op.is_deposit(),
|
||||
CustomTransaction::Payment(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_deposit(&self) -> Option<&Sealed<TxDeposit>> {
|
||||
match self {
|
||||
CustomTransaction::Op(op) => op.as_deposit(),
|
||||
CustomTransaction::Payment(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignerRecoverable for CustomTransaction {
|
||||
fn recover_signer(&self) -> Result<Address, RecoveryError> {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => SignerRecoverable::recover_signer(tx),
|
||||
CustomTransaction::Payment(tx) => SignerRecoverable::recover_signer(tx),
|
||||
}
|
||||
}
|
||||
|
||||
fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => SignerRecoverable::recover_signer_unchecked(tx),
|
||||
CustomTransaction::Payment(tx) => SignerRecoverable::recover_signer_unchecked(tx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TxHashRef for CustomTransaction {
|
||||
fn tx_hash(&self) -> &B256 {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => TxHashRef::tx_hash(tx),
|
||||
CustomTransaction::Payment(tx) => tx.hash(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignedTransaction for CustomTransaction {}
|
||||
|
||||
impl InMemorySize for CustomTransaction {
|
||||
fn size(&self) -> usize {
|
||||
match self {
|
||||
CustomTransaction::Op(tx) => InMemorySize::size(tx),
|
||||
CustomTransaction::Payment(tx) => InMemorySize::size(tx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
use crate::primitives::PAYMENT_TX_TYPE_ID;
|
||||
use alloy_consensus::{
|
||||
transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx},
|
||||
SignableTransaction, Transaction,
|
||||
};
|
||||
use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization, Typed2718};
|
||||
use alloy_primitives::{Address, Bytes, ChainId, Signature, TxKind, B256, U256};
|
||||
use alloy_rlp::{BufMut, Decodable, Encodable};
|
||||
use reth_ethereum::primitives::{serde_bincode_compat::RlpBincode, InMemorySize};
|
||||
|
||||
/// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)).
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
reth_codecs::Compact,
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[doc(alias = "PaymentTransaction", alias = "TransactionPayment", alias = "PaymentTx")]
|
||||
pub struct TxPayment {
|
||||
/// EIP-155: Simple replay attack protection
|
||||
#[serde(with = "alloy_serde::quantity")]
|
||||
pub chain_id: ChainId,
|
||||
/// A scalar value equal to the number of transactions sent by the sender; formally Tn.
|
||||
#[serde(with = "alloy_serde::quantity")]
|
||||
pub nonce: u64,
|
||||
/// A scalar value equal to the maximum
|
||||
/// amount of gas that should be used in executing
|
||||
/// this transaction. This is paid up-front, before any
|
||||
/// computation is done and may not be increased
|
||||
/// later; formally Tg.
|
||||
#[serde(with = "alloy_serde::quantity", rename = "gas", alias = "gasLimit")]
|
||||
pub gas_limit: u64,
|
||||
/// A scalar value equal to the maximum
|
||||
/// amount of gas that should be used in executing
|
||||
/// this transaction. This is paid up-front, before any
|
||||
/// computation is done and may not be increased
|
||||
/// later; formally Tg.
|
||||
///
|
||||
/// As ethereum circulation is around 120mil eth as of 2022 that is around
|
||||
/// 120000000000000000000000000 wei we are safe to use u128 as its max number is:
|
||||
/// 340282366920938463463374607431768211455
|
||||
///
|
||||
/// This is also known as `GasFeeCap`
|
||||
#[serde(with = "alloy_serde::quantity")]
|
||||
pub max_fee_per_gas: u128,
|
||||
/// Max Priority fee that transaction is paying
|
||||
///
|
||||
/// As ethereum circulation is around 120mil eth as of 2022 that is around
|
||||
/// 120000000000000000000000000 wei we are safe to use u128 as its max number is:
|
||||
/// 340282366920938463463374607431768211455
|
||||
///
|
||||
/// This is also known as `GasTipCap`
|
||||
#[serde(with = "alloy_serde::quantity")]
|
||||
pub max_priority_fee_per_gas: u128,
|
||||
/// The 160-bit address of the message call’s recipient.
|
||||
pub to: Address,
|
||||
/// A scalar value equal to the number of Wei to
|
||||
/// be transferred to the message call’s recipient or,
|
||||
/// in the case of contract creation, as an endowment
|
||||
/// to the newly created account; formally Tv.
|
||||
pub value: U256,
|
||||
}
|
||||
|
||||
impl TxPayment {
|
||||
/// Get the transaction type
|
||||
#[doc(alias = "transaction_type")]
|
||||
pub const fn tx_type() -> super::tx::TxTypeCustom {
|
||||
super::tx::TxTypeCustom::Payment
|
||||
}
|
||||
|
||||
/// Calculates a heuristic for the in-memory size of the [TxPayment] transaction.
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
size_of::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl RlpEcdsaEncodableTx for TxPayment {
|
||||
/// Outputs the length of the transaction's fields, without a RLP header.
|
||||
fn rlp_encoded_fields_length(&self) -> usize {
|
||||
self.chain_id.length() +
|
||||
self.nonce.length() +
|
||||
self.max_priority_fee_per_gas.length() +
|
||||
self.max_fee_per_gas.length() +
|
||||
self.gas_limit.length() +
|
||||
self.to.length() +
|
||||
self.value.length()
|
||||
}
|
||||
|
||||
/// Encodes only the transaction's fields into the desired buffer, without
|
||||
/// a RLP header.
|
||||
fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) {
|
||||
self.chain_id.encode(out);
|
||||
self.nonce.encode(out);
|
||||
self.max_priority_fee_per_gas.encode(out);
|
||||
self.max_fee_per_gas.encode(out);
|
||||
self.gas_limit.encode(out);
|
||||
self.to.encode(out);
|
||||
self.value.encode(out);
|
||||
}
|
||||
}
|
||||
|
||||
impl RlpEcdsaDecodableTx for TxPayment {
|
||||
const DEFAULT_TX_TYPE: u8 = { PAYMENT_TX_TYPE_ID };
|
||||
|
||||
/// Decodes the inner [TxPayment] fields from RLP bytes.
|
||||
///
|
||||
/// NOTE: This assumes a RLP header has already been decoded, and _just_
|
||||
/// decodes the following RLP fields in the following order:
|
||||
///
|
||||
/// - `chain_id`
|
||||
/// - `nonce`
|
||||
/// - `max_priority_fee_per_gas`
|
||||
/// - `max_fee_per_gas`
|
||||
/// - `gas_limit`
|
||||
/// - `to`
|
||||
/// - `value`
|
||||
/// - `data` (`input`)
|
||||
/// - `access_list`
|
||||
fn rlp_decode_fields(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||
Ok(Self {
|
||||
chain_id: Decodable::decode(buf)?,
|
||||
nonce: Decodable::decode(buf)?,
|
||||
max_priority_fee_per_gas: Decodable::decode(buf)?,
|
||||
max_fee_per_gas: Decodable::decode(buf)?,
|
||||
gas_limit: Decodable::decode(buf)?,
|
||||
to: Decodable::decode(buf)?,
|
||||
value: Decodable::decode(buf)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Transaction for TxPayment {
|
||||
#[inline]
|
||||
fn chain_id(&self) -> Option<ChainId> {
|
||||
Some(self.chain_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nonce(&self) -> u64 {
|
||||
self.nonce
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.gas_limit
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gas_price(&self) -> Option<u128> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_fee_per_gas(&self) -> u128 {
|
||||
self.max_fee_per_gas
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
Some(self.max_priority_fee_per_gas)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_fee_per_blob_gas(&self) -> Option<u128> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn priority_fee_or_price(&self) -> u128 {
|
||||
self.max_priority_fee_per_gas
|
||||
}
|
||||
|
||||
fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
|
||||
base_fee.map_or(self.max_fee_per_gas, |base_fee| {
|
||||
// if the tip is greater than the max priority fee per gas, set it to the max
|
||||
// priority fee per gas + base fee
|
||||
let tip = self.max_fee_per_gas.saturating_sub(base_fee as u128);
|
||||
if tip > self.max_priority_fee_per_gas {
|
||||
self.max_priority_fee_per_gas + base_fee as u128
|
||||
} else {
|
||||
// otherwise return the max fee per gas
|
||||
self.max_fee_per_gas
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_dynamic_fee(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn kind(&self) -> TxKind {
|
||||
TxKind::Call(self.to)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_create(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value(&self) -> U256 {
|
||||
self.value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn input(&self) -> &Bytes {
|
||||
// No input data
|
||||
static EMPTY_BYTES: Bytes = Bytes::new();
|
||||
&EMPTY_BYTES
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn access_list(&self) -> Option<&AccessList> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn blob_versioned_hashes(&self) -> Option<&[B256]> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed2718 for TxPayment {
|
||||
fn ty(&self) -> u8 {
|
||||
PAYMENT_TX_TYPE_ID
|
||||
}
|
||||
}
|
||||
|
||||
impl SignableTransaction<Signature> for TxPayment {
|
||||
fn set_chain_id(&mut self, chain_id: ChainId) {
|
||||
self.chain_id = chain_id;
|
||||
}
|
||||
|
||||
fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) {
|
||||
out.put_u8(Self::tx_type().ty());
|
||||
self.encode(out)
|
||||
}
|
||||
|
||||
fn payload_len_for_signature(&self) -> usize {
|
||||
self.length() + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for TxPayment {
|
||||
fn encode(&self, out: &mut dyn BufMut) {
|
||||
self.rlp_encode(out);
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
self.rlp_encoded_length()
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for TxPayment {
|
||||
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||
Self::rlp_decode(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for TxPayment {
|
||||
fn size(&self) -> usize {
|
||||
TxPayment::size(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl RlpBincode for TxPayment {}
|
||||
@@ -1,39 +0,0 @@
|
||||
use crate::primitives::TxTypeCustom;
|
||||
use alloy_primitives::bytes::{Buf, BufMut};
|
||||
use reth_codecs::{txtype::COMPACT_EXTENDED_IDENTIFIER_FLAG, Compact};
|
||||
|
||||
pub const PAYMENT_TX_TYPE_ID: u8 = 42;
|
||||
|
||||
impl Compact for TxTypeCustom {
|
||||
fn to_compact<B>(&self, buf: &mut B) -> usize
|
||||
where
|
||||
B: BufMut + AsMut<[u8]>,
|
||||
{
|
||||
match self {
|
||||
Self::Op(ty) => ty.to_compact(buf),
|
||||
Self::Payment => {
|
||||
buf.put_u8(PAYMENT_TX_TYPE_ID);
|
||||
COMPACT_EXTENDED_IDENTIFIER_FLAG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
match identifier {
|
||||
COMPACT_EXTENDED_IDENTIFIER_FLAG => (
|
||||
{
|
||||
let extended_identifier = buf.get_u8();
|
||||
match extended_identifier {
|
||||
PAYMENT_TX_TYPE_ID => Self::Payment,
|
||||
_ => panic!("Unsupported TxType identifier: {extended_identifier}"),
|
||||
}
|
||||
},
|
||||
buf,
|
||||
),
|
||||
v => {
|
||||
let (inner, buf) = TxTypeCustom::from_compact(buf, v);
|
||||
(inner, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
use crate::{
|
||||
evm::CustomTxEnv,
|
||||
primitives::{CustomHeader, CustomTransaction},
|
||||
};
|
||||
use alloy_consensus::error::ValueError;
|
||||
use alloy_evm::EvmEnv;
|
||||
use alloy_network::TxSigner;
|
||||
use op_alloy_consensus::OpTxEnvelope;
|
||||
use op_alloy_rpc_types::{OpTransactionReceipt, OpTransactionRequest};
|
||||
use reth_op::rpc::RpcTypes;
|
||||
use reth_rpc_api::eth::{
|
||||
EthTxEnvError, SignTxRequestError, SignableTxRequest, TryIntoSimTx, TryIntoTxEnv,
|
||||
};
|
||||
use revm::context::BlockEnv;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct CustomRpcTypes;
|
||||
|
||||
impl RpcTypes for CustomRpcTypes {
|
||||
type Header = alloy_rpc_types_eth::Header<CustomHeader>;
|
||||
type Receipt = OpTransactionReceipt;
|
||||
type TransactionRequest = OpTransactionRequest;
|
||||
type TransactionResponse = op_alloy_rpc_types::Transaction<CustomTransaction>;
|
||||
}
|
||||
|
||||
impl TryIntoSimTx<CustomTransaction> for OpTransactionRequest {
|
||||
fn try_into_sim_tx(self) -> Result<CustomTransaction, ValueError<Self>> {
|
||||
Ok(CustomTransaction::Op(self.try_into_sim_tx()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryIntoTxEnv<CustomTxEnv> for OpTransactionRequest {
|
||||
type Err = EthTxEnvError;
|
||||
|
||||
fn try_into_tx_env<Spec>(
|
||||
self,
|
||||
evm_env: &EvmEnv<Spec, BlockEnv>,
|
||||
) -> Result<CustomTxEnv, Self::Err> {
|
||||
Ok(CustomTxEnv::Op(self.try_into_tx_env(evm_env)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl SignableTxRequest<CustomTransaction> for OpTransactionRequest {
|
||||
async fn try_build_and_sign(
|
||||
self,
|
||||
signer: impl TxSigner<alloy_primitives::Signature> + Send,
|
||||
) -> Result<CustomTransaction, SignTxRequestError> {
|
||||
Ok(CustomTransaction::Op(
|
||||
SignableTxRequest::<OpTxEnvelope>::try_build_and_sign(self, signer).await?,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "example-engine-api-access"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-db = { workspace = true, features = ["op", "test-utils"] }
|
||||
reth-node-builder.workspace = true
|
||||
reth-optimism-node.workspace = true
|
||||
reth-optimism-chainspec.workspace = true
|
||||
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
@@ -1,47 +0,0 @@
|
||||
//! Example demonstrating how to access the Engine API instance during construction.
|
||||
//!
|
||||
//! Run with
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo run -p example-engine-api-access
|
||||
//! ```
|
||||
|
||||
use reth_db::test_utils::create_test_rw_db;
|
||||
use reth_node_builder::{EngineApiExt, FullNodeComponents, NodeBuilder, NodeConfig};
|
||||
use reth_optimism_chainspec::BASE_MAINNET;
|
||||
use reth_optimism_node::{
|
||||
args::RollupArgs, node::OpEngineValidatorBuilder, OpAddOns, OpEngineApiBuilder, OpNode,
|
||||
};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Op node configuration and setup
|
||||
let config = NodeConfig::new(BASE_MAINNET.clone());
|
||||
let db = create_test_rw_db();
|
||||
let args = RollupArgs::default();
|
||||
let op_node = OpNode::new(args);
|
||||
|
||||
let (engine_api_tx, _engine_api_rx) = oneshot::channel();
|
||||
|
||||
let engine_api =
|
||||
EngineApiExt::new(OpEngineApiBuilder::<OpEngineValidatorBuilder>::default(), move |api| {
|
||||
let _ = engine_api_tx.send(api);
|
||||
});
|
||||
|
||||
let _builder = NodeBuilder::new(config)
|
||||
.with_database(db)
|
||||
.with_types::<OpNode>()
|
||||
.with_components(op_node.components())
|
||||
.with_add_ons(OpAddOns::default().with_engine_api(engine_api))
|
||||
.on_component_initialized(move |ctx| {
|
||||
let _provider = ctx.provider();
|
||||
Ok(())
|
||||
})
|
||||
.on_node_started(|_full_node| Ok(()))
|
||||
.on_rpc_started(|_ctx, handles| {
|
||||
let _client = handles.rpc.http_client();
|
||||
Ok(())
|
||||
})
|
||||
.check_launch();
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "example-exex-hello-world"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-ethereum = { workspace = true, features = ["full", "cli"] }
|
||||
reth-tracing.workspace = true
|
||||
|
||||
eyre.workspace = true
|
||||
futures.workspace = true
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
reth-op = { workspace = true, features = ["full", "cli"] }
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
@@ -1,145 +0,0 @@
|
||||
//! Example for a simple Execution Extension
|
||||
//!
|
||||
//! Run with
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo run -p example-exex-hello-world -- node --dev --dev.block-time 5s
|
||||
//! ```
|
||||
|
||||
use clap::Parser;
|
||||
use futures::TryStreamExt;
|
||||
use reth_ethereum::{
|
||||
chainspec::EthereumHardforks,
|
||||
exex::{ExExContext, ExExEvent, ExExNotification},
|
||||
node::{
|
||||
api::{FullNodeComponents, NodeTypes},
|
||||
builder::rpc::RpcHandle,
|
||||
EthereumNode,
|
||||
},
|
||||
rpc::api::eth::helpers::FullEthApi,
|
||||
};
|
||||
use reth_tracing::tracing::info;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
/// Additional CLI arguments
|
||||
#[derive(Parser)]
|
||||
struct ExExArgs {
|
||||
/// whether to launch an op-reth node
|
||||
#[arg(long)]
|
||||
optimism: bool,
|
||||
}
|
||||
|
||||
/// A basic subscription loop of new blocks.
|
||||
async fn my_exex<Node: FullNodeComponents>(mut ctx: ExExContext<Node>) -> eyre::Result<()> {
|
||||
while let Some(notification) = ctx.notifications.try_next().await? {
|
||||
match ¬ification {
|
||||
ExExNotification::ChainCommitted { new } => {
|
||||
info!(committed_chain = ?new.range(), "Received commit");
|
||||
}
|
||||
ExExNotification::ChainReorged { old, new } => {
|
||||
info!(from_chain = ?old.range(), to_chain = ?new.range(), "Received reorg");
|
||||
}
|
||||
ExExNotification::ChainReverted { old } => {
|
||||
info!(reverted_chain = ?old.range(), "Received revert");
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(committed_chain) = notification.committed_chain() {
|
||||
ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().num_hash()))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This is an example of how to access the [`RpcHandle`] inside an ExEx. It receives the
|
||||
/// [`RpcHandle`] once the node is launched fully.
|
||||
///
|
||||
/// This function supports both Opstack Eth API and ethereum Eth API.
|
||||
///
|
||||
/// The received handle gives access to the `EthApi` has full access to all eth api functionality
|
||||
/// [`FullEthApi`]. And also gives access to additional eth-related rpc method handlers, such as eth
|
||||
/// filter.
|
||||
async fn ethapi_exex<Node, EthApi>(
|
||||
mut ctx: ExExContext<Node>,
|
||||
rpc_handle: oneshot::Receiver<RpcHandle<Node, EthApi>>,
|
||||
) -> eyre::Result<()>
|
||||
where
|
||||
Node: FullNodeComponents<Types: NodeTypes<ChainSpec: EthereumHardforks>>,
|
||||
EthApi: FullEthApi,
|
||||
{
|
||||
// Wait for the ethapi to be sent from the main function
|
||||
let rpc_handle = rpc_handle.await?;
|
||||
info!("Received rpc handle inside exex");
|
||||
|
||||
// obtain the ethapi from the rpc handle
|
||||
let ethapi = rpc_handle.eth_api();
|
||||
|
||||
// EthFilter type that provides all eth_getlogs related logic
|
||||
let _eth_filter = rpc_handle.eth_handlers().filter.clone();
|
||||
// EthPubSub type that provides all eth_subscribe logic
|
||||
let _eth_pubsub = rpc_handle.eth_handlers().pubsub.clone();
|
||||
// The TraceApi type that provides all the trace_ handlers
|
||||
let _trace_api = rpc_handle.trace_api();
|
||||
// The DebugApi type that provides all the debug_ handlers
|
||||
let _debug_api = rpc_handle.debug_api();
|
||||
|
||||
while let Some(notification) = ctx.notifications.try_next().await? {
|
||||
if let Some(committed_chain) = notification.committed_chain() {
|
||||
ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().num_hash()))?;
|
||||
|
||||
// can use the eth api to interact with the node
|
||||
let _rpc_block = ethapi.rpc_block(committed_chain.tip().hash().into(), true).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
let args = ExExArgs::parse();
|
||||
|
||||
if args.optimism {
|
||||
reth_op::cli::Cli::parse_args().run(|builder, _| {
|
||||
let (rpc_handle_tx, rpc_handle_rx) = oneshot::channel();
|
||||
Box::pin(async move {
|
||||
let handle = builder
|
||||
.node(reth_op::node::OpNode::default())
|
||||
.install_exex("my-exex", async move |ctx| Ok(my_exex(ctx)))
|
||||
.install_exex("ethapi-exex", async move |ctx| {
|
||||
Ok(ethapi_exex(ctx, rpc_handle_rx))
|
||||
})
|
||||
.launch()
|
||||
.await?;
|
||||
|
||||
// Retrieve the rpc handle from the node and send it to the exex
|
||||
rpc_handle_tx
|
||||
.send(handle.node.add_ons_handle.clone())
|
||||
.expect("Failed to send ethapi to ExEx");
|
||||
|
||||
handle.wait_for_node_exit().await
|
||||
})
|
||||
})
|
||||
} else {
|
||||
reth_ethereum::cli::Cli::parse_args().run(|builder, _| {
|
||||
Box::pin(async move {
|
||||
let (rpc_handle_tx, rpc_handle_rx) = oneshot::channel();
|
||||
let handle = builder
|
||||
.node(EthereumNode::default())
|
||||
.install_exex("my-exex", async move |ctx| Ok(my_exex(ctx)))
|
||||
.install_exex("ethapi-exex", async move |ctx| {
|
||||
Ok(ethapi_exex(ctx, rpc_handle_rx))
|
||||
})
|
||||
.launch()
|
||||
.await?;
|
||||
|
||||
// Retrieve the rpc handle from the node and send it to the exex
|
||||
rpc_handle_tx
|
||||
.send(handle.node.add_ons_handle.clone())
|
||||
.expect("Failed to send ethapi to ExEx");
|
||||
|
||||
handle.wait_for_node_exit().await
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "example-op-db-access"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
reth-op = { workspace = true, features = ["node"] }
|
||||
|
||||
eyre.workspace = true
|
||||
@@ -1,23 +0,0 @@
|
||||
//! Shows how to manually access the database
|
||||
|
||||
use reth_op::{chainspec::BASE_MAINNET, node::OpNode, provider::providers::ReadOnlyConfig};
|
||||
|
||||
// Providers are zero-cost abstractions on top of an opened MDBX Transaction
|
||||
// exposing a familiar API to query the chain's information without requiring knowledge
|
||||
// of the inner tables.
|
||||
//
|
||||
// These abstractions do not include any caching and the user is responsible for doing that.
|
||||
// Other parts of the code which include caching are parts of the `EthApi` abstraction.
|
||||
fn main() -> eyre::Result<()> {
|
||||
// The path to data directory, e.g. "~/.local/reth/share/base"
|
||||
let datadir = std::env::var("RETH_DATADIR")?;
|
||||
|
||||
// Instantiate a provider factory for Ethereum mainnet using the provided datadir path.
|
||||
let factory = OpNode::provider_factory_builder()
|
||||
.open_read_only(BASE_MAINNET.clone(), ReadOnlyConfig::from_datadir(datadir))?;
|
||||
|
||||
// obtain a provider access that has direct access to the database.
|
||||
let _provider = factory.provider();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user