Skip to content

Commit 7845ccd

Browse files
Add message-transact back (#74)
1 parent e68b051 commit 7845ccd

24 files changed

Lines changed: 1912 additions & 13 deletions

File tree

Cargo.lock

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ panic = "unwind"
55
members = [
66
"core/*",
77
"node",
8+
"pallets/*",
89
"precompiles/*",
910
"runtime/*",
1011
]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
[package]
2+
authors = ["Darwinia Network <hello@darwinia.network>"]
3+
description = "State storage precompiles for EVM pallet."
4+
edition = "2021"
5+
homepage = "https://darwinia.network"
6+
license = "GPL-3.0"
7+
name = "darwinia-message-transact"
8+
readme = "README.md"
9+
repository = "https://github.com/darwinia-network/darwinia"
10+
version = "6.0.0"
11+
12+
[dependencies]
13+
# crates.io
14+
codec = { default-features = false, package = "parity-scale-codec", version = "3.2.1", features = ["derive"] }
15+
ethereum = { default-features = false, version = "0.12.0", features = ["with-codec"] }
16+
scale-info = { default-features = false, version = "2.3.0", features = ["derive"] }
17+
18+
# frontier
19+
fp-ethereum = { default-features = false, git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
20+
fp-evm = { default-features = false, git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
21+
pallet-evm = { default-features = false, git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
22+
23+
# paritytech
24+
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
25+
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
26+
sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
27+
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
28+
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
29+
30+
[dev-dependencies]
31+
array-bytes = { version = "4.1" }
32+
libsecp256k1 = { version = "0.5", features = ["static-context", "hmac"] }
33+
rlp = { version = "0.5" }
34+
sha3 = { version = "0.9" }
35+
36+
# darwinia
37+
pallet-bridge-dispatch = { git = "https://github.com/darwinia-network/darwinia-messages-substrate", branch = "polkadot-v0.9.30" }
38+
bp-message-dispatch = { git = "https://github.com/darwinia-network/darwinia-messages-substrate", branch = "polkadot-v0.9.30" }
39+
bp-runtime = { git = "https://github.com/darwinia-network/darwinia-messages-substrate", branch = "polkadot-v0.9.30" }
40+
41+
# frontier
42+
fp-self-contained = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
43+
44+
# moonbeam
45+
precompile-utils = { git = "https://github.com/darwinia-network/moonbeam.git", branch = "polkadot-v0.9.30", features = ["testing"] }
46+
47+
# substrate
48+
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
49+
pallet-ethereum = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.30" }
50+
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
51+
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30" }
52+
53+
[features]
54+
default = ["std"]
55+
std = [
56+
# crates.io
57+
"codec/std",
58+
"ethereum/std",
59+
"scale-info/std",
60+
61+
# frontier
62+
"fp-evm/std",
63+
"fp-ethereum/std",
64+
"pallet-evm/std",
65+
"pallet-ethereum/std",
66+
67+
# paritytech
68+
"frame-support/std",
69+
"frame-system/std",
70+
"sp-core/std",
71+
"sp-runtime/std",
72+
"sp-std/std",
73+
]
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// This file is part of Darwinia.
2+
//
3+
// Copyright (C) 2018-2022 Darwinia Network
4+
// SPDX-License-Identifier: GPL-3.0
5+
//
6+
// Darwinia is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU General Public License as published by
8+
// the Free Software Foundation, either version 3 of the License, or
9+
// (at your option) any later version.
10+
//
11+
// Darwinia is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU General Public License
17+
// along with Darwinia. If not, see <https://www.gnu.org/licenses/>.
18+
19+
#![cfg_attr(not(feature = "std"), no_std)]
20+
21+
#[cfg(test)]
22+
mod mock;
23+
#[cfg(test)]
24+
mod tests;
25+
26+
// crates.io
27+
use codec::{Decode, Encode, MaxEncodedLen};
28+
use ethereum::TransactionV2 as Transaction;
29+
use frame_support::sp_runtime::traits::UniqueSaturatedInto;
30+
use scale_info::TypeInfo;
31+
// frontier
32+
use fp_ethereum::{TransactionData, ValidatedTransaction};
33+
use fp_evm::{CheckEvmTransaction, CheckEvmTransactionConfig, InvalidEvmTransactionError};
34+
use pallet_evm::{FeeCalculator, GasWeightMapping};
35+
// substrate
36+
use frame_support::{traits::EnsureOrigin, PalletError, RuntimeDebug};
37+
use sp_core::{H160, U256};
38+
39+
pub use pallet::*;
40+
41+
#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
42+
pub enum LcmpEthOrigin {
43+
MessageTransact(H160),
44+
}
45+
46+
pub fn ensure_message_transact<OuterOrigin>(o: OuterOrigin) -> Result<H160, &'static str>
47+
where
48+
OuterOrigin: Into<Result<LcmpEthOrigin, OuterOrigin>>,
49+
{
50+
match o.into() {
51+
Ok(LcmpEthOrigin::MessageTransact(n)) => Ok(n),
52+
_ => Err("bad origin: expected to be an Lcmp Ethereum transaction"),
53+
}
54+
}
55+
56+
pub struct EnsureLcmpEthOrigin;
57+
impl<O: Into<Result<LcmpEthOrigin, O>> + From<LcmpEthOrigin>> EnsureOrigin<O>
58+
for EnsureLcmpEthOrigin
59+
{
60+
type Success = H160;
61+
62+
fn try_origin(o: O) -> Result<Self::Success, O> {
63+
o.into().map(|o| match o {
64+
LcmpEthOrigin::MessageTransact(id) => id,
65+
})
66+
}
67+
68+
#[cfg(feature = "runtime-benchmarks")]
69+
fn successful_origin() -> O {
70+
O::from(LcmpEthOrigin::MessageTransact(Default::default()))
71+
}
72+
}
73+
74+
#[frame_support::pallet]
75+
pub mod pallet {
76+
use super::*;
77+
use frame_support::pallet_prelude::*;
78+
use frame_system::pallet_prelude::*;
79+
80+
#[pallet::pallet]
81+
pub struct Pallet<T>(PhantomData<T>);
82+
83+
#[pallet::origin]
84+
pub type Origin = LcmpEthOrigin;
85+
86+
#[pallet::config]
87+
pub trait Config: frame_system::Config + pallet_evm::Config {
88+
/// Handler for applying an already validated transaction
89+
type ValidatedTransaction: ValidatedTransaction;
90+
/// Origin for message transact
91+
type LcmpEthOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = H160>;
92+
}
93+
94+
#[pallet::error]
95+
pub enum Error<T> {
96+
/// Evm validation errors.
97+
MessageTransactError(EvmTxErrorWrapper),
98+
}
99+
100+
#[pallet::call]
101+
impl<T: Config> Pallet<T>
102+
where
103+
OriginFor<T>: Into<Result<LcmpEthOrigin, OriginFor<T>>>,
104+
{
105+
/// This call can only be called by the lcmp message layer and is not available to normal
106+
/// users.
107+
#[pallet::weight({
108+
let without_base_extrinsic_weight = true;
109+
<T as pallet_evm::Config>::GasWeightMapping::gas_to_weight({
110+
let transaction_data: TransactionData = transaction.into();
111+
transaction_data.gas_limit.unique_saturated_into()
112+
}, without_base_extrinsic_weight)
113+
})]
114+
pub fn message_transact(
115+
origin: OriginFor<T>,
116+
transaction: Transaction,
117+
) -> DispatchResultWithPostInfo {
118+
let source = ensure_message_transact(origin)?;
119+
let (who, _) = pallet_evm::Pallet::<T>::account_basic(&source);
120+
let base_fee = T::FeeCalculator::min_gas_price().0;
121+
122+
let mut transaction_mut = transaction;
123+
match transaction_mut {
124+
Transaction::Legacy(ref mut tx) => {
125+
tx.nonce = who.nonce;
126+
tx.gas_price = base_fee;
127+
},
128+
Transaction::EIP2930(ref mut tx) => {
129+
tx.nonce = who.nonce;
130+
tx.gas_price = base_fee;
131+
},
132+
Transaction::EIP1559(ref mut tx) => {
133+
tx.nonce = who.nonce;
134+
tx.max_fee_per_gas = base_fee;
135+
tx.max_priority_fee_per_gas = U256::zero();
136+
},
137+
};
138+
139+
let transaction_data: TransactionData = (&transaction_mut).into();
140+
let _ = CheckEvmTransaction::<EvmTxErrorWrapper>::new(
141+
CheckEvmTransactionConfig {
142+
evm_config: T::config(),
143+
block_gas_limit: T::BlockGasLimit::get(),
144+
base_fee,
145+
chain_id: T::ChainId::get(),
146+
is_transactional: true,
147+
},
148+
transaction_data.clone().into(),
149+
)
150+
.validate_in_block_for(&who)
151+
.and_then(|v| v.with_chain_id())
152+
.and_then(|v| v.with_base_fee())
153+
.and_then(|v| v.with_balance_for(&who))
154+
.map_err(|e| <Error<T>>::MessageTransactError(e))?;
155+
156+
T::ValidatedTransaction::apply(source, transaction_mut)
157+
}
158+
}
159+
}
160+
161+
#[derive(Encode, Decode, TypeInfo, PalletError)]
162+
pub enum EvmTxErrorWrapper {
163+
GasLimitTooLow,
164+
GasLimitTooHigh,
165+
GasPriceTooLow,
166+
PriorityFeeTooHigh,
167+
BalanceTooLow,
168+
TxNonceTooLow,
169+
TxNonceTooHigh,
170+
InvalidPaymentInput,
171+
InvalidChainId,
172+
}
173+
174+
impl From<InvalidEvmTransactionError> for EvmTxErrorWrapper {
175+
fn from(validation_error: InvalidEvmTransactionError) -> Self {
176+
match validation_error {
177+
InvalidEvmTransactionError::GasLimitTooLow => EvmTxErrorWrapper::GasLimitTooLow,
178+
InvalidEvmTransactionError::GasLimitTooHigh => EvmTxErrorWrapper::GasLimitTooHigh,
179+
InvalidEvmTransactionError::GasPriceTooLow => EvmTxErrorWrapper::GasPriceTooLow,
180+
InvalidEvmTransactionError::PriorityFeeTooHigh => EvmTxErrorWrapper::PriorityFeeTooHigh,
181+
InvalidEvmTransactionError::BalanceTooLow => EvmTxErrorWrapper::BalanceTooLow,
182+
InvalidEvmTransactionError::TxNonceTooLow => EvmTxErrorWrapper::TxNonceTooLow,
183+
InvalidEvmTransactionError::TxNonceTooHigh => EvmTxErrorWrapper::TxNonceTooHigh,
184+
InvalidEvmTransactionError::InvalidPaymentInput =>
185+
EvmTxErrorWrapper::InvalidPaymentInput,
186+
InvalidEvmTransactionError::InvalidChainId => EvmTxErrorWrapper::InvalidChainId,
187+
}
188+
}
189+
}
190+
191+
/// Calculates the fee for a relayer to submit an LCMP Evm transaction.
192+
///
193+
/// The gas_price of an LCMP Evm transaction is always the min_gas_price(), which is a fixed value.
194+
/// Therefore, only the gas_limit and value of the transaction should be considered in the
195+
/// calculation of the fee, and the gas_price of the transaction itself can be ignored.
196+
pub fn total_payment<T: pallet_evm::Config>(tx_data: TransactionData) -> U256 {
197+
let base_fee = <T as pallet_evm::Config>::FeeCalculator::min_gas_price().0;
198+
let fee = base_fee.saturating_mul(tx_data.gas_limit);
199+
200+
tx_data.value.saturating_add(fee)
201+
}

0 commit comments

Comments
 (0)