Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/node/builder/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use reth_node_core::{
version::{version_metadata, CLIENT_CODE},
};
use reth_payload_builder::{PayloadBuilderHandle, PayloadStore};
use reth_rpc::eth::{core::EthRpcConverterFor, EthApiTypes, FullEthApiServer};
use reth_rpc_api::{eth::helpers::AddDevSigners, IntoEngineApiRpcModule};
use reth_rpc::eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer};
use reth_rpc_api::{eth::helpers::EthTransactions, IntoEngineApiRpcModule};
use reth_rpc_builder::{
auth::{AuthRpcModule, AuthServerHandle},
config::RethRpcServerConfig,
Expand Down Expand Up @@ -991,7 +991,8 @@ where

// in dev mode we generate 20 random dev-signer accounts
if config.dev.dev {
registry.eth_api().with_dev_accounts();
let signers = DevSigner::from_mnemonic(config.dev.dev_mnemonic.as_str(), 20);
registry.eth_api().signers().write().extend(signers);
}

let mut registry = RpcRegistry { registry };
Expand Down Expand Up @@ -1163,7 +1164,6 @@ pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
/// The Ethapi implementation this builder will build.
type EthApi: EthApiTypes
+ FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
+ AddDevSigners
+ Unpin
+ 'static;

Expand Down
69 changes: 63 additions & 6 deletions crates/node/core/src/args/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use std::time::Duration;
use clap::Args;
use humantime::parse_duration;

const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";

/// Parameters for Dev testnet configuration
#[derive(Debug, Args, PartialEq, Eq, Default, Clone, Copy)]
#[derive(Debug, Args, PartialEq, Eq, Clone)]
#[command(next_help_heading = "Dev testnet")]
pub struct DevArgs {
/// Start the node in dev mode
Expand Down Expand Up @@ -39,6 +41,28 @@ pub struct DevArgs {
verbatim_doc_comment
)]
pub block_time: Option<Duration>,

/// Derive dev accounts from a fixed mnemonic instead of random ones.
#[arg(
long = "dev.mnemonic",
help_heading = "Dev testnet",
value_name = "MNEMONIC",
requires = "dev",
verbatim_doc_comment,
default_value = DEFAULT_MNEMONIC
)]
pub dev_mnemonic: String,
}

impl Default for DevArgs {
fn default() -> Self {
Self {
dev: false,
block_max_transactions: None,
block_time: None,
dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
}
}
}

#[cfg(test)]
Expand All @@ -56,13 +80,37 @@ mod tests {
#[test]
fn test_parse_dev_args() {
let args = CommandParser::<DevArgs>::parse_from(["reth"]).args;
assert_eq!(args, DevArgs { dev: false, block_max_transactions: None, block_time: None });
assert_eq!(
args,
DevArgs {
dev: false,
block_max_transactions: None,
block_time: None,
dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
}
);

let args = CommandParser::<DevArgs>::parse_from(["reth", "--dev"]).args;
assert_eq!(args, DevArgs { dev: true, block_max_transactions: None, block_time: None });
assert_eq!(
args,
DevArgs {
dev: true,
block_max_transactions: None,
block_time: None,
dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
}
);

let args = CommandParser::<DevArgs>::parse_from(["reth", "--auto-mine"]).args;
assert_eq!(args, DevArgs { dev: true, block_max_transactions: None, block_time: None });
assert_eq!(
args,
DevArgs {
dev: true,
block_max_transactions: None,
block_time: None,
dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
}
);

let args = CommandParser::<DevArgs>::parse_from([
"reth",
Expand All @@ -71,7 +119,15 @@ mod tests {
"2",
])
.args;
assert_eq!(args, DevArgs { dev: true, block_max_transactions: Some(2), block_time: None });
assert_eq!(
args,
DevArgs {
dev: true,
block_max_transactions: Some(2),
block_time: None,
dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
}
);

let args =
CommandParser::<DevArgs>::parse_from(["reth", "--dev", "--dev.block-time", "1s"]).args;
Expand All @@ -80,7 +136,8 @@ mod tests {
DevArgs {
dev: true,
block_max_transactions: None,
block_time: Some(std::time::Duration::from_secs(1))
block_time: Some(std::time::Duration::from_secs(1)),
dev_mnemonic: DEFAULT_MNEMONIC.to_string(),
}
);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/node/core/src/node_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl<ChainSpec> NodeConfig<ChainSpec> {
}

/// Set the dev args for the node
pub const fn with_dev(mut self, dev: DevArgs) -> Self {
pub fn with_dev(mut self, dev: DevArgs) -> Self {
self.dev = dev;
self
}
Expand Down Expand Up @@ -519,7 +519,7 @@ impl<ChainSpec> Clone for NodeConfig<ChainSpec> {
builder: self.builder.clone(),
debug: self.debug.clone(),
db: self.db,
dev: self.dev,
dev: self.dev.clone(),
pruning: self.pruning.clone(),
datadir: self.datadir.clone(),
engine: self.engine.clone(),
Expand Down
24 changes: 6 additions & 18 deletions crates/optimism/rpc/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ use reth_optimism_flashblocks::{
ExecutionPayloadBaseV1, FlashBlockBuildInfo, FlashBlockCompleteSequenceRx, FlashBlockService,
InProgressFlashBlockRx, PendingBlockRx, PendingFlashBlock, WsFlashBlockStream,
};
use reth_rpc::eth::{core::EthApiInner, DevSigner};
use reth_rpc::eth::core::EthApiInner;
use reth_rpc_eth_api::{
helpers::{
pending_block::BuildPendingEnv, AddDevSigners, EthApiSpec, EthFees, EthState, LoadFee,
LoadPendingBlock, LoadState, SpawnBlocking, Trace,
pending_block::BuildPendingEnv, EthApiSpec, EthFees, EthState, LoadFee, LoadPendingBlock,
LoadState, SpawnBlocking, Trace,
},
EthApiTypes, FromEvmError, FullEthApiServer, RpcConvert, RpcConverter, RpcNodeCore,
RpcNodeCoreExt, RpcTypes, SignableTxRequest,
RpcNodeCoreExt, RpcTypes,
};
use reth_rpc_eth_types::{
EthStateCache, FeeHistoryCache, GasPriceOracle, PendingBlock, PendingBlockEnvOrigin,
};
use reth_storage_api::{ProviderHeader, ProviderTx};
use reth_storage_api::ProviderHeader;
use reth_tasks::{
pool::{BlockingTaskGuard, BlockingTaskPool},
TaskSpawner,
Expand Down Expand Up @@ -335,18 +335,6 @@ where
{
}

impl<N, Rpc> AddDevSigners for OpEthApi<N, Rpc>
where
N: RpcNodeCore,
Rpc: RpcConvert<
Network: RpcTypes<TransactionRequest: SignableTxRequest<ProviderTx<N::Provider>>>,
>,
{
fn with_dev_accounts(&self) {
*self.inner.eth_api.signers().write() = DevSigner::random_signers(20)
}
}

impl<N: RpcNodeCore, Rpc: RpcConvert> fmt::Debug for OpEthApi<N, Rpc> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OpEthApi").finish_non_exhaustive()
Expand Down Expand Up @@ -483,7 +471,7 @@ where
NetworkT: RpcTypes,
OpRpcConvert<N, NetworkT>: RpcConvert<Network = NetworkT>,
OpEthApi<N, OpRpcConvert<N, NetworkT>>:
FullEthApiServer<Provider = N::Provider, Pool = N::Pool> + AddDevSigners,
FullEthApiServer<Provider = N::Provider, Pool = N::Pool>,
{
type EthApi = OpEthApi<N, OpRpcConvert<N, NetworkT>>;

Expand Down
6 changes: 3 additions & 3 deletions crates/rpc/rpc-convert/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::{
fees::{CallFees, CallFeesError},
RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes,
RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes, SignableTxRequest,
};
use alloy_consensus::{
error::ValueError, transaction::Recovered, EthereumTxEnvelope, Sealable, TxEip4844,
Expand Down Expand Up @@ -128,7 +128,7 @@ pub trait RpcConvert: Send + Sync + Unpin + Debug + DynClone + 'static {

/// Associated upper layer JSON-RPC API network requests and responses to convert from and into
/// types of [`Self::Primitives`].
type Network: RpcTypes + Send + Sync + Unpin + Clone + Debug;
type Network: RpcTypes<TransactionRequest: SignableTxRequest<TxTy<Self::Primitives>>>;

/// An associated RPC conversion error.
type Error: error::Error + Into<jsonrpsee_types::ErrorObject<'static>>;
Expand Down Expand Up @@ -901,7 +901,7 @@ impl<N, Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> RpcConvert
for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
where
N: NodePrimitives,
Network: RpcTypes + Send + Sync + Unpin + Clone + Debug,
Network: RpcTypes<TransactionRequest: SignableTxRequest<N::SignedTx>>,
Evm: ConfigureEvm<Primitives = N> + 'static,
Receipt: ReceiptConverter<
N,
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc/rpc-eth-api/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub use call::{Call, EthCall};
pub use fee::{EthFees, LoadFee};
pub use pending_block::LoadPendingBlock;
pub use receipt::LoadReceipt;
pub use signer::{AddDevSigners, EthSigner};
pub use signer::EthSigner;
pub use spec::EthApiSpec;
pub use state::{EthState, LoadState};
pub use trace::Trace;
Expand Down
8 changes: 0 additions & 8 deletions crates/rpc/rpc-eth-api/src/helpers/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,3 @@ pub trait EthSigner<T, TxReq = TransactionRequest>: Send + Sync + DynClone {
}

dyn_clone::clone_trait_object!(<T> EthSigner<T>);

/// Adds 20 random dev signers for access via the API. Used in dev mode.
#[auto_impl::auto_impl(&)]
pub trait AddDevSigners {
/// Generates 20 random developer accounts.
/// Used in DEV mode.
fn with_dev_accounts(&self);
}
24 changes: 10 additions & 14 deletions crates/rpc/rpc-eth-api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

use crate::{AsEthApiError, FromEthApiError, RpcNodeCore};
use alloy_rpc_types_eth::Block;
use reth_chain_state::CanonStateSubscriptions;
use reth_rpc_convert::RpcConvert;
use reth_rpc_convert::{RpcConvert, SignableTxRequest};
pub use reth_rpc_convert::{RpcTransaction, RpcTxReq, RpcTypes};
use reth_storage_api::{ProviderTx, ReceiptProvider, TransactionsProvider};
use reth_transaction_pool::{PoolTransaction, TransactionPool};
use reth_storage_api::ProviderTx;
use std::{
error::Error,
fmt::{self},
Expand Down Expand Up @@ -52,12 +50,11 @@ pub type RpcError<T> = <T as EthApiTypes>::Error;
/// Helper trait holds necessary trait bounds on [`EthApiTypes`] to implement `eth` API.
pub trait FullEthApiTypes
where
Self: RpcNodeCore<
Provider: TransactionsProvider + ReceiptProvider + CanonStateSubscriptions,
Pool: TransactionPool<
Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>,
Self: RpcNodeCore
+ EthApiTypes<
NetworkTypes: RpcTypes<
TransactionRequest: SignableTxRequest<ProviderTx<Self::Provider>>,
>,
> + EthApiTypes<
RpcConvert: RpcConvert<
Primitives = Self::Primitives,
Network = Self::NetworkTypes,
Expand All @@ -68,12 +65,11 @@ where
}

impl<T> FullEthApiTypes for T where
T: RpcNodeCore<
Provider: TransactionsProvider + ReceiptProvider + CanonStateSubscriptions,
Pool: TransactionPool<
Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>,
T: RpcNodeCore
+ EthApiTypes<
NetworkTypes: RpcTypes<
TransactionRequest: SignableTxRequest<ProviderTx<Self::Provider>>,
>,
> + EthApiTypes<
RpcConvert: RpcConvert<
Primitives = <Self as RpcNodeCore>::Primitives,
Network = Self::NetworkTypes,
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ reth-trie-common.workspace = true
alloy-evm = { workspace = true, features = ["overrides"] }
alloy-consensus.workspace = true
alloy-signer.workspace = true
alloy-signer-local.workspace = true
alloy-signer-local = { workspace = true, features = ["mnemonic"] }
alloy-eips = { workspace = true, features = ["kzg"] }
alloy-dyn-abi.workspace = true
alloy-genesis.workspace = true
Expand Down
55 changes: 31 additions & 24 deletions crates/rpc/rpc/src/eth/helpers/signer.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
//! An abstraction over ethereum signers.

use std::collections::HashMap;

use crate::EthApi;
use alloy_dyn_abi::TypedData;
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::{eip191_hash_message, Address, Signature, B256};
use alloy_signer::SignerSync;
use alloy_signer_local::PrivateKeySigner;
use reth_rpc_convert::{RpcConvert, RpcTypes, SignableTxRequest};
use reth_rpc_eth_api::{
helpers::{signer::Result, AddDevSigners, EthSigner},
FromEvmError, RpcNodeCore,
};
use reth_rpc_eth_types::{EthApiError, SignError};
use reth_storage_api::ProviderTx;

impl<N, Rpc> AddDevSigners for EthApi<N, Rpc>
where
N: RpcNodeCore,
EthApiError: FromEvmError<N::Evm>,
Rpc: RpcConvert<
Network: RpcTypes<TransactionRequest: SignableTxRequest<ProviderTx<N::Provider>>>,
>,
{
fn with_dev_accounts(&self) {
*self.inner.signers().write() = DevSigner::random_signers(20)
}
}
use alloy_signer_local::{coins_bip39::English, MnemonicBuilder, PrivateKeySigner};
use reth_rpc_convert::SignableTxRequest;
use reth_rpc_eth_api::helpers::{signer::Result, EthSigner};
use reth_rpc_eth_types::SignError;
use std::collections::HashMap;

/// Holds developer keys
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -55,6 +36,32 @@ impl DevSigner {
signers
}

/// Generates dev signers deterministically from a fixed mnemonic.
/// Uses the Ethereum derivation path: `m/44'/60'/0'/0/{index}`
pub fn from_mnemonic<T: Decodable2718, TxReq: SignableTxRequest<T>>(
mnemonic: &str,
num: u32,
) -> Vec<Box<dyn EthSigner<T, TxReq> + 'static>> {
let mut signers = Vec::with_capacity(num as usize);

for i in 0..num {
let sk = MnemonicBuilder::<English>::default()
.phrase(mnemonic)
.index(i)
.expect("invalid derivation path")
.build()
.expect("failed to build signer from mnemonic");

let address = sk.address();
let addresses = vec![address];
let accounts = HashMap::from([(address, sk)]);

signers.push(Box::new(Self { addresses, accounts }) as Box<dyn EthSigner<T, TxReq>>);
}

signers
}

fn get_key(&self, account: Address) -> Result<&PrivateKeySigner> {
self.accounts.get(&account).ok_or(SignError::NoAccount)
}
Expand Down
5 changes: 5 additions & 0 deletions docs/vocs/docs/pages/cli/reth/node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,11 @@ Dev testnet:
Parses strings using [`humantime::parse_duration`]
--dev.block-time 12s
--dev.mnemonic <MNEMONIC>
Derive dev accounts from a fixed mnemonic instead of random ones.
[default: "test test test test test test test test test test test junk"]
Pruning:
--full
Run full node. Only the most recent [`MINIMUM_PRUNING_DISTANCE`] block states are stored
Expand Down
Loading