Skip to content

Commit

Permalink
partial port of polkadot-evm#510 (without EIP1559)
Browse files Browse the repository at this point in the history
  • Loading branch information
nanocryk committed Nov 10, 2021
1 parent c77e43a commit 5582581
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 16 deletions.
34 changes: 34 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 11 additions & 12 deletions client/rpc/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::{
error_on_execution_failure, frontier_backend_client, internal_err, public_key, EthSigner,
StorageOverride,
error_on_execution_failure, format::Formatter, frontier_backend_client, internal_err,
public_key, EthSigner, StorageOverride,
};
use ethereum::{BlockV0 as EthereumBlock, TransactionV0 as EthereumTransaction};
use ethereum_types::{H160, H256, H512, H64, U256, U64};
Expand Down Expand Up @@ -60,7 +60,7 @@ use codec::{self, Decode, Encode};
pub use fc_rpc_core::{EthApiServer, EthFilterApiServer, NetApiServer, Web3ApiServer};
use pallet_ethereum::EthereumStorageSchema;

pub struct EthApi<B: BlockT, C, P, CT, BE, H: ExHashT, A: ChainApi> {
pub struct EthApi<B: BlockT, C, P, CT, BE, H: ExHashT, A: ChainApi, F: Formatter> {
pool: Arc<P>,
graph: Arc<Pool<A>>,
client: Arc<C>,
Expand All @@ -72,16 +72,17 @@ pub struct EthApi<B: BlockT, C, P, CT, BE, H: ExHashT, A: ChainApi> {
backend: Arc<fc_db::Backend<B>>,
max_past_logs: u32,
block_data_cache: Arc<EthBlockDataCache<B>>,
_marker: PhantomData<(B, BE)>,
_marker: PhantomData<(B, BE, F)>,
}

impl<B: BlockT, C, P, CT, BE, H: ExHashT, A: ChainApi> EthApi<B, C, P, CT, BE, H, A>
impl<B: BlockT, C, P, CT, BE, H: ExHashT, A: ChainApi, F> EthApi<B, C, P, CT, BE, H, A, F>
where
C: ProvideRuntimeApi<B>,
C::Api: EthereumRuntimeRPCApi<B>,
B: BlockT<Hash = H256> + Send + Sync + 'static,
A: ChainApi<Block = B> + 'static,
C: Send + Sync + 'static,
F: Formatter,
{
pub fn new(
client: Arc<C>,
Expand All @@ -95,6 +96,7 @@ where
is_authority: bool,
max_past_logs: u32,
block_data_cache: Arc<EthBlockDataCache<B>>,
_formatter: F,
) -> Self {
Self {
client,
Expand Down Expand Up @@ -421,7 +423,7 @@ fn filter_block_logs<'a>(
ret
}

impl<B, C, P, CT, BE, H: ExHashT, A> EthApiT for EthApi<B, C, P, CT, BE, H, A>
impl<B, C, P, CT, BE, H: ExHashT, A, F> EthApiT for EthApi<B, C, P, CT, BE, H, A, F>
where
C: ProvideRuntimeApi<B> + StorageProvider<B, BE>,
C: HeaderBackend<B> + HeaderMetadata<B, Error = BlockChainError> + 'static,
Expand All @@ -433,6 +435,7 @@ where
P: TransactionPool<Block = B> + Send + Sync + 'static,
A: ChainApi<Block = B> + 'static,
CT: ConvertTransaction<<B as BlockT>::Extrinsic> + Send + Sync + 'static,
F: Formatter,
{
fn protocol_version(&self) -> Result<u64> {
Ok(1)
Expand Down Expand Up @@ -833,9 +836,7 @@ where
.convert_transaction(transaction.clone()),
)
.map_ok(move |_| transaction_hash)
.map_err(|err| {
internal_err(format!("submit transaction to pool failed: {:?}", err))
}),
.map_err(|err| internal_err(F::pool_error(err))),
)
}

Expand All @@ -856,9 +857,7 @@ where
.convert_transaction(transaction.clone()),
)
.map_ok(move |_| transaction_hash)
.map_err(|err| {
internal_err(format!("submit transaction to pool failed: {:?}", err))
}),
.map_err(|err| internal_err(F::pool_error(err))),
)
}

Expand Down
72 changes: 72 additions & 0 deletions client/rpc/src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use pallet_ethereum::TransactionValidationError as VError;
use sc_transaction_pool_api::error::{Error as PError, IntoPoolError};
use sp_runtime::transaction_validity::InvalidTransaction;

// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This file is part of Frontier.
//
// Copyright (c) 2020 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

// Allows to customize the formating of strings returned by the API.
// This allow to comply with the different formatting various Ethereum
// node implementations have.
pub trait Formatter: Send + Sync + 'static {
fn pool_error(err: impl IntoPoolError) -> String;
}

