Skip to content

Commit

Permalink
Add message-transact back (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
boundless-forest authored Nov 30, 2022
1 parent e68b051 commit 7845ccd
Show file tree
Hide file tree
Showing 24 changed files with 1,912 additions and 13 deletions.
52 changes: 52 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ panic = "unwind"
members = [
"core/*",
"node",
"pallets/*",
"precompiles/*",
"runtime/*",
]
73 changes: 73 additions & 0 deletions pallets/message-transact/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
[package]
authors = ["Darwinia Network <[email protected]>"]
description = "State storage precompiles for EVM pallet."
edition = "2021"
homepage = "https://darwinia.network"
license = "GPL-3.0"
name = "darwinia-message-transact"
readme = "README.md"
repository = "https://github.com/darwinia-network/darwinia"
version = "6.0.0"

[dependencies]
# crates.io
codec = { default-features = false, package = "parity-scale-codec", version = "3.2.1", features = ["derive"] }
ethereum = { default-features = false, version = "0.12.0", features = ["with-codec"] }
scale-info = { default-features = false, version = "2.3.0", features = ["derive"] }

# frontier
fp-ethereum = { default-features = false, git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
fp-evm = { default-features = false, git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
pallet-evm = { default-features = false, git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }

# paritytech
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }

[dev-dependencies]
array-bytes = { version = "4.1" }
libsecp256k1 = { version = "0.5", features = ["static-context", "hmac"] }
rlp = { version = "0.5" }
sha3 = { version = "0.9" }

# darwinia
pallet-bridge-dispatch = { git = "https://github.com/darwinia-network/darwinia-messages-substrate", branch = "polkadot-v0.9.30" }
bp-message-dispatch = { git = "https://github.com/darwinia-network/darwinia-messages-substrate", branch = "polkadot-v0.9.30" }
bp-runtime = { git = "https://github.com/darwinia-network/darwinia-messages-substrate", branch = "polkadot-v0.9.30" }

# frontier
fp-self-contained = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }

# moonbeam
precompile-utils = { git = "https://github.com/darwinia-network/moonbeam.git", branch = "polkadot-v0.9.30", features = ["testing"] }

# substrate
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
pallet-ethereum = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }

[features]
default = ["std"]
std = [
# crates.io
"codec/std",
"ethereum/std",
"scale-info/std",

# frontier
"fp-evm/std",
"fp-ethereum/std",
"pallet-evm/std",
"pallet-ethereum/std",

# paritytech
"frame-support/std",
"frame-system/std",
"sp-core/std",
"sp-runtime/std",
"sp-std/std",
]
201 changes: 201 additions & 0 deletions pallets/message-transact/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// This file is part of Darwinia.
//
// Copyright (C) 2018-2022 Darwinia Network
// SPDX-License-Identifier: GPL-3.0
//
// Darwinia 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.
//
// Darwinia 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 Darwinia. If not, see <https://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

// crates.io
use codec::{Decode, Encode, MaxEncodedLen};
use ethereum::TransactionV2 as Transaction;
use frame_support::sp_runtime::traits::UniqueSaturatedInto;
use scale_info::TypeInfo;
// frontier
use fp_ethereum::{TransactionData, ValidatedTransaction};
use fp_evm::{CheckEvmTransaction, CheckEvmTransactionConfig, InvalidEvmTransactionError};
use pallet_evm::{FeeCalculator, GasWeightMapping};
// substrate
use frame_support::{traits::EnsureOrigin, PalletError, RuntimeDebug};
use sp_core::{H160, U256};

pub use pallet::*;

#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum LcmpEthOrigin {
MessageTransact(H160),
}

pub fn ensure_message_transact<OuterOrigin>(o: OuterOrigin) -> Result<H160, &'static str>
where
OuterOrigin: Into<Result<LcmpEthOrigin, OuterOrigin>>,
{
match o.into() {
Ok(LcmpEthOrigin::MessageTransact(n)) => Ok(n),
_ => Err("bad origin: expected to be an Lcmp Ethereum transaction"),
}
}

pub struct EnsureLcmpEthOrigin;
impl<O: Into<Result<LcmpEthOrigin, O>> + From<LcmpEthOrigin>> EnsureOrigin<O>
for EnsureLcmpEthOrigin
{
type Success = H160;

fn try_origin(o: O) -> Result<Self::Success, O> {
o.into().map(|o| match o {
LcmpEthOrigin::MessageTransact(id) => id,
})
}

#[cfg(feature = "runtime-benchmarks")]
fn successful_origin() -> O {
O::from(LcmpEthOrigin::MessageTransact(Default::default()))
}
}

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);

#[pallet::origin]
pub type Origin = LcmpEthOrigin;

