mod block;
mod cache;
mod client;
mod execute;
mod fee;
mod filter;
pub mod format;
mod mining;
pub mod pending;
mod state;
mod submit;
mod transaction;
use std::{collections::BTreeMap, marker::PhantomData, sync::Arc};
use ethereum::{BlockV2 as EthereumBlock, TransactionV2 as EthereumTransaction};
use ethereum_types::{H160, H256, H512, H64, U256, U64};
use jsonrpsee::core::{async_trait, RpcResult};
use sc_client_api::backend::{Backend, StorageProvider};
use sc_network_sync::SyncingService;
use sc_transaction_pool::{ChainApi, Pool};
use sc_transaction_pool_api::TransactionPool;
use sp_api::{CallApiAt, ProvideRuntimeApi};
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_blockchain::HeaderBackend;
use sp_core::hashing::keccak_256;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::traits::{Block as BlockT, UniqueSaturatedInto};
use fc_rpc_core::{types::*, EthApiServer};
use fc_storage::OverrideHandle;
use fp_rpc::{
ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi,
RuntimeStorageOverride, TransactionStatus,
};
use crate::{public_key, signer::EthSigner};
pub use self::{
cache::{EthBlockDataCacheTask, EthTask},
execute::EstimateGasAdapter,
filter::EthFilter,
};
pub trait EthConfig<B: BlockT, C>: Send + Sync + 'static {
type EstimateGasAdapter: EstimateGasAdapter + Send + Sync;
type RuntimeStorageOverride: RuntimeStorageOverride<B, C>;
}
impl<B: BlockT, C> EthConfig<B, C> for () {
type EstimateGasAdapter = ();
type RuntimeStorageOverride = ();
}
pub struct Eth<B: BlockT, C, P, CT, BE, A: ChainApi, CIDP, EC: EthConfig<B, C>> {
pool: Arc<P>,
graph: Arc<Pool<A>>,
client: Arc<C>,
convert_transaction: Option<CT>,
sync: Arc<SyncingService<B>>,
is_authority: bool,
signers: Vec<Box<dyn EthSigner>>,
overrides: Arc<OverrideHandle<B>>,
backend: Arc<dyn fc_db::BackendReader<B> + Send + Sync>,
block_data_cache: Arc<EthBlockDataCacheTask<B>>,
fee_history_cache: FeeHistoryCache,
fee_history_cache_limit: FeeHistoryCacheLimit,
execute_gas_limit_multiplier: u64,
forced_parent_hashes: Option<BTreeMap<H256, H256>>,
pending_create_inherent_data_providers: CIDP,
pending_consensus_data_provider: Option<Box<dyn pending::ConsensusDataProvider<B>>>,
_marker: PhantomData<(BE, EC)>,
}
impl<B, C, P, CT, BE, A, CIDP> Eth<B, C, P, CT, BE, A, CIDP, ()>
where
B: BlockT,
A: ChainApi<Block = B>,
{
pub fn new(
client: Arc<C>,
pool: Arc<P>,
graph: Arc<Pool<A>>,
convert_transaction: Option<CT>,
sync: Arc<SyncingService<B>>,
signers: Vec<Box<dyn EthSigner>>,
overrides: Arc<OverrideHandle<B>>,
backend: Arc<dyn fc_db::BackendReader<B> + Send + Sync>,
is_authority: bool,
block_data_cache: Arc<EthBlockDataCacheTask<B>>,
fee_history_cache: FeeHistoryCache,
fee_history_cache_limit: FeeHistoryCacheLimit,
execute_gas_limit_multiplier: u64,
forced_parent_hashes: Option<BTreeMap<H256, H256>>,
pending_create_inherent_data_providers: CIDP,
pending_consensus_data_provider: Option<Box<dyn pending::ConsensusDataProvider<B>>>,
) -> Self {
Self {
client,
pool,
graph,
convert_transaction,
sync,
is_authority,
signers,
overrides,
backend,
block_data_cache,
fee_history_cache,
fee_history_cache_limit,
execute_gas_limit_multiplier,
forced_parent_hashes,
pending_create_inherent_data_providers,
pending_consensus_data_provider,
_marker: PhantomData,
}
}
}
impl<B, C, P, CT, BE, A, CIDP, EC> Eth<B, C, P, CT, BE, A, CIDP, EC>
where
B: BlockT,
A: ChainApi<Block = B>,
EC: EthConfig<B, C>,
{
pub fn replace_config<EC2: EthConfig<B, C>>(self) -> Eth<B, C, P, CT, BE, A, CIDP, EC2> {
let Self {
client,
pool,
graph,
convert_transaction,
sync,
is_authority,
signers,
overrides,
backend,
block_data_cache,
fee_history_cache,
fee_history_cache_limit,
execute_gas_limit_multiplier,
forced_parent_hashes,
pending_create_inherent_data_providers,
pending_consensus_data_provider,
_marker: _,
} = self;
Eth {
client,
pool,
graph,
convert_transaction,
sync,
is_authority,
signers,
overrides,
backend,
block_data_cache,
fee_history_cache,
fee_history_cache_limit,
execute_gas_limit_multiplier,
forced_parent_hashes,
pending_create_inherent_data_providers,
pending_consensus_data_provider,
_marker: PhantomData,
}
}
}
#[async_trait]
impl<B, C, P, CT, BE, A, CIDP, EC> EthApiServer for Eth<B, C, P, CT, BE, A, CIDP, EC>
where
B: BlockT,
C: CallApiAt<B> + ProvideRuntimeApi<B>,
C::Api: BlockBuilderApi<B> + ConvertTransactionRuntimeApi<B> + EthereumRuntimeRPCApi<B>,
C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
BE: Backend<B> + 'static,
P: TransactionPool<Block = B> + 'static,
CT: ConvertTransaction<<B as BlockT>::Extrinsic> + Send + Sync + 'static,
A: ChainApi<Block = B> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
EC: EthConfig<B, C>,
{
fn protocol_version(&self) -> RpcResult<u64> {
self.protocol_version()
}
fn syncing(&self) -> RpcResult<SyncStatus> {
self.syncing()
}
fn author(&self) -> RpcResult<H160> {
self.author()
}
fn accounts(&self) -> RpcResult<Vec<H160>> {
self.accounts()
}
fn block_number(&self) -> RpcResult<U256> {
self.block_number()
}
fn chain_id(&self) -> RpcResult<Option<U64>> {
self.chain_id()
}
async fn block_by_hash(&self, hash: H256, full: bool) -> RpcResult<Option<RichBlock>> {
self.block_by_hash(hash, full).await
}
async fn block_by_number(
&self,
number: BlockNumber,
full: bool,
) -> RpcResult<Option<RichBlock>> {
self.block_by_number(number, full).await
}
async fn block_transaction_count_by_hash(&self, hash: H256) -> RpcResult<Option<U256>> {
self.block_transaction_count_by_hash(hash).await
}
async fn block_transaction_count_by_number(
&self,
number: BlockNumber,
) -> RpcResult<Option<U256>> {
self.block_transaction_count_by_number(number).await
}
fn block_uncles_count_by_hash(&self, hash: H256) -> RpcResult<U256> {
self.block_uncles_count_by_hash(hash)
}
fn block_uncles_count_by_number(&self, number: BlockNumber) -> RpcResult<U256> {
self.block_uncles_count_by_number(number)
}
fn uncle_by_block_hash_and_index(
&self,
hash: H256,
index: Index,
) -> RpcResult<Option<RichBlock>> {
self.uncle_by_block_hash_and_index(hash, index)
}
fn uncle_by_block_number_and_index(
&self,
number: BlockNumber,
index: Index,
) -> RpcResult<Option<RichBlock>> {
self.uncle_by_block_number_and_index(number, index)
}
async fn transaction_by_hash(&self, hash: H256) -> RpcResult<Option<Transaction>> {
self.transaction_by_hash(hash).await
}
async fn transaction_by_block_hash_and_index(
&self,
hash: H256,
index: Index,
) -> RpcResult<Option<Transaction>> {
self.transaction_by_block_hash_and_index(hash, index).await
}
async fn transaction_by_block_number_and_index(
&self,
number: BlockNumber,
index: Index,
) -> RpcResult<Option<Transaction>> {
self.transaction_by_block_number_and_index(number, index)
.await
}
async fn transaction_receipt(&self, hash: H256) -> RpcResult<Option<Receipt>> {
self.transaction_receipt(hash).await
}
async fn balance(&self, address: H160, number: Option<BlockNumber>) -> RpcResult<U256> {
self.balance(address, number).await
}
async fn storage_at(
&self,
address: H160,
index: U256,
number: Option<BlockNumber>,
) -> RpcResult<H256> {
self.storage_at(address, index, number).await
}
async fn transaction_count(
&self,
address: H160,
number: Option<BlockNumber>,
) -> RpcResult<U256> {
self.transaction_count(address, number).await
}
async fn code_at(&self, address: H160, number: Option<BlockNumber>) -> RpcResult<Bytes> {
self.code_at(address, number).await
}
async fn call(
&self,
request: CallRequest,
number: Option<BlockNumber>,
state_overrides: Option<BTreeMap<H160, CallStateOverride>>,
) -> RpcResult<Bytes> {
self.call(request, number, state_overrides).await
}
async fn estimate_gas(
&self,
request: CallRequest,
number: Option<BlockNumber>,
) -> RpcResult<U256> {
self.estimate_gas(request, number).await
}
fn gas_price(&self) -> RpcResult<U256> {
self.gas_price()
}
async fn fee_history(
&self,
block_count: U256,
newest_block: BlockNumber,
reward_percentiles: Option<Vec<f64>>,
) -> RpcResult<FeeHistory> {
self.fee_history(block_count, newest_block, reward_percentiles)
.await
}
fn max_priority_fee_per_gas(&self) -> RpcResult<U256> {
self.max_priority_fee_per_gas()
}
fn is_mining(&self) -> RpcResult<bool> {
self.is_mining()
}
fn hashrate(&self) -> RpcResult<U256> {
self.hashrate()
}
fn work(&self) -> RpcResult<Work> {
self.work()
}
fn submit_hashrate(&self, hashrate: U256, id: H256) -> RpcResult<bool> {
self.submit_hashrate(hashrate, id)
}
fn submit_work(&self, nonce: H64, pow_hash: H256, mix_digest: H256) -> RpcResult<bool> {
self.submit_work(nonce, pow_hash, mix_digest)
}
async fn send_transaction(&self, request: TransactionRequest) -> RpcResult<H256> {
self.send_transaction(request).await
}
async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult<H256> {
self.send_raw_transaction(bytes).await
}
}
fn rich_block_build(
block: EthereumBlock,
statuses: Vec<Option<TransactionStatus>>,
hash: Option<H256>,
full_transactions: bool,
base_fee: Option<U256>,
is_pending: bool,
) -> RichBlock {
let (hash, miner, nonce, total_difficulty) = if !is_pending {
(
Some(hash.unwrap_or_else(|| H256::from(keccak_256(&rlp::encode(&block.header))))),
Some(block.header.beneficiary),
Some(block.header.nonce),
Some(U256::zero()),
)
} else {
(None, None, None, None)
};
Rich {
inner: Block {
header: Header {
hash,
parent_hash: block.header.parent_hash,
uncles_hash: block.header.ommers_hash,
author: block.header.beneficiary,
miner,
state_root: block.header.state_root,
transactions_root: block.header.transactions_root,
receipts_root: block.header.receipts_root,
number: Some(block.header.number),
gas_used: block.header.gas_used,
gas_limit: block.header.gas_limit,
extra_data: Bytes(block.header.extra_data.clone()),
logs_bloom: block.header.logs_bloom,
timestamp: U256::from(block.header.timestamp / 1000),
difficulty: block.header.difficulty,
nonce,
size: Some(U256::from(rlp::encode(&block.header).len() as u32)),
},
total_difficulty,
uncles: vec![],
transactions: {
if full_transactions {
BlockTransactions::Full(
block
.transactions
.iter()
.enumerate()
.map(|(index, transaction)| {
transaction_build(
transaction.clone(),
Some(block.clone()),
Some(statuses[index].clone().unwrap_or_default()),
base_fee,
)
})
.collect(),
)
} else {
BlockTransactions::Hashes(
block
.transactions
.iter()
.map(|transaction| transaction.hash())
.collect(),
)
}
},
size: Some(U256::from(rlp::encode(&block).len() as u32)),
base_fee_per_gas: base_fee,
},
extra_info: BTreeMap::new(),
}
}
fn transaction_build(
ethereum_transaction: EthereumTransaction,
block: Option<EthereumBlock>,
status: Option<TransactionStatus>,
base_fee: Option<U256>,
) -> Transaction {
let mut transaction: Transaction = ethereum_transaction.clone().into();
if let EthereumTransaction::EIP1559(_) = ethereum_transaction {
if block.is_none() && status.is_none() {
transaction.gas_price = transaction.max_fee_per_gas;
} else {
let base_fee = base_fee.unwrap_or_default();
let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap_or_default();
let max_fee_per_gas = transaction.max_fee_per_gas.unwrap_or_default();
transaction.gas_price = Some(
base_fee
.checked_add(max_priority_fee_per_gas)
.unwrap_or_else(U256::max_value)
.min(max_fee_per_gas),
);
}
}
let pubkey = match public_key(ðereum_transaction) {
Ok(p) => Some(p),
Err(_e) => None,
};
transaction.block_hash = block
.as_ref()
.map(|block| H256::from(keccak_256(&rlp::encode(&block.header))));
transaction.block_number = block.as_ref().map(|block| block.header.number);
transaction.transaction_index = status.as_ref().map(|status| {
U256::from(UniqueSaturatedInto::<u32>::unique_saturated_into(
status.transaction_index,
))
});
transaction.from = status.as_ref().map_or(
{
match pubkey {
Some(pk) => H160::from(H256::from(keccak_256(&pk))),
_ => H160::default(),
}
},
|status| status.from,
);
transaction.to = status.as_ref().map_or(
{
let action = match ethereum_transaction {
EthereumTransaction::Legacy(t) => t.action,
EthereumTransaction::EIP2930(t) => t.action,
EthereumTransaction::EIP1559(t) => t.action,
};
match action {
ethereum::TransactionAction::Call(to) => Some(to),
_ => None,
}
},
|status| status.to,
);
transaction.creates = status.as_ref().and_then(|status| status.contract_address);
transaction.public_key = pubkey.as_ref().map(H512::from);
transaction
}