mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feature: move node-api example into examples (#6390)
Signed-off-by: jsvisa <delweng@gmail.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
24
examples/custom-node/Cargo.toml
Normal file
24
examples/custom-node/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "custom-node"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
reth.workspace = true
|
||||
reth-rpc-api.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
reth-node-core.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
|
||||
alloy-chains.workspace = true
|
||||
jsonrpsee.workspace = true
|
||||
eyre.workspace = true
|
||||
tokio.workspace = true
|
||||
thiserror.workspace = true
|
||||
serde.workspace = true
|
||||
200
examples/custom-node/src/main.rs
Normal file
200
examples/custom-node/src/main.rs
Normal file
@ -0,0 +1,200 @@
|
||||
//! This example shows how to implement a custom [EngineTypes].
|
||||
//!
|
||||
//! The [EngineTypes] trait can be implemented to configure the engine to work with custom types,
|
||||
//! as long as those types implement certain traits.
|
||||
//!
|
||||
//! Custom payload attributes can be supported by implementing two main traits:
|
||||
//!
|
||||
//! [PayloadAttributes] can be implemented for payload attributes types that are used as
|
||||
//! arguments to the `engine_forkchoiceUpdated` method. This type should be used to define and
|
||||
//! _spawn_ payload jobs.
|
||||
//!
|
||||
//! [PayloadBuilderAttributes] can be implemented for payload attributes types that _describe_
|
||||
//! running payload jobs.
|
||||
//!
|
||||
//! Once traits are implemented and custom types are defined, the [EngineTypes] trait can be
|
||||
//! implemented:
|
||||
|
||||
use alloy_chains::Chain;
|
||||
use jsonrpsee::http_client::HttpClient;
|
||||
use reth::builder::spawn_node;
|
||||
use reth_node_api::{
|
||||
validate_version_specific_fields, AttributesValidationError, EngineApiMessageVersion,
|
||||
EngineTypes, PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
};
|
||||
use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig};
|
||||
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes};
|
||||
use reth_primitives::{Address, ChainSpec, Genesis, Withdrawals, B256, U256};
|
||||
use reth_rpc_api::{EngineApiClient, EthApiClient};
|
||||
use reth_rpc_types::{
|
||||
engine::{ForkchoiceState, PayloadAttributes as EthPayloadAttributes, PayloadId},
|
||||
withdrawal::Withdrawal,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::Infallible;
|
||||
use thiserror::Error;
|
||||
|
||||
/// A custom payload attributes type.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CustomPayloadAttributes {
|
||||
/// An inner payload type
|
||||
#[serde(flatten)]
|
||||
pub inner: EthPayloadAttributes,
|
||||
/// A custom field
|
||||
pub custom: u64,
|
||||
}
|
||||
|
||||
/// Custom error type used in payload attributes validation
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CustomError {
|
||||
#[error("Custom field is not zero")]
|
||||
CustomFieldIsNotZero,
|
||||
}
|
||||
|
||||
impl PayloadAttributes for CustomPayloadAttributes {
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.inner.timestamp()
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> Option<&Vec<Withdrawal>> {
|
||||
self.inner.withdrawals()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.inner.parent_beacon_block_root()
|
||||
}
|
||||
|
||||
fn ensure_well_formed_attributes(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
validate_version_specific_fields(chain_spec, version, self.into())?;
|
||||
|
||||
// custom validation logic - ensure that the custom field is not zero
|
||||
if self.custom == 0 {
|
||||
return Err(AttributesValidationError::invalid_params(CustomError::CustomFieldIsNotZero))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Newtype around the payload builder attributes type
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CustomPayloadBuilderAttributes(EthPayloadBuilderAttributes);
|
||||
|
||||
impl PayloadBuilderAttributes for CustomPayloadBuilderAttributes {
|
||||
type RpcPayloadAttributes = CustomPayloadAttributes;
|
||||
type Error = Infallible;
|
||||
|
||||
fn try_new(parent: B256, attributes: CustomPayloadAttributes) -> Result<Self, Infallible> {
|
||||
Ok(Self(EthPayloadBuilderAttributes::new(parent, attributes.inner)))
|
||||
}
|
||||
|
||||
fn payload_id(&self) -> PayloadId {
|
||||
self.0.id
|
||||
}
|
||||
|
||||
fn parent(&self) -> B256 {
|
||||
self.0.parent
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.0.timestamp
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.0.parent_beacon_block_root
|
||||
}
|
||||
|
||||
fn suggested_fee_recipient(&self) -> Address {
|
||||
self.0.suggested_fee_recipient
|
||||
}
|
||||
|
||||
fn prev_randao(&self) -> B256 {
|
||||
self.0.prev_randao
|
||||
}
|
||||
|
||||
fn withdrawals(&self) -> &Withdrawals {
|
||||
&self.0.withdrawals
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom engine types - uses a custom payload attributes RPC type, but uses the default
|
||||
/// payload builder attributes type.
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct CustomEngineTypes;
|
||||
|
||||
impl EngineTypes for CustomEngineTypes {
|
||||
type PayloadAttributes = CustomPayloadAttributes;
|
||||
type PayloadBuilderAttributes = CustomPayloadBuilderAttributes;
|
||||
type BuiltPayload = EthBuiltPayload;
|
||||
|
||||
fn validate_version_specific_fields(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
payload_or_attrs: PayloadOrAttributes<'_, CustomPayloadAttributes>,
|
||||
) -> Result<(), AttributesValidationError> {
|
||||
validate_version_specific_fields(chain_spec, version, payload_or_attrs)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> eyre::Result<()> {
|
||||
// this launches a test node with http
|
||||
let rpc_args = RpcServerArgs::default().with_http();
|
||||
|
||||
// create optimism genesis with canyon at block 2
|
||||
let spec = ChainSpec::builder()
|
||||
.chain(Chain::mainnet())
|
||||
.genesis(Genesis::default())
|
||||
.london_activated()
|
||||
.paris_activated()
|
||||
.shanghai_activated()
|
||||
.build();
|
||||
|
||||
let genesis_hash = spec.genesis_hash();
|
||||
|
||||
// create node config
|
||||
let node_config = NodeConfig::test().with_rpc(rpc_args).with_chain(spec);
|
||||
|
||||
let (handle, _manager) = spawn_node(node_config).await.unwrap();
|
||||
|
||||
// call a function on the node
|
||||
let client = handle.rpc_server_handles().auth.http_client();
|
||||
let block_number = client.block_number().await.unwrap();
|
||||
|
||||
// it should be zero, since this is an ephemeral test node
|
||||
assert_eq!(block_number, U256::ZERO);
|
||||
|
||||
// call the engine_forkchoiceUpdated function with payload attributes
|
||||
let forkchoice_state = ForkchoiceState {
|
||||
head_block_hash: genesis_hash,
|
||||
safe_block_hash: genesis_hash,
|
||||
finalized_block_hash: genesis_hash,
|
||||
};
|
||||
|
||||
let payload_attributes = CustomPayloadAttributes {
|
||||
inner: EthPayloadAttributes {
|
||||
timestamp: 1,
|
||||
prev_randao: Default::default(),
|
||||
suggested_fee_recipient: Default::default(),
|
||||
withdrawals: Some(vec![]),
|
||||
parent_beacon_block_root: None,
|
||||
},
|
||||
custom: 42,
|
||||
};
|
||||
|
||||
// call the engine_forkchoiceUpdated function with payload attributes
|
||||
let res = <HttpClient as EngineApiClient<CustomEngineTypes>>::fork_choice_updated_v2(
|
||||
&client,
|
||||
forkchoice_state,
|
||||
Some(payload_attributes),
|
||||
)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user