mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): more rpc scaffolding (#99)
* feat(rpc): more rpc scaffolding * use Into<String> * rm unused error
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2568,7 +2568,9 @@ name = "reth-rpc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"hex",
|
||||
"jsonrpsee",
|
||||
"reth-interfaces",
|
||||
"reth-primitives",
|
||||
"reth-rpc-api",
|
||||
"reth-rpc-types",
|
||||
@ -2585,6 +2587,7 @@ dependencies = [
|
||||
"jsonrpsee",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3027,9 +3030,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.85"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
||||
@ -5,7 +5,7 @@ use reth_primitives::{
|
||||
};
|
||||
|
||||
/// Client trait for fetching `Block` related data.
|
||||
pub trait BlockProvider: Send + Sync {
|
||||
pub trait BlockProvider: Send + Sync + 'static {
|
||||
/// Returns the current info for the chain.
|
||||
fn chain_info(&self) -> Result<ChainInfo>;
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::Result;
|
||||
use reth_primitives::{rpc::BlockId, Address, H256, U256};
|
||||
|
||||
/// Provides access to storage data
|
||||
pub trait StorageProvider {
|
||||
pub trait StorageProvider: Send + Sync + 'static {
|
||||
/// Returns the value from a storage position at a given address and `BlockId`
|
||||
fn storage_at(&self, address: Address, index: U256, at: BlockId) -> Result<Option<H256>>;
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ reth-rpc-types = { path = "../rpc-types" }
|
||||
|
||||
# misc
|
||||
jsonrpsee = { version = "0.15", features = ["server", "macros"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[features]
|
||||
client = ["jsonrpsee/client", "jsonrpsee/async-client"]
|
||||
client = ["jsonrpsee/client", "jsonrpsee/async-client"]
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
use jsonrpsee::{
|
||||
core::{RpcResult as Result, __reexports::serde_json},
|
||||
proc_macros::rpc,
|
||||
};
|
||||
use jsonrpsee::{core::RpcResult as Result, proc_macros::rpc};
|
||||
use reth_primitives::{
|
||||
rpc::{transaction::eip2930::AccessListWithGasUsed, BlockId},
|
||||
Address, BlockNumber, Bytes, H256, H64, U256, U64,
|
||||
@ -11,18 +8,18 @@ use reth_rpc_types::{
|
||||
Transaction, TransactionReceipt, TransactionRequest, Work,
|
||||
};
|
||||
|
||||
/// Eth rpc interface.
|
||||
/// Eth rpc interface: <https://ethereum.github.io/execution-apis/api-documentation/>
|
||||
#[cfg_attr(not(feature = "client"), rpc(server))]
|
||||
#[cfg_attr(feature = "client", rpc(server, client))]
|
||||
#[async_trait]
|
||||
pub trait EthApi {
|
||||
/// Returns protocol version encoded as a string (quotes are necessary).
|
||||
/// Returns protocol version encoded as a string.
|
||||
#[method(name = "eth_protocolVersion")]
|
||||
async fn protocol_version(&self) -> Result<u64>;
|
||||
fn protocol_version(&self) -> Result<U64>;
|
||||
|
||||
/// Returns an object with data about the sync status or false.
|
||||
#[method(name = "eth_syncing")]
|
||||
async fn syncing(&self) -> Result<SyncStatus>;
|
||||
fn syncing(&self) -> Result<SyncStatus>;
|
||||
|
||||
/// Returns block author.
|
||||
#[method(name = "eth_coinbase")]
|
||||
@ -32,9 +29,9 @@ pub trait EthApi {
|
||||
#[method(name = "eth_accounts")]
|
||||
async fn accounts(&self) -> Result<Vec<Address>>;
|
||||
|
||||
/// Returns highest block number.
|
||||
/// Returns the best block number.
|
||||
#[method(name = "eth_blockNumber")]
|
||||
async fn block_number(&self) -> Result<U256>;
|
||||
fn block_number(&self) -> Result<U256>;
|
||||
|
||||
/// Returns the chain ID used for transaction signing at the
|
||||
/// current best block. None is returned if not
|
||||
|
||||
@ -10,6 +10,7 @@ Reth RPC implementation
|
||||
"""
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-interfaces = { path = "../../interfaces" }
|
||||
reth-primitives = { path = "../../primitives" }
|
||||
reth-rpc-api = { path = "../rpc-api" }
|
||||
reth-rpc-types = { path = "../rpc-types" }
|
||||
@ -23,3 +24,4 @@ async-trait = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
hex = "0.4"
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
//! Implementation for the `eth` namespace rpc request handler.
|
||||
//! Implementation of the [`jsonrpsee`] generated [`reth_rpc_api::EthApiServer`] trait
|
||||
//! implementation for handling RPC requests for he `eth_` namespace.
|
||||
|
||||
use crate::{eth::EthApi, result::ToRpcResult};
|
||||
use jsonrpsee::core::RpcResult as Result;
|
||||
use reth_interfaces::provider::{BlockProvider, StorageProvider};
|
||||
use reth_primitives::{
|
||||
rpc::{transaction::eip2930::AccessListWithGasUsed, BlockId},
|
||||
Address, BlockNumber, Bytes, Transaction, H256, H64, U256, U64,
|
||||
@ -13,25 +16,17 @@ use reth_rpc_types::{
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use serde_json::Value;
|
||||
|
||||
/// `Eth` API implementation.
|
||||
///
|
||||
/// This type serves as the handler for RPCs for the `eth` namespace.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EthApi<Pool> {
|
||||
/// The transaction pool.
|
||||
_pool: Pool,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<Pool> EthApiServer for EthApi<Pool>
|
||||
impl<Pool, Client> EthApiServer for EthApi<Pool, Client>
|
||||
where
|
||||
Pool: TransactionPool<Transaction = Transaction>,
|
||||
Pool: TransactionPool<Transaction = Transaction> + Clone,
|
||||
Client: BlockProvider + StorageProvider,
|
||||
{
|
||||
async fn protocol_version(&self) -> Result<u64> {
|
||||
todo!()
|
||||
fn protocol_version(&self) -> Result<U64> {
|
||||
Ok(self.protocol_version())
|
||||
}
|
||||
|
||||
async fn syncing(&self) -> Result<SyncStatus> {
|
||||
fn syncing(&self) -> Result<SyncStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -43,8 +38,8 @@ where
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn block_number(&self) -> Result<U256> {
|
||||
todo!()
|
||||
fn block_number(&self) -> Result<U256> {
|
||||
self.block_number().with_message("Failed to read block number")
|
||||
}
|
||||
|
||||
async fn chain_id(&self) -> Result<Option<U64>> {
|
||||
64
crates/net/rpc/src/eth/mod.rs
Normal file
64
crates/net/rpc/src/eth/mod.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! Provides everything related to `eth_` namespace
|
||||
|
||||
use reth_interfaces::{
|
||||
provider::{BlockProvider, StorageProvider},
|
||||
Result,
|
||||
};
|
||||
use reth_primitives::{Transaction, U256, U64};
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod eth_server;
|
||||
|
||||
/// `Eth` API implementation.
|
||||
///
|
||||
/// This type provides the functionality for handling `eth_` related requests.
|
||||
/// These are implemented two-fold: Core functionality is implemented as functions directly on this
|
||||
/// type. Additionally, the required server implementations (e.g. [`reth_rpc_api::EthApiServer`])
|
||||
/// are implemented separately in submodules. The rpc handler implementation can then delegate to
|
||||
/// the main impls. This way [`EthApi`] is not limited to [`jsonrpsee`] and can be used standalone
|
||||
/// or in other network handlers (for example ipc).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EthApi<Pool, Client> {
|
||||
/// All nested fields bundled together.
|
||||
inner: Arc<EthApiInner<Pool, Client>>,
|
||||
}
|
||||
|
||||
impl<Pool, Client> EthApi<Pool, Client>
|
||||
where
|
||||
Pool: TransactionPool<Transaction = Transaction> + Clone,
|
||||
Client: BlockProvider + StorageProvider,
|
||||
{
|
||||
/// Creates a new, shareable instance.
|
||||
pub fn new(client: Arc<Client>, pool: Pool) -> Self {
|
||||
let inner = EthApiInner { client, pool };
|
||||
Self { inner: Arc::new(inner) }
|
||||
}
|
||||
|
||||
/// Returns the inner `Client`
|
||||
fn client(&self) -> &Arc<Client> {
|
||||
&self.inner.client
|
||||
}
|
||||
|
||||
/// Returns the current ethereum protocol version.
|
||||
///
|
||||
/// Note: This returns an `U64`, since this should return as hex string.
|
||||
pub fn protocol_version(&self) -> U64 {
|
||||
1u64.into()
|
||||
}
|
||||
|
||||
/// Returns the best block number
|
||||
pub fn block_number(&self) -> Result<U256> {
|
||||
Ok(self.client().chain_info()?.best_number.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Container type `EthApi`
|
||||
#[derive(Debug)]
|
||||
struct EthApiInner<Pool, Client> {
|
||||
/// The transaction pool.
|
||||
pool: Pool,
|
||||
/// The client that can interact with the chain.
|
||||
client: Arc<Client>,
|
||||
// TODO needs network access to handle things like `eth_syncing`
|
||||
}
|
||||
@ -4,9 +4,15 @@
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
// TODO remove later
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Reth RPC implementation
|
||||
//!
|
||||
//! Provides the implementation of all RPC interfaces.
|
||||
|
||||
pub mod eth;
|
||||
mod eth;
|
||||
|
||||
pub use eth::EthApi;
|
||||
|
||||
pub(crate) mod result;
|
||||
|
||||
134
crates/net/rpc/src/result.rs
Normal file
134
crates/net/rpc/src/result.rs
Normal file
@ -0,0 +1,134 @@
|
||||
//! Additional helpers for converting errors.
|
||||
|
||||
use jsonrpsee::core::{Error as RpcError, RpcResult};
|
||||
|
||||
/// Helper trait to easily convert various `Result` types into [`RpcResult`]
|
||||
pub(crate) trait ToRpcResult<Ok, Err> {
|
||||
/// Converts this type into an [`RpcResult`]
|
||||
fn map_rpc_err<'a, F, M>(self, op: F) -> RpcResult<Ok>
|
||||
where
|
||||
F: FnOnce(Err) -> (i32, M, Option<&'a [u8]>),
|
||||
M: Into<String>;
|
||||
|
||||
/// Converts this type into an [`RpcResult`] with the
|
||||
/// [`jsonrpsee::types::error::INTERNAL_ERROR_CODE` and the given message.
|
||||
fn map_internal_err<F, M>(self, op: F) -> RpcResult<Ok>
|
||||
where
|
||||
F: FnOnce(Err) -> M,
|
||||
M: Into<String>;
|
||||
|
||||
/// Converts this type into an [`RpcResult`] with the
|
||||
/// [`jsonrpsee::types::error::INTERNAL_ERROR_CODE`] and given message and data.
|
||||
fn map_internal_err_with_data<'a, F, M>(self, op: F) -> RpcResult<Ok>
|
||||
where
|
||||
F: FnOnce(Err) -> (M, &'a [u8]),
|
||||
M: Into<String>;
|
||||
|
||||
/// Adds a message to the error variant and returns an internal Error.
|
||||
///
|
||||
/// This is shorthand for `Self::map_internal_err(|err| format!("{msg}: {err}"))`.
|
||||
fn with_message(self, msg: &str) -> RpcResult<Ok>;
|
||||
}
|
||||
|
||||
impl<Ok> ToRpcResult<Ok, reth_interfaces::Error> for reth_interfaces::Result<Ok> {
|
||||
#[inline]
|
||||
fn map_rpc_err<'a, F, M>(self, op: F) -> RpcResult<Ok>
|
||||
where
|
||||
F: FnOnce(reth_interfaces::Error) -> (i32, M, Option<&'a [u8]>),
|
||||
M: Into<String>,
|
||||
{
|
||||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(err) => {
|
||||
let (code, msg, data) = op(err);
|
||||
Err(rpc_err(code, msg, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map_internal_err<'a, F, M>(self, op: F) -> RpcResult<Ok>
|
||||
where
|
||||
F: FnOnce(reth_interfaces::Error) -> M,
|
||||
M: Into<String>,
|
||||
{
|
||||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(err) => Err(internal_rpc_err(op(err))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map_internal_err_with_data<'a, F, M>(self, op: F) -> RpcResult<Ok>
|
||||
where
|
||||
F: FnOnce(reth_interfaces::Error) -> (M, &'a [u8]),
|
||||
M: Into<String>,
|
||||
{
|
||||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(err) => {
|
||||
let (msg, data) = op(err);
|
||||
Err(internal_rpc_err_with_data(msg, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_message(self, msg: &str) -> RpcResult<Ok> {
|
||||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(err) => {
|
||||
let msg = format!("{}: {:?}", msg, err);
|
||||
Err(internal_rpc_err(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an internal JSON-RPC error.
|
||||
pub(crate) fn internal_rpc_err(msg: impl Into<String>) -> jsonrpsee::core::Error {
|
||||
rpc_err(jsonrpsee::types::error::INTERNAL_ERROR_CODE, msg, None)
|
||||
}
|
||||
|
||||
/// Constructs an internal JSON-RPC error with data
|
||||
pub(crate) fn internal_rpc_err_with_data(
|
||||
msg: impl Into<String>,
|
||||
data: &[u8],
|
||||
) -> jsonrpsee::core::Error {
|
||||
rpc_err(jsonrpsee::types::error::INTERNAL_ERROR_CODE, msg, Some(data))
|
||||
}
|
||||
|
||||
/// Constructs a JSON-RPC error, consisting of `code`, `message` and optional `data`.
|
||||
pub(crate) fn rpc_err(code: i32, msg: impl Into<String>, data: Option<&[u8]>) -> RpcError {
|
||||
RpcError::Call(jsonrpsee::types::error::CallError::Custom(
|
||||
jsonrpsee::types::error::ErrorObject::owned(
|
||||
code,
|
||||
msg.into(),
|
||||
data.map(|data| {
|
||||
jsonrpsee::core::to_json_raw_value(&format!("0x{}", hex::encode(data)))
|
||||
.expect("serializing String does fail")
|
||||
}),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_rpc_result<Ok, Err, T: ToRpcResult<Ok, Err>>() {}
|
||||
|
||||
fn to_reth_err<Ok>(o: Ok) -> reth_interfaces::Result<Ok> {
|
||||
Ok(o)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_convert_rpc() {
|
||||
assert_rpc_result::<(), reth_interfaces::Error, reth_interfaces::Result<()>>();
|
||||
let res = to_reth_err(100);
|
||||
|
||||
let rpc_res = res.map_internal_err(|_| "This is a message");
|
||||
let val = rpc_res.unwrap();
|
||||
assert_eq!(val, 100);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user