#[pallet::config]
pub trait Config: frame_system::Config + pallet_evm::Config {
/// Handler for applying an already validated transaction
type ValidatedTransaction: ValidatedTransaction;
/// Origin for message transact
type LcmpEthOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = H160>;
}

#[pallet::error]
pub enum Error<T> {
/// Evm validation errors.
MessageTransactError(EvmTxErrorWrapper),
}

#[pallet::call]
impl<T: Config> Pallet<T>
where
OriginFor<T>: Into<Result<LcmpEthOrigin, OriginFor<T>>>,
{
/// This call can only be called by the lcmp message layer and is not available to normal
/// users.
#[pallet::weight({
let without_base_extrinsic_weight = true;
<T as pallet_evm::Config>::GasWeightMapping::gas_to_weight({
let transaction_data: TransactionData = transaction.into();
transaction_data.gas_limit.unique_saturated_into()
}, without_base_extrinsic_weight)
})]
pub fn message_transact(
origin: OriginFor<T>,
transaction: Transaction,
) -> DispatchResultWithPostInfo {
let source = ensure_message_transact(origin)?;
let (who, _) = pallet_evm::Pallet::<T>::account_basic(&source);
let base_fee = T::FeeCalculator::min_gas_price().0;

let mut transaction_mut = transaction;
match transaction_mut {
Transaction::Legacy(ref mut tx) => {
tx.nonce = who.nonce;
tx.gas_price = base_fee;
},
Transaction::EIP2930(ref mut tx) => {
tx.nonce = who.nonce;
tx.gas_price = base_fee;
},
Transaction::EIP1559(ref mut tx) => {
tx.nonce = who.nonce;
tx.max_fee_per_gas = base_fee;
tx.max_priority_fee_per_gas = U256::zero();
},
};

let transaction_data: TransactionData = (&transaction_mut).into();
let _ = CheckEvmTransaction::<EvmTxErrorWrapper>::new(
CheckEvmTransactionConfig {
evm_config: T::config(),
block_gas_limit: T::BlockGasLimit::get(),
base_fee,
chain_id: T::ChainId::get(),
is_transactional: true,
},
transaction_data.clone().into(),
)
.validate_in_block_for(&who)
.and_then(|v| v.with_chain_id())
.and_then(|v| v.with_base_fee())
.and_then(|v| v.with_balance_for(&who))
.map_err(|e| <Error<T>>::MessageTransactError(e))?;

T::ValidatedTransaction::apply(source, transaction_mut)
}
}
}

#[derive(Encode, Decode, TypeInfo, PalletError)]
pub enum EvmTxErrorWrapper {
GasLimitTooLow,
GasLimitTooHigh,
GasPriceTooLow,
PriorityFeeTooHigh,
BalanceTooLow,
TxNonceTooLow,
TxNonceTooHigh,
InvalidPaymentInput,
InvalidChainId,
}

impl From<InvalidEvmTransactionError> for EvmTxErrorWrapper {
fn from(validation_error: InvalidEvmTransactionError) -> Self {
match validation_error {
InvalidEvmTransactionError::GasLimitTooLow => EvmTxErrorWrapper::GasLimitTooLow,
InvalidEvmTransactionError::GasLimitTooHigh => EvmTxErrorWrapper::GasLimitTooHigh,
InvalidEvmTransactionError::GasPriceTooLow => EvmTxErrorWrapper::GasPriceTooLow,
InvalidEvmTransactionError::PriorityFeeTooHigh => EvmTxErrorWrapper::PriorityFeeTooHigh,
InvalidEvmTransactionError::BalanceTooLow => EvmTxErrorWrapper::BalanceTooLow,
InvalidEvmTransactionError::TxNonceTooLow => EvmTxErrorWrapper::TxNonceTooLow,
InvalidEvmTransactionError::TxNonceTooHigh => EvmTxErrorWrapper::TxNonceTooHigh,
InvalidEvmTransactionError::InvalidPaymentInput =>
EvmTxErrorWrapper::InvalidPaymentInput,
InvalidEvmTransactionError::InvalidChainId => EvmTxErrorWrapper::InvalidChainId,
}
}
}

/// Calculates the fee for a relayer to submit an LCMP Evm transaction.
///
/// The gas_price of an LCMP Evm transaction is always the min_gas_price(), which is a fixed value.
/// Therefore, only the gas_limit and value of the transaction should be considered in the
/// calculation of the fee, and the gas_price of the transaction itself can be ignored.
pub fn total_payment<T: pallet_evm::Config>(tx_data: TransactionData) -> U256 {
let base_fee = <T as pallet_evm::Config>::FeeCalculator::min_gas_price().0;
let fee = base_fee.saturating_mul(tx_data.gas_limit);

tx_data.value.saturating_add(fee)
}
Loading

0 comments on commit 7845ccd

Please sign in to comment.