fix: add block rewards to trace_block (#3491)

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
Bjerg
2023-07-02 17:06:52 +02:00
committed by GitHub
parent 951fd0ae0c
commit 4f32f5627c
12 changed files with 160 additions and 34 deletions

1
Cargo.lock generated
View File

@ -5651,6 +5651,7 @@ dependencies = [
"jsonwebtoken", "jsonwebtoken",
"pin-project", "pin-project",
"rand 0.8.5", "rand 0.8.5",
"reth-consensus-common",
"reth-interfaces", "reth-interfaces",
"reth-metrics", "reth-metrics",
"reth-network-api", "reth-network-api",

View File

@ -8,7 +8,8 @@ use clap::{
use futures::TryFutureExt; use futures::TryFutureExt;
use reth_network_api::{NetworkInfo, Peers}; use reth_network_api::{NetworkInfo, Peers};
use reth_provider::{ use reth_provider::{
BlockReaderIdExt, CanonStateSubscriptions, EvmEnvProvider, HeaderProvider, StateProviderFactory, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, EvmEnvProvider, HeaderProvider,
StateProviderFactory,
}; };
use reth_rpc::{ use reth_rpc::{
eth::{ eth::{
@ -235,6 +236,7 @@ impl RpcServerArgs {
+ HeaderProvider + HeaderProvider
+ StateProviderFactory + StateProviderFactory
+ EvmEnvProvider + EvmEnvProvider
+ ChainSpecProvider
+ Clone + Clone
+ Unpin + Unpin
+ 'static, + 'static,
@ -295,6 +297,7 @@ impl RpcServerArgs {
+ HeaderProvider + HeaderProvider
+ StateProviderFactory + StateProviderFactory
+ EvmEnvProvider + EvmEnvProvider
+ ChainSpecProvider
+ Clone + Clone
+ Unpin + Unpin
+ 'static, + 'static,

View File

@ -31,13 +31,13 @@
//! //!
//! ``` //! ```
//! use reth_network_api::{NetworkInfo, Peers}; //! use reth_network_api::{NetworkInfo, Peers};
//! use reth_provider::{BlockReaderIdExt, CanonStateSubscriptions, StateProviderFactory, EvmEnvProvider}; //! use reth_provider::{BlockReaderIdExt, ChainSpecProvider, CanonStateSubscriptions, StateProviderFactory, EvmEnvProvider};
//! use reth_rpc_builder::{RethRpcModule, RpcModuleBuilder, RpcServerConfig, ServerBuilder, TransportRpcModuleConfig}; //! use reth_rpc_builder::{RethRpcModule, RpcModuleBuilder, RpcServerConfig, ServerBuilder, TransportRpcModuleConfig};
//! use reth_tasks::TokioTaskExecutor; //! use reth_tasks::TokioTaskExecutor;
//! use reth_transaction_pool::TransactionPool; //! use reth_transaction_pool::TransactionPool;
//! pub async fn launch<Provider, Pool, Network, Events>(provider: Provider, pool: Pool, network: Network, events: Events) //! pub async fn launch<Provider, Pool, Network, Events>(provider: Provider, pool: Pool, network: Network, events: Events)
//! where //! where
//! Provider: BlockReaderIdExt + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static, //! Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static,
//! Pool: TransactionPool + Clone + 'static, //! Pool: TransactionPool + Clone + 'static,
//! Network: NetworkInfo + Peers + Clone + 'static, //! Network: NetworkInfo + Peers + Clone + 'static,
//! Events: CanonStateSubscriptions + Clone + 'static, //! Events: CanonStateSubscriptions + Clone + 'static,
@ -64,7 +64,7 @@
//! ``` //! ```
//! use tokio::try_join; //! use tokio::try_join;
//! use reth_network_api::{NetworkInfo, Peers}; //! use reth_network_api::{NetworkInfo, Peers};
//! use reth_provider::{BlockReaderIdExt, CanonStateSubscriptions, StateProviderFactory, EvmEnvProvider}; //! use reth_provider::{BlockReaderIdExt, ChainSpecProvider, CanonStateSubscriptions, StateProviderFactory, EvmEnvProvider};
//! use reth_rpc::JwtSecret; //! use reth_rpc::JwtSecret;
//! use reth_rpc_builder::{RethRpcModule, RpcModuleBuilder, RpcServerConfig, TransportRpcModuleConfig}; //! use reth_rpc_builder::{RethRpcModule, RpcModuleBuilder, RpcServerConfig, TransportRpcModuleConfig};
//! use reth_tasks::TokioTaskExecutor; //! use reth_tasks::TokioTaskExecutor;
@ -73,7 +73,7 @@
//! use reth_rpc_builder::auth::AuthServerConfig; //! use reth_rpc_builder::auth::AuthServerConfig;
//! pub async fn launch<Provider, Pool, Network, Events, EngineApi>(provider: Provider, pool: Pool, network: Network, events: Events, engine_api: EngineApi) //! pub async fn launch<Provider, Pool, Network, Events, EngineApi>(provider: Provider, pool: Pool, network: Network, events: Events, engine_api: EngineApi)
//! where //! where
//! Provider: BlockReaderIdExt + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static, //! Provider: BlockReaderIdExt + ChainSpecProvider + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static,
//! Pool: TransactionPool + Clone + 'static, //! Pool: TransactionPool + Clone + 'static,
//! Network: NetworkInfo + Peers + Clone + 'static, //! Network: NetworkInfo + Peers + Clone + 'static,
//! Events: CanonStateSubscriptions + Clone + 'static, //! Events: CanonStateSubscriptions + Clone + 'static,
@ -113,7 +113,8 @@ use jsonrpsee::{
use reth_ipc::server::IpcServer; use reth_ipc::server::IpcServer;
use reth_network_api::{NetworkInfo, Peers}; use reth_network_api::{NetworkInfo, Peers};
use reth_provider::{ use reth_provider::{
BlockReader, BlockReaderIdExt, CanonStateSubscriptions, EvmEnvProvider, StateProviderFactory, BlockReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, EvmEnvProvider,
StateProviderFactory,
}; };
use reth_rpc::{ use reth_rpc::{
eth::{ eth::{
@ -169,7 +170,13 @@ pub async fn launch<Provider, Pool, Network, Tasks, Events>(
events: Events, events: Events,
) -> Result<RpcServerHandle, RpcError> ) -> Result<RpcServerHandle, RpcError>
where where
Provider: BlockReaderIdExt + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static, Provider: BlockReaderIdExt
+ StateProviderFactory
+ EvmEnvProvider
+ ChainSpecProvider
+ Clone
+ Unpin
+ 'static,
Pool: TransactionPool + Clone + 'static, Pool: TransactionPool + Clone + 'static,
Network: NetworkInfo + Peers + Clone + 'static, Network: NetworkInfo + Peers + Clone + 'static,
Tasks: TaskSpawner + Clone + 'static, Tasks: TaskSpawner + Clone + 'static,
@ -268,7 +275,13 @@ impl<Provider, Pool, Network, Tasks, Events>
impl<Provider, Pool, Network, Tasks, Events> impl<Provider, Pool, Network, Tasks, Events>
RpcModuleBuilder<Provider, Pool, Network, Tasks, Events> RpcModuleBuilder<Provider, Pool, Network, Tasks, Events>
where where
Provider: BlockReaderIdExt + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static, Provider: BlockReaderIdExt
+ StateProviderFactory
+ EvmEnvProvider
+ ChainSpecProvider
+ Clone
+ Unpin
+ 'static,
Pool: TransactionPool + Clone + 'static, Pool: TransactionPool + Clone + 'static,
Network: NetworkInfo + Peers + Clone + 'static, Network: NetworkInfo + Peers + Clone + 'static,
Tasks: TaskSpawner + Clone + 'static, Tasks: TaskSpawner + Clone + 'static,
@ -485,8 +498,13 @@ impl RpcModuleSelection {
config: RpcModuleConfig, config: RpcModuleConfig,
) -> RpcModule<()> ) -> RpcModule<()>
where where
Provider: Provider: BlockReaderIdExt
BlockReaderIdExt + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static, + StateProviderFactory
+ EvmEnvProvider
+ ChainSpecProvider
+ Clone
+ Unpin
+ 'static,
Pool: TransactionPool + Clone + 'static, Pool: TransactionPool + Clone + 'static,
Network: NetworkInfo + Peers + Clone + 'static, Network: NetworkInfo + Peers + Clone + 'static,
Tasks: TaskSpawner + Clone + 'static, Tasks: TaskSpawner + Clone + 'static,
@ -692,7 +710,13 @@ where
impl<Provider, Pool, Network, Tasks, Events> impl<Provider, Pool, Network, Tasks, Events>
RethModuleRegistry<Provider, Pool, Network, Tasks, Events> RethModuleRegistry<Provider, Pool, Network, Tasks, Events>
where where
Provider: BlockReaderIdExt + StateProviderFactory + EvmEnvProvider + Clone + Unpin + 'static, Provider: BlockReaderIdExt
+ StateProviderFactory
+ EvmEnvProvider
+ ChainSpecProvider
+ Clone
+ Unpin
+ 'static,
Pool: TransactionPool + Clone + 'static, Pool: TransactionPool + Clone + 'static,
Network: NetworkInfo + Peers + Clone + 'static, Network: NetworkInfo + Peers + Clone + 'static,
Tasks: TaskSpawner + Clone + 'static, Tasks: TaskSpawner + Clone + 'static,

View File

@ -23,6 +23,7 @@ reth-rpc-engine-api = { path = "../rpc-engine-api" }
reth-revm = { path = "../../revm" } reth-revm = { path = "../../revm" }
reth-tasks = { workspace = true } reth-tasks = { workspace = true }
reth-metrics = { workspace = true } reth-metrics = { workspace = true }
reth-consensus-common = { path = "../../consensus/common" }
# eth # eth
revm = { workspace = true, features = [ revm = { workspace = true, features = [

View File

@ -11,8 +11,11 @@ use crate::{
}; };
use async_trait::async_trait; use async_trait::async_trait;
use jsonrpsee::core::RpcResult as Result; use jsonrpsee::core::RpcResult as Result;
use reth_primitives::{BlockId, BlockNumberOrTag, Bytes, H256}; use reth_consensus_common::calc::{base_block_reward, block_reward};
use reth_provider::{BlockReader, EvmEnvProvider, StateProviderBox, StateProviderFactory}; use reth_primitives::{BlockId, BlockNumberOrTag, Bytes, SealedHeader, H256, U256};
use reth_provider::{
BlockReader, ChainSpecProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory,
};
use reth_revm::{ use reth_revm::{
database::{State, SubState}, database::{State, SubState},
env::tx_env_with_recovered, env::tx_env_with_recovered,
@ -77,7 +80,7 @@ impl<Provider, Eth> TraceApi<Provider, Eth> {
impl<Provider, Eth> TraceApi<Provider, Eth> impl<Provider, Eth> TraceApi<Provider, Eth>
where where
Provider: BlockReader + StateProviderFactory + EvmEnvProvider + 'static, Provider: BlockReader + StateProviderFactory + EvmEnvProvider + ChainSpecProvider + 'static,
Eth: EthTransactions + 'static, Eth: EthTransactions + 'static,
{ {
/// Executes the future on a new blocking task. /// Executes the future on a new blocking task.
@ -396,7 +399,7 @@ where
&self, &self,
block_id: BlockId, block_id: BlockId,
) -> EthResult<Option<Vec<LocalizedTransactionTrace>>> { ) -> EthResult<Option<Vec<LocalizedTransactionTrace>>> {
let traces = self let mut traces: Option<Vec<LocalizedTransactionTrace>> = self
.trace_block_with( .trace_block_with(
block_id, block_id,
TracingInspectorConfig::default_parity(), TracingInspectorConfig::default_parity(),
@ -408,6 +411,43 @@ where
) )
.await? .await?
.map(|traces| traces.into_iter().flatten().collect()); .map(|traces| traces.into_iter().flatten().collect());
// Add block reward traces
// TODO: We only really need the header and ommers here to determine the reward
if let (Some(block), Some(traces)) =
(self.inner.eth_api.block_by_id(block_id).await?, traces.as_mut())
{
if let Some(header_td) = self.provider().header_td(&block.header.hash)? {
if let Some(base_block_reward) = base_block_reward(
self.provider().chain_spec().as_ref(),
block.header.number,
block.header.difficulty,
header_td,
) {
traces.push(reward_trace(
&block.header,
RewardAction {
author: block.header.beneficiary,
reward_type: RewardType::Block,
value: U256::from(base_block_reward),
},
));
if !block.ommers.is_empty() {
traces.push(reward_trace(
&block.header,
RewardAction {
author: block.header.beneficiary,
reward_type: RewardType::Uncle,
value: block_reward(base_block_reward, block.ommers.len()) -
U256::from(base_block_reward),
},
));
}
}
}
}
Ok(traces) Ok(traces)
} }
@ -448,7 +488,7 @@ where
#[async_trait] #[async_trait]
impl<Provider, Eth> TraceApiServer for TraceApi<Provider, Eth> impl<Provider, Eth> TraceApiServer for TraceApi<Provider, Eth>
where where
Provider: BlockReader + StateProviderFactory + EvmEnvProvider + 'static, Provider: BlockReader + StateProviderFactory + EvmEnvProvider + ChainSpecProvider + 'static,
Eth: EthTransactions + 'static, Eth: EthTransactions + 'static,
{ {
/// Executes the given call and returns a number of possible traces for it. /// Executes the given call and returns a number of possible traces for it.
@ -581,3 +621,20 @@ fn tracing_config(trace_types: &HashSet<TraceType>) -> TracingInspectorConfig {
.set_state_diffs(trace_types.contains(&TraceType::StateDiff)) .set_state_diffs(trace_types.contains(&TraceType::StateDiff))
.set_steps(trace_types.contains(&TraceType::VmTrace)) .set_steps(trace_types.contains(&TraceType::VmTrace))
} }
/// Helper to construct a [`LocalizedTransactionTrace`] that describes a reward to the block
/// beneficiary.
fn reward_trace(header: &SealedHeader, reward: RewardAction) -> LocalizedTransactionTrace {
LocalizedTransactionTrace {
block_hash: Some(header.hash),
block_number: Some(header.number),
transaction_hash: None,
transaction_position: None,
trace: TransactionTrace {
trace_address: vec![],
subtraces: 0,
action: Action::Reward(reward),
result: None,
},
}
}

View File

@ -24,11 +24,12 @@ pub use traits::{
AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockHashReader, AccountExtReader, AccountReader, BlockExecutionWriter, BlockExecutor, BlockHashReader,
BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, BlockWriter, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, BlockWriter,
BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotification, BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotification,
CanonStateNotificationSender, CanonStateNotifications, CanonStateSubscriptions, EvmEnvProvider, CanonStateNotificationSender, CanonStateNotifications, CanonStateSubscriptions,
ExecutorFactory, HashingWriter, HeaderProvider, HistoryWriter, PostStateDataProvider, ChainSpecProvider, EvmEnvProvider, ExecutorFactory, HashingWriter, HeaderProvider,
ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StageCheckpointWriter, HistoryWriter, PostStateDataProvider, ReceiptProvider, ReceiptProviderIdExt,
StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, StorageReader, StageCheckpointReader, StageCheckpointWriter, StateProvider, StateProviderBox,
TransactionsProvider, WithdrawalsProvider, StateProviderFactory, StateRootProvider, StorageReader, TransactionsProvider,
WithdrawalsProvider,
}; };
/// Provider trait implementations. /// Provider trait implementations.

View File

@ -1,8 +1,9 @@
use crate::{ use crate::{
providers::state::{historical::HistoricalStateProvider, latest::LatestStateProvider}, providers::state::{historical::HistoricalStateProvider, latest::LatestStateProvider},
traits::{BlockSource, ReceiptProvider}, traits::{BlockSource, ReceiptProvider},
BlockHashReader, BlockNumReader, BlockReader, EvmEnvProvider, HeaderProvider, ProviderError, BlockHashReader, BlockNumReader, BlockReader, ChainSpecProvider, EvmEnvProvider,
StageCheckpointReader, StateProviderBox, TransactionsProvider, WithdrawalsProvider, HeaderProvider, ProviderError, StageCheckpointReader, StateProviderBox, TransactionsProvider,
WithdrawalsProvider,
}; };
use reth_db::{database::Database, init_db, models::StoredBlockBodyIndices, DatabaseEnv}; use reth_db::{database::Database, init_db, models::StoredBlockBodyIndices, DatabaseEnv};
use reth_interfaces::Result; use reth_interfaces::Result;
@ -343,6 +344,15 @@ impl<DB: Database> EvmEnvProvider for ProviderFactory<DB> {
} }
} }
impl<DB> ChainSpecProvider for ProviderFactory<DB>
where
DB: Send + Sync,
{
fn chain_spec(&self) -> Arc<ChainSpec> {
self.chain_spec.clone()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ProviderFactory; use super::ProviderFactory;

View File

@ -1,9 +1,10 @@
use crate::{ use crate::{
BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotifications, BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotifications,
CanonStateSubscriptions, EvmEnvProvider, HeaderProvider, PostStateDataProvider, ProviderError, CanonStateSubscriptions, ChainSpecProvider, EvmEnvProvider, HeaderProvider,
ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateProviderBox, PostStateDataProvider, ProviderError, ReceiptProvider, ReceiptProviderIdExt,
StateProviderFactory, TransactionsProvider, WithdrawalsProvider, StageCheckpointReader, StateProviderBox, StateProviderFactory, TransactionsProvider,
WithdrawalsProvider,
}; };
use reth_db::{database::Database, models::StoredBlockBodyIndices}; use reth_db::{database::Database, models::StoredBlockBodyIndices};
use reth_interfaces::{ use reth_interfaces::{
@ -14,7 +15,7 @@ use reth_interfaces::{
use reth_primitives::{ use reth_primitives::{
stage::{StageCheckpoint, StageId}, stage::{StageCheckpoint, StageId},
Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumber, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumber,
BlockNumberOrTag, BlockWithSenders, ChainInfo, Header, Receipt, SealedBlock, BlockNumberOrTag, BlockWithSenders, ChainInfo, ChainSpec, Header, Receipt, SealedBlock,
SealedBlockWithSenders, SealedHeader, TransactionMeta, TransactionSigned, SealedBlockWithSenders, SealedHeader, TransactionMeta, TransactionSigned,
TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, H256, U256, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, H256, U256,
}; };
@ -26,6 +27,7 @@ pub use state::{
use std::{ use std::{
collections::{BTreeMap, HashSet}, collections::{BTreeMap, HashSet},
ops::RangeBounds, ops::RangeBounds,
sync::Arc,
time::Instant, time::Instant,
}; };
use tracing::trace; use tracing::trace;
@ -431,6 +433,16 @@ where
} }
} }
impl<DB, Tree> ChainSpecProvider for BlockchainProvider<DB, Tree>
where
DB: Send + Sync,
Tree: Send + Sync,
{
fn chain_spec(&self) -> Arc<ChainSpec> {
self.database.chain_spec()
}
}
impl<DB, Tree> StateProviderFactory for BlockchainProvider<DB, Tree> impl<DB, Tree> StateProviderFactory for BlockchainProvider<DB, Tree>
where where
DB: Database, DB: Database,

View File

@ -1,26 +1,32 @@
use crate::{ use crate::{
traits::{BlockSource, ReceiptProvider}, traits::{BlockSource, ReceiptProvider},
AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
EvmEnvProvider, HeaderProvider, PostState, ReceiptProviderIdExt, StageCheckpointReader, ChainSpecProvider, EvmEnvProvider, HeaderProvider, PostState, ReceiptProviderIdExt,
StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, TransactionsProvider, StageCheckpointReader, StateProvider, StateProviderBox, StateProviderFactory,
WithdrawalsProvider, StateRootProvider, TransactionsProvider, WithdrawalsProvider,
}; };
use reth_db::models::StoredBlockBodyIndices; use reth_db::models::StoredBlockBodyIndices;
use reth_interfaces::Result; use reth_interfaces::Result;
use reth_primitives::{ use reth_primitives::{
stage::{StageCheckpoint, StageId}, stage::{StageCheckpoint, StageId},
Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode, Bytes, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode, Bytes,
ChainInfo, Header, Receipt, SealedBlock, SealedHeader, StorageKey, StorageValue, ChainInfo, ChainSpec, Header, Receipt, SealedBlock, SealedHeader, StorageKey, StorageValue,
TransactionMeta, TransactionSigned, TxHash, TxNumber, H256, KECCAK_EMPTY, U256, TransactionMeta, TransactionSigned, TxHash, TxNumber, H256, KECCAK_EMPTY, MAINNET, U256,
}; };
use reth_revm_primitives::primitives::{BlockEnv, CfgEnv}; use reth_revm_primitives::primitives::{BlockEnv, CfgEnv};
use std::ops::RangeBounds; use std::{ops::RangeBounds, sync::Arc};
/// Supports various api interfaces for testing purposes. /// Supports various api interfaces for testing purposes.
#[derive(Debug, Clone, Default, Copy)] #[derive(Debug, Clone, Default, Copy)]
#[non_exhaustive] #[non_exhaustive]
pub struct NoopProvider; pub struct NoopProvider;
impl ChainSpecProvider for NoopProvider {
fn chain_spec(&self) -> Arc<ChainSpec> {
MAINNET.clone()
}
}
/// Noop implementation for testing purposes /// Noop implementation for testing purposes
impl BlockHashReader for NoopProvider { impl BlockHashReader for NoopProvider {
fn block_hash(&self, _number: u64) -> Result<Option<H256>> { fn block_hash(&self, _number: u64) -> Result<Option<H256>> {

View File

@ -48,6 +48,9 @@ pub use chain::{
CanonStateSubscriptions, CanonStateSubscriptions,
}; };
mod spec;
pub use spec::ChainSpecProvider;
mod stage_checkpoint; mod stage_checkpoint;
pub use stage_checkpoint::{StageCheckpointReader, StageCheckpointWriter}; pub use stage_checkpoint::{StageCheckpointReader, StageCheckpointWriter};

View File

@ -0,0 +1,8 @@
use reth_primitives::ChainSpec;
use std::sync::Arc;
/// A trait for reading the current chainspec.
pub trait ChainSpecProvider: Send + Sync {
/// Get an [`Arc`] to the chainspec.
fn chain_spec(&self) -> Arc<ChainSpec>;
}