// Formatter keeping the same output as before the introduction of this
// formatter design.
pub struct Legacy;

impl Formatter for Legacy {
fn pool_error(err: impl IntoPoolError) -> String {
format!("submit transaction to pool failed: {:?}", err)
}
}

// Formats the same way Geth node formats responses.
pub struct Geth;

impl Formatter for Geth {
fn pool_error(err: impl IntoPoolError) -> String {
// Error strings from :
// https://github.com/ethereum/go-ethereum/blob/794c6133efa2c7e8376d9d141c900ea541790bce/core/error.go
match err.into_pool_error() {
Ok(PError::AlreadyImported(_)) => "already known".to_string(),
// In Frontier the only case there is a `TemporarilyBanned` is because
// the same transaction was received before and returned
// `InvalidTransaction::Stale`. Thus we return the same error.
Ok(PError::TemporarilyBanned) => "nonce too low".into(),
Ok(PError::TooLowPriority { .. }) => "replacement transaction underpriced".into(),
Ok(PError::InvalidTransaction(inner)) => match inner {
InvalidTransaction::Stale => "nonce too low".into(),
InvalidTransaction::Payment => "insufficient funds for gas * price + value".into(),
InvalidTransaction::Custom(inner) => match inner.into() {
VError::UnknownError => "unknown error".into(),
VError::InvalidChainId => "invalid chain id".into(),
VError::InvalidSignature => "invalid sender".into(),
VError::GasLimitTooLow => "intrinsic gas too low".into(),
VError::GasLimitTooHigh => "exceeds block gas limit".into(),
VError::InsufficientFundsForTransfer => {
"insufficient funds for transfer".into()
}
},
_ => "unknown error".into(),
},
err @ _ => format!("submit transaction to pool failed: {:?}", err),
}
}
}
2 changes: 2 additions & 0 deletions client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ mod eth;
mod eth_pubsub;
mod overrides;

pub mod format;

pub use eth::{
EthApi, EthApiServer, EthBlockDataCache, EthFilterApi, EthFilterApiServer, EthTask, NetApi,
NetApiServer, Web3Api, Web3ApiServer,
Expand Down
2 changes: 2 additions & 0 deletions frame/ethereum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fp-rpc = { version = "3.0.0-dev", path = "../../primitives/rpc", default-feature
fp-storage = { version = "2.0.0", path = "../../primitives/storage", default-features = false }
fp-self-contained = { version = "1.0.0-dev", path = "../../primitives/self-contained", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
num_enum = { version = "0.5.4", default-features = false }

[dev-dependencies]
sp-core = { version = "4.0.0-dev", git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.12" }
Expand Down Expand Up @@ -64,4 +65,5 @@ std = [
"evm/std",
"fp-self-contained/std",
"scale-info/std",
"num_enum/std",
]
19 changes: 15 additions & 4 deletions frame/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl<T: Config> Pallet<T> {
};
if gasometer.record_transaction(transaction_cost).is_err() {
return Err(InvalidTransaction::Custom(
TransactionValidationError::InvalidGasLimit as u8,
TransactionValidationError::GasLimitTooLow as u8,
)
.into());
}
Expand All @@ -403,13 +403,20 @@ impl<T: Config> Pallet<T> {

if transaction.gas_limit >= T::BlockGasLimit::get() {
return Err(InvalidTransaction::Custom(
TransactionValidationError::InvalidGasLimit as u8,
TransactionValidationError::GasLimitTooHigh as u8,
)
.into());
}

let account_data = pallet_evm::Pallet::<T>::account_basic(&origin);

if account_data.balance < transaction.value {
return Err(InvalidTransaction::Custom(
TransactionValidationError::InsufficientFundsForTransfer as u8,
)
.into());
}

let fee = transaction.gas_price.saturating_mul(transaction.gas_limit);
let total_payment = transaction.value.saturating_add(fee);
if account_data.balance < total_payment {
Expand Down Expand Up @@ -663,10 +670,14 @@ impl<T: Config> BlockHashMapping for EthereumBlockHashMapping<T> {
}

#[repr(u8)]
enum TransactionValidationError {
#[derive(num_enum::FromPrimitive, num_enum::IntoPrimitive)]
pub enum TransactionValidationError {
#[allow(dead_code)]
#[num_enum(default)]
UnknownError,
InvalidChainId,
InvalidSignature,
InvalidGasLimit,
GasLimitTooLow,
GasLimitTooHigh,
InsufficientFundsForTransfer,
}
1 change: 1 addition & 0 deletions template/node/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ where
is_authority,
max_past_logs,
block_data_cache.clone(),
fc_rpc::format::Legacy,
)));

if let Some(filter_pool) = filter_pool {
Expand Down

0 comments on commit 5582581

Please sign in to comment.