mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
Feat: add signers (#6826)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -6461,6 +6461,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"dyn-clone",
|
||||
"futures",
|
||||
"http",
|
||||
"http-body",
|
||||
@ -6468,6 +6469,7 @@ dependencies = [
|
||||
"jsonrpsee",
|
||||
"jsonwebtoken",
|
||||
"metrics",
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
|
||||
@ -237,6 +237,7 @@ once_cell = "1.17"
|
||||
syn = "2.0"
|
||||
nybbles = "0.2.1"
|
||||
smallvec = "1"
|
||||
dyn-clone = "1.0.17"
|
||||
|
||||
# proc-macros
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
@ -559,6 +559,7 @@ where
|
||||
// Configure the pipeline
|
||||
let (mut pipeline, client) = if config.dev.dev {
|
||||
info!(target: "reth::cli", "Starting Reth in dev mode");
|
||||
|
||||
for (idx, (address, alloc)) in config.chain.genesis.alloc.iter().enumerate() {
|
||||
info!(target: "reth::cli", "Allocated Genesis Account: {:02}. {} ({} ETH)", idx, address.to_string(), format_ether(alloc.balance));
|
||||
}
|
||||
@ -691,7 +692,7 @@ where
|
||||
|
||||
// Start RPC servers
|
||||
|
||||
let (rpc_server_handles, rpc_registry) = crate::rpc::launch_rpc_servers(
|
||||
let (rpc_server_handles, mut rpc_registry) = crate::rpc::launch_rpc_servers(
|
||||
node_components.clone(),
|
||||
engine_api,
|
||||
&config,
|
||||
@ -700,6 +701,11 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
// in dev mode we generate 20 random dev-signer accounts
|
||||
if config.dev.dev {
|
||||
rpc_registry.eth_api().with_dev_accounts();
|
||||
}
|
||||
|
||||
// Run consensus engine to completion
|
||||
let (tx, rx) = oneshot::channel();
|
||||
info!(target: "reth::cli", "Starting consensus engine");
|
||||
|
||||
@ -27,7 +27,7 @@ pub trait EthApi {
|
||||
|
||||
/// Returns a list of addresses owned by client.
|
||||
#[method(name = "accounts")]
|
||||
async fn accounts(&self) -> RpcResult<Vec<Address>>;
|
||||
fn accounts(&self) -> RpcResult<Vec<Address>>;
|
||||
|
||||
/// Returns the number of most recent block.
|
||||
#[method(name = "blockNumber")]
|
||||
|
||||
@ -59,6 +59,7 @@ tokio = { workspace = true, features = ["sync"] }
|
||||
tower = "0.4"
|
||||
tokio-stream = { workspace = true, features = ["sync"] }
|
||||
pin-project.workspace = true
|
||||
parking_lot.workspace = true
|
||||
|
||||
# metrics
|
||||
reth-metrics.workspace = true
|
||||
@ -80,6 +81,7 @@ tracing-futures = "0.2"
|
||||
schnellru.workspace = true
|
||||
futures.workspace = true
|
||||
derive_more.workspace = true
|
||||
dyn-clone.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
jsonrpsee = { workspace = true, features = ["client"] }
|
||||
|
||||
@ -39,6 +39,7 @@ use tokio::sync::{oneshot, Mutex};
|
||||
mod block;
|
||||
mod call;
|
||||
pub(crate) mod fee_history;
|
||||
|
||||
mod fees;
|
||||
#[cfg(feature = "optimism")]
|
||||
mod optimism;
|
||||
@ -144,7 +145,7 @@ where
|
||||
provider,
|
||||
pool,
|
||||
network,
|
||||
signers: Default::default(),
|
||||
signers: parking_lot::RwLock::new(Default::default()),
|
||||
eth_cache,
|
||||
gas_oracle,
|
||||
gas_cap,
|
||||
@ -404,7 +405,7 @@ where
|
||||
}
|
||||
|
||||
fn accounts(&self) -> Vec<Address> {
|
||||
self.inner.signers.iter().flat_map(|s| s.accounts()).collect()
|
||||
self.inner.signers.read().iter().flat_map(|s| s.accounts()).collect()
|
||||
}
|
||||
|
||||
fn is_syncing(&self) -> bool {
|
||||
@ -469,7 +470,7 @@ struct EthApiInner<Provider, Pool, Network, EvmConfig> {
|
||||
/// An interface to interact with the network
|
||||
network: Network,
|
||||
/// All configured Signers
|
||||
signers: Vec<Box<dyn EthSigner>>,
|
||||
signers: parking_lot::RwLock<Vec<Box<dyn EthSigner>>>,
|
||||
/// The async cache frontend for eth related data
|
||||
eth_cache: EthStateCache,
|
||||
/// The async gas oracle frontend for gas price suggestions
|
||||
|
||||
@ -65,7 +65,7 @@ where
|
||||
}
|
||||
|
||||
/// Handler for: `eth_accounts`
|
||||
async fn accounts(&self) -> Result<Vec<Address>> {
|
||||
fn accounts(&self) -> Result<Vec<Address>> {
|
||||
trace!(target: "rpc::eth", "Serving eth_accounts");
|
||||
Ok(EthApiSpec::accounts(self))
|
||||
}
|
||||
@ -408,7 +408,7 @@ where
|
||||
/// Handler for: `eth_signTypedData`
|
||||
async fn sign_typed_data(&self, address: Address, data: Value) -> Result<Bytes> {
|
||||
trace!(target: "rpc::eth", ?address, ?data, "Serving eth_signTypedData");
|
||||
Ok(EthApi::sign_typed_data(self, data, address).await?)
|
||||
Ok(EthApi::sign_typed_data(self, data, address)?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getProof`
|
||||
|
||||
@ -3,21 +3,20 @@
|
||||
use crate::{
|
||||
eth::{
|
||||
error::{EthResult, SignError},
|
||||
signer::EthSigner,
|
||||
signer::{DevSigner, EthSigner},
|
||||
},
|
||||
EthApi,
|
||||
};
|
||||
use alloy_dyn_abi::TypedData;
|
||||
use reth_primitives::{Address, Bytes};
|
||||
use serde_json::Value;
|
||||
use std::ops::Deref;
|
||||
|
||||
impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConfig> {
|
||||
pub(crate) async fn sign(&self, account: Address, message: Bytes) -> EthResult<Bytes> {
|
||||
Ok(self.find_signer(&account)?.sign(account, &message).await?.to_hex_bytes())
|
||||
}
|
||||
|
||||
pub(crate) async fn sign_typed_data(&self, data: Value, account: Address) -> EthResult<Bytes> {
|
||||
pub(crate) fn sign_typed_data(&self, data: Value, account: Address) -> EthResult<Bytes> {
|
||||
Ok(self
|
||||
.find_signer(&account)?
|
||||
.sign_typed_data(
|
||||
@ -31,12 +30,20 @@ impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConf
|
||||
pub(crate) fn find_signer(
|
||||
&self,
|
||||
account: &Address,
|
||||
) -> Result<&(dyn EthSigner + 'static), SignError> {
|
||||
) -> Result<Box<(dyn EthSigner + 'static)>, SignError> {
|
||||
self.inner
|
||||
.signers
|
||||
.read()
|
||||
.iter()
|
||||
.find(|signer| signer.is_signer_for(account))
|
||||
.map(|signer| signer.deref())
|
||||
.map(|signer| dyn_clone::clone_box(&**signer))
|
||||
.ok_or(SignError::NoAccount)
|
||||
}
|
||||
|
||||
/// Generates 20 random developer accounts.
|
||||
/// Used in DEV mode.
|
||||
pub fn with_dev_accounts(&mut self) {
|
||||
let mut signers = self.inner.signers.write();
|
||||
*signers = DevSigner::random_signers(20);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1340,7 +1340,7 @@ where
|
||||
from: &Address,
|
||||
request: TypedTransactionRequest,
|
||||
) -> EthResult<TransactionSigned> {
|
||||
for signer in self.inner.signers.iter() {
|
||||
for signer in self.inner.signers.read().iter() {
|
||||
if signer.is_signer_for(from) {
|
||||
return match signer.sign_transaction(request, from) {
|
||||
Ok(tx) => Ok(tx),
|
||||
|
||||
@ -7,6 +7,7 @@ use reth_primitives::{
|
||||
};
|
||||
use reth_rpc_types::TypedTransactionRequest;
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
use reth_rpc_types_compat::transaction::to_primitive_transaction;
|
||||
use secp256k1::SecretKey;
|
||||
use std::collections::HashMap;
|
||||
@ -15,7 +16,7 @@ type Result<T> = std::result::Result<T, SignError>;
|
||||
|
||||
/// An Ethereum Signer used via RPC.
|
||||
#[async_trait::async_trait]
|
||||
pub(crate) trait EthSigner: Send + Sync {
|
||||
pub(crate) trait EthSigner: Send + Sync + DynClone {
|
||||
/// Returns the available accounts for this signer.
|
||||
fn accounts(&self) -> Vec<Address>;
|
||||
|
||||
@ -38,13 +39,38 @@ pub(crate) trait EthSigner: Send + Sync {
|
||||
fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature>;
|
||||
}
|
||||
|
||||
dyn_clone::clone_trait_object!(EthSigner);
|
||||
|
||||
/// Holds developer keys
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DevSigner {
|
||||
addresses: Vec<Address>,
|
||||
accounts: HashMap<Address, SecretKey>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl DevSigner {
|
||||
/// Generates a random dev signer which satisfies [EthSigner] trait
|
||||
pub(crate) fn random() -> Box<dyn EthSigner> {
|
||||
let mut signers = Self::random_signers(1);
|
||||
signers.pop().expect("expect to generate at leas one signer")
|
||||
}
|
||||
|
||||
/// Generates provided number of random dev signers
|
||||
/// which satisfy [EthSigner] trait
|
||||
pub(crate) fn random_signers(num: u32) -> Vec<Box<dyn EthSigner + 'static>> {
|
||||
let mut signers = Vec::new();
|
||||
for _ in 0..num {
|
||||
let (sk, pk) = secp256k1::generate_keypair(&mut rand::thread_rng());
|
||||
|
||||
let address = reth_primitives::public_key_to_address(pk);
|
||||
let addresses = vec![address];
|
||||
let accounts = HashMap::from([(address, sk)]);
|
||||
signers.push(Box::new(DevSigner { addresses, accounts }) as Box<dyn EthSigner>);
|
||||
}
|
||||
signers
|
||||
}
|
||||
|
||||
fn get_key(&self, account: Address) -> Result<&SecretKey> {
|
||||
self.accounts.get(&account).ok_or(SignError::NoAccount)
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ metrics.workspace = true
|
||||
# misc
|
||||
tracing.workspace = true
|
||||
thiserror.workspace = true
|
||||
dyn-clone = "1.0"
|
||||
dyn-clone.workspace = true
|
||||
|
||||
# feature `rayon`
|
||||
rayon = { workspace = true, optional = true }
|
||||
|
||||
Reference in New Issue
Block a user