mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix: Convert header type for eth_subscribe
Due to custom header usage, only `eth_subscribe` method was returning the new header format in raw format, while other part were using RpcConvert to convert headers. Make `eth_subscribe` newHeads to return the `inner` field (original eth header) instead.
This commit is contained in:
@ -19,62 +19,23 @@ use alloy_rpc_types::{
|
|||||||
TransactionInfo,
|
TransactionInfo,
|
||||||
pubsub::{Params, SubscriptionKind},
|
pubsub::{Params, SubscriptionKind},
|
||||||
};
|
};
|
||||||
use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage, SubscriptionSink, proc_macros::rpc};
|
use jsonrpsee::{PendingSubscriptionSink, proc_macros::rpc};
|
||||||
use jsonrpsee_core::{RpcResult, async_trait};
|
use jsonrpsee_core::{RpcResult, async_trait};
|
||||||
use jsonrpsee_types::{ErrorObject, error::INTERNAL_ERROR_CODE};
|
use jsonrpsee_types::{ErrorObject, error::INTERNAL_ERROR_CODE};
|
||||||
use reth::{api::FullNodeComponents, builder::rpc::RpcContext, tasks::TaskSpawner};
|
use reth::{api::FullNodeComponents, builder::rpc::RpcContext, tasks::TaskSpawner};
|
||||||
use reth_primitives_traits::SignedTransaction;
|
use reth_primitives_traits::SignedTransaction;
|
||||||
use reth_provider::{BlockIdReader, BlockReader, BlockReaderIdExt, ReceiptProvider};
|
use reth_provider::{BlockIdReader, BlockReader, BlockReaderIdExt, ReceiptProvider};
|
||||||
use reth_rpc::{EthFilter, EthPubSub, RpcTypes, eth::pubsub::SubscriptionSerializeError};
|
use reth_rpc::{EthFilter, EthPubSub};
|
||||||
use reth_rpc_eth_api::{
|
use reth_rpc_eth_api::{
|
||||||
EthApiServer, EthApiTypes, EthFilterApiServer, EthPubSubApiServer, FullEthApiTypes, RpcBlock,
|
EthApiTypes, EthFilterApiServer, EthPubSubApiServer, RpcBlock, RpcConvert, RpcReceipt,
|
||||||
RpcConvert, RpcHeader, RpcNodeCoreExt, RpcReceipt, RpcTransaction, RpcTxReq,
|
RpcTransaction, helpers::EthBlocks, transaction::ConvertReceiptInput,
|
||||||
helpers::{EthBlocks, EthTransactions, LoadReceipt},
|
|
||||||
transaction::ConvertReceiptInput,
|
|
||||||
};
|
};
|
||||||
use reth_rpc_eth_types::EthApiError;
|
use reth_rpc_eth_types::EthApiError;
|
||||||
use serde::Serialize;
|
|
||||||
use std::{marker::PhantomData, sync::Arc};
|
use std::{marker::PhantomData, sync::Arc};
|
||||||
use tokio_stream::{Stream, StreamExt};
|
use tokio_stream::StreamExt;
|
||||||
use tracing::{Instrument, trace};
|
use tracing::{Instrument, trace};
|
||||||
|
|
||||||
use crate::{HlBlock, node::primitives::HlPrimitives};
|
use crate::addons::utils::{EthWrapper, new_headers_stream, pipe_from_stream};
|
||||||
|
|
||||||
pub trait EthWrapper:
|
|
||||||
EthApiServer<
|
|
||||||
RpcTxReq<Self::NetworkTypes>,
|
|
||||||
RpcTransaction<Self::NetworkTypes>,
|
|
||||||
RpcBlock<Self::NetworkTypes>,
|
|
||||||
RpcReceipt<Self::NetworkTypes>,
|
|
||||||
RpcHeader<Self::NetworkTypes>,
|
|
||||||
> + FullEthApiTypes<
|
|
||||||
Primitives = HlPrimitives,
|
|
||||||
NetworkTypes: RpcTypes<TransactionResponse = alloy_rpc_types_eth::Transaction>,
|
|
||||||
> + RpcNodeCoreExt<Provider: BlockReader<Block = HlBlock>>
|
|
||||||
+ EthBlocks
|
|
||||||
+ EthTransactions
|
|
||||||
+ LoadReceipt
|
|
||||||
+ 'static
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EthWrapper for T where
|
|
||||||
T: EthApiServer<
|
|
||||||
RpcTxReq<Self::NetworkTypes>,
|
|
||||||
RpcTransaction<Self::NetworkTypes>,
|
|
||||||
RpcBlock<Self::NetworkTypes>,
|
|
||||||
RpcReceipt<Self::NetworkTypes>,
|
|
||||||
RpcHeader<Self::NetworkTypes>,
|
|
||||||
> + FullEthApiTypes<
|
|
||||||
Primitives = HlPrimitives,
|
|
||||||
NetworkTypes: RpcTypes<TransactionResponse = alloy_rpc_types_eth::Transaction>,
|
|
||||||
> + RpcNodeCoreExt<Provider: BlockReader<Block = HlBlock>>
|
|
||||||
+ EthBlocks
|
|
||||||
+ EthTransactions
|
|
||||||
+ LoadReceipt
|
|
||||||
+ 'static
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rpc(server, namespace = "eth")]
|
#[rpc(server, namespace = "eth")]
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -387,7 +348,7 @@ where
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
let _ = pubsub.handle_accepted(sink, kind, params).await;
|
let _ = pipe_from_stream(sink, new_headers_stream::<Eth>(&provider)).await;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -412,23 +373,6 @@ fn adjust_log<Eth: EthWrapper>(mut log: Log, provider: &Eth::Provider) -> Option
|
|||||||
Some(log)
|
Some(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn pipe_from_stream<T: Serialize, St: Stream<Item = T> + Unpin>(
|
|
||||||
sink: SubscriptionSink,
|
|
||||||
mut stream: St,
|
|
||||||
) -> Result<(), ErrorObject<'static>> {
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
_ = sink.closed() => break Ok(()),
|
|
||||||
maybe_item = stream.next() => {
|
|
||||||
let Some(item) = maybe_item else { break Ok(()) };
|
|
||||||
let msg = SubscriptionMessage::new(sink.method_name(), sink.subscription_id(), &item)
|
|
||||||
.map_err(SubscriptionSerializeError::from)?;
|
|
||||||
if sink.send(msg).await.is_err() { break Ok(()); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HlNodeBlockFilterHttp<Eth: EthWrapper> {
|
pub struct HlNodeBlockFilterHttp<Eth: EthWrapper> {
|
||||||
eth_api: Arc<Eth>,
|
eth_api: Arc<Eth>,
|
||||||
_marker: PhantomData<Eth>,
|
_marker: PhantomData<Eth>,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
pub mod call_forwarder;
|
pub mod call_forwarder;
|
||||||
pub mod hl_node_compliance;
|
pub mod hl_node_compliance;
|
||||||
pub mod tx_forwarder;
|
pub mod tx_forwarder;
|
||||||
|
pub mod subscribe_fixup;
|
||||||
|
mod utils;
|
||||||
|
|||||||
54
src/addons/subscribe_fixup.rs
Normal file
54
src/addons/subscribe_fixup.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::addons::utils::{EthWrapper, new_headers_stream, pipe_from_stream};
|
||||||
|
use alloy_rpc_types::pubsub::{Params, SubscriptionKind};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use jsonrpsee::PendingSubscriptionSink;
|
||||||
|
use jsonrpsee_types::ErrorObject;
|
||||||
|
use reth::tasks::TaskSpawner;
|
||||||
|
use reth_rpc::EthPubSub;
|
||||||
|
use reth_rpc_convert::RpcTransaction;
|
||||||
|
use reth_rpc_eth_api::{EthApiTypes, EthPubSubApiServer};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct SubscribeFixup<Eth: EthWrapper> {
|
||||||
|
pubsub: Arc<EthPubSub<Eth>>,
|
||||||
|
provider: Arc<Eth::Provider>,
|
||||||
|
subscription_task_spawner: Box<dyn TaskSpawner + 'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<Eth: EthWrapper> EthPubSubApiServer<RpcTransaction<Eth::NetworkTypes>> for SubscribeFixup<Eth>
|
||||||
|
where
|
||||||
|
ErrorObject<'static>: From<<Eth as EthApiTypes>::Error>,
|
||||||
|
{
|
||||||
|
async fn subscribe(
|
||||||
|
&self,
|
||||||
|
pending: PendingSubscriptionSink,
|
||||||
|
kind: SubscriptionKind,
|
||||||
|
params: Option<Params>,
|
||||||
|
) -> jsonrpsee::core::SubscriptionResult {
|
||||||
|
let sink = pending.accept().await?;
|
||||||
|
let (pubsub, provider) = (self.pubsub.clone(), self.provider.clone());
|
||||||
|
self.subscription_task_spawner.spawn(Box::pin(async move {
|
||||||
|
if kind == SubscriptionKind::NewHeads {
|
||||||
|
let _ = pipe_from_stream(sink, new_headers_stream::<Eth>(&provider)).await;
|
||||||
|
} else {
|
||||||
|
let _ = pubsub.handle_accepted(sink, kind, params).await;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Eth: EthWrapper> SubscribeFixup<Eth> {
|
||||||
|
pub fn new(
|
||||||
|
pubsub: Arc<EthPubSub<Eth>>,
|
||||||
|
provider: Arc<Eth::Provider>,
|
||||||
|
subscription_task_spawner: Box<dyn TaskSpawner + 'static>,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
Eth: EthWrapper,
|
||||||
|
ErrorObject<'static>: From<Eth::Error>,
|
||||||
|
{
|
||||||
|
Self { pubsub, provider, subscription_task_spawner }
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/addons/utils.rs
Normal file
90
src/addons/utils.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{HlBlock, HlPrimitives};
|
||||||
|
use alloy_primitives::U256;
|
||||||
|
use alloy_rpc_types::Header;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use jsonrpsee::{SubscriptionMessage, SubscriptionSink};
|
||||||
|
use jsonrpsee_types::ErrorObject;
|
||||||
|
use reth_primitives::SealedHeader;
|
||||||
|
use reth_provider::{BlockReader, CanonStateSubscriptions};
|
||||||
|
use reth_rpc::{RpcTypes, eth::pubsub::SubscriptionSerializeError};
|
||||||
|
use reth_rpc_convert::{RpcBlock, RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq};
|
||||||
|
use reth_rpc_eth_api::{
|
||||||
|
EthApiServer, FullEthApiTypes, RpcNodeCoreExt,
|
||||||
|
helpers::{EthBlocks, EthTransactions, LoadReceipt},
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use tokio_stream::Stream;
|
||||||
|
|
||||||
|
pub trait EthWrapper:
|
||||||
|
EthApiServer<
|
||||||
|
RpcTxReq<Self::NetworkTypes>,
|
||||||
|
RpcTransaction<Self::NetworkTypes>,
|
||||||
|
RpcBlock<Self::NetworkTypes>,
|
||||||
|
RpcReceipt<Self::NetworkTypes>,
|
||||||
|
RpcHeader<Self::NetworkTypes>,
|
||||||
|
> + FullEthApiTypes<
|
||||||
|
Primitives = HlPrimitives,
|
||||||
|
NetworkTypes: RpcTypes<TransactionResponse = alloy_rpc_types_eth::Transaction>,
|
||||||
|
> + RpcNodeCoreExt<Provider: BlockReader<Block = HlBlock>>
|
||||||
|
+ EthBlocks
|
||||||
|
+ EthTransactions
|
||||||
|
+ LoadReceipt
|
||||||
|
+ 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EthWrapper for T where
|
||||||
|
T: EthApiServer<
|
||||||
|
RpcTxReq<Self::NetworkTypes>,
|
||||||
|
RpcTransaction<Self::NetworkTypes>,
|
||||||
|
RpcBlock<Self::NetworkTypes>,
|
||||||
|
RpcReceipt<Self::NetworkTypes>,
|
||||||
|
RpcHeader<Self::NetworkTypes>,
|
||||||
|
> + FullEthApiTypes<
|
||||||
|
Primitives = HlPrimitives,
|
||||||
|
NetworkTypes: RpcTypes<TransactionResponse = alloy_rpc_types_eth::Transaction>,
|
||||||
|
> + RpcNodeCoreExt<Provider: BlockReader<Block = HlBlock>>
|
||||||
|
+ EthBlocks
|
||||||
|
+ EthTransactions
|
||||||
|
+ LoadReceipt
|
||||||
|
+ 'static
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn pipe_from_stream<T: Serialize, St: Stream<Item = T> + Unpin>(
|
||||||
|
sink: SubscriptionSink,
|
||||||
|
mut stream: St,
|
||||||
|
) -> Result<(), ErrorObject<'static>> {
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
_ = sink.closed() => break Ok(()),
|
||||||
|
maybe_item = stream.next() => {
|
||||||
|
let Some(item) = maybe_item else { break Ok(()) };
|
||||||
|
let msg = SubscriptionMessage::new(sink.method_name(), sink.subscription_id(), &item)
|
||||||
|
.map_err(SubscriptionSerializeError::from)?;
|
||||||
|
if sink.send(msg).await.is_err() { break Ok(()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn new_headers_stream<Eth: EthWrapper>(
|
||||||
|
provider: &Arc<Eth::Provider>,
|
||||||
|
) -> impl Stream<Item = Header<alloy_consensus::Header>> {
|
||||||
|
provider.canonical_state_stream().flat_map(|new_chain| {
|
||||||
|
let headers = new_chain
|
||||||
|
.committed()
|
||||||
|
.blocks_iter()
|
||||||
|
.map(|block| {
|
||||||
|
Header::from_consensus(
|
||||||
|
SealedHeader::new(block.header().inner.clone(), block.hash()).into(),
|
||||||
|
None,
|
||||||
|
Some(U256::from(block.rlp_length())),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
futures::stream::iter(headers)
|
||||||
|
})
|
||||||
|
}
|
||||||
17
src/main.rs
17
src/main.rs
@ -1,12 +1,16 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use reth::builder::{NodeBuilder, NodeHandle, WithLaunchContext};
|
use reth::{
|
||||||
|
builder::{NodeBuilder, NodeHandle, WithLaunchContext},
|
||||||
|
rpc::{api::EthPubSubApiServer, eth::RpcNodeCore},
|
||||||
|
};
|
||||||
use reth_db::DatabaseEnv;
|
use reth_db::DatabaseEnv;
|
||||||
use reth_hl::{
|
use reth_hl::{
|
||||||
addons::{
|
addons::{
|
||||||
call_forwarder::{self, CallForwarderApiServer},
|
call_forwarder::{self, CallForwarderApiServer},
|
||||||
hl_node_compliance::install_hl_node_compliance,
|
hl_node_compliance::install_hl_node_compliance,
|
||||||
|
subscribe_fixup::SubscribeFixup,
|
||||||
tx_forwarder::{self, EthForwarderApiServer},
|
tx_forwarder::{self, EthForwarderApiServer},
|
||||||
},
|
},
|
||||||
chainspec::{HlChainSpec, parser::HlChainSpecParser},
|
chainspec::{HlChainSpec, parser::HlChainSpecParser},
|
||||||
@ -69,6 +73,17 @@ fn main() -> eyre::Result<()> {
|
|||||||
info!("eth_getProof is disabled by default");
|
info!("eth_getProof is disabled by default");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a temporary workaround to fix the issue with custom headers
|
||||||
|
// affects `eth_subscribe[type=newHeads]`
|
||||||
|
ctx.modules.replace_configured(
|
||||||
|
SubscribeFixup::new(
|
||||||
|
Arc::new(ctx.registry.eth_handlers().pubsub.clone()),
|
||||||
|
Arc::new(ctx.registry.eth_api().provider().clone()),
|
||||||
|
Box::new(ctx.node().task_executor.clone()),
|
||||||
|
)
|
||||||
|
.into_rpc(),
|
||||||
|
)?;
|
||||||
|
|
||||||
ctx.modules.merge_configured(
|
ctx.modules.merge_configured(
|
||||||
HlBlockPrecompileExt::new(ctx.registry.eth_api().clone()).into_rpc(),
|
HlBlockPrecompileExt::new(ctx.registry.eth_api().clone()).into_rpc(),
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user