Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eip1559 support #364

Open
wants to merge 11 commits into
base: develop-ready
Choose a base branch
from
5 changes: 4 additions & 1 deletion src/components/config/src/abci/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ pub struct CheckPointConfig {
// Fix the amount in the delegators that staking did not modify when it punished the validator.
pub fix_delegators_am_height: u64,
pub validators_limit_v2_height: u64,

// eip1559 support switch.
pub enable_eip1559_height: u64,
HarryLiIsMe marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/FindoraNetwork/platform/pull/707
// FO-1370: V0.3.30 EVM bug: receipt missing when error code === 1
pub fix_deliver_tx_revert_nonce_height: i64,
Expand Down Expand Up @@ -131,6 +132,7 @@ impl CheckPointConfig {
proper_gas_set_height: 0,
fix_delegators_am_height: 0,
validators_limit_v2_height: 0,
enable_eip1559_height: 0,
fix_deliver_tx_revert_nonce_height: 0,
};
#[cfg(not(feature = "debug_env"))]
Expand Down Expand Up @@ -160,6 +162,7 @@ impl CheckPointConfig {
fix_undelegation_missing_reward_height: 3000000,
fix_delegators_am_height: 30000000,
validators_limit_v2_height: 30000000,
enable_eip1559_height: 40000000,
fix_deliver_tx_revert_nonce_height: 40000000,
};
let content = toml::to_string(&config).unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/components/contracts/primitives/rpc-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ description = "RPC traits of Ethereum."
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"

[dependencies]
ethereum = { version = "0.12.0", default-features = false, features = ["with-serde"] }
ethereum-types = "0.13.1"
futures = "0.3.16"
jsonrpc-core = { git = "https://github.com/FindoraNetwork/jsonrpc.git", package = "jsonrpc-core" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ use std::ops::Deref;

use crate::types::{Bytes, Transaction};
use ethereum_types::{Bloom as H2048, H160, H256, U256};
use serde::ser::Error;
use serde::{Serialize, Serializer};
use serde::{ser::Error, Serialize, Serializer};

/// Block Transactions
#[derive(Debug)]
Expand Down Expand Up @@ -90,6 +89,9 @@ pub struct Block {
pub transactions: BlockTransactions,
/// Size in bytes
pub size: Option<U256>,
/// Base Fee for post-EIP1559 blocks.
HarryLiIsMe marked this conversation as resolved.
Show resolved Hide resolved
#[serde(skip_serializing_if = "Option::is_none")]
pub base_fee_per_gas: Option<U256>,
}

/// Block header representation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub struct CallRequest {
pub to: Option<H160>,
/// Gas Price
pub gas_price: Option<U256>,
/// EIP-1559 Max base fee the caller is willing to pay
pub max_fee_per_gas: Option<U256>,
/// EIP-1559 Priority fee the caller is paying to the block author
pub max_priority_fee_per_gas: Option<U256>,
/// Gas
pub gas: Option<U256>,
/// Value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ pub struct Receipt {
// NOTE(niklasad1): Unknown after EIP98 rules, if it's missing then skip serializing it
#[serde(skip_serializing_if = "Option::is_none", rename = "status")]
pub status_code: Option<U64>,
/// Effective gas price. Pre-eip1559 this is just the gasprice. Post-eip1559 this is base fee + priority fee.
HarryLiIsMe marked this conversation as resolved.
Show resolved Hide resolved
pub effective_gas_price: U256,
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::types::Bytes;
use ethereum::AccessListItem;
use ethereum_types::{H160, H256, H512, U256, U64};
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};
use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::{
collections::HashMap,
sync::{Arc, Mutex},
Expand All @@ -46,7 +46,14 @@ pub struct Transaction {
/// Transfered value
pub value: U256,
/// Gas Price
pub gas_price: U256,
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_price: Option<U256>,
/// eip1559. Max BaseFeePerGas the user is willing to pay.
#[serde(skip_serializing_if = "Option::is_none")]
pub max_fee_per_gas: Option<U256>,
/// eip1559.The miner's tip.
#[serde(skip_serializing_if = "Option::is_none")]
pub max_priority_fee_per_gas: Option<U256>,
/// Gas
pub gas: U256,
/// Data
Expand All @@ -67,6 +74,9 @@ pub struct Transaction {
pub r: U256,
/// The S field of the signature.
pub s: U256,
/// eip1559. Pre-pay to warm storage access.
#[cfg_attr(feature = "std", serde(skip_serializing_if = "Option::is_none"))]
pub access_list: Option<Vec<AccessListItem>>,
}

/// Local Transaction Status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
//! `TransactionRequest` type
use crate::types::Bytes;
use ethereum::AccessListItem;
use ethereum_types::{H160, U256};
use serde::{Deserialize, Serialize};

/// Transaction request coming from RPC
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct TransactionRequest {
Expand All @@ -32,7 +33,14 @@ pub struct TransactionRequest {
/// Recipient
pub to: Option<H160>,
/// Gas Price
#[serde(default)]
pub gas_price: Option<U256>,
/// Max BaseFeePerGas the user is willing to pay.
#[serde(default)]
pub max_fee_per_gas: Option<U256>,
/// The miner's tip.
#[serde(default)]
pub max_priority_fee_per_gas: Option<U256>,
/// Gas
pub gas: Option<U256>,
/// Value of transaction in wei
Expand All @@ -41,4 +49,7 @@ pub struct TransactionRequest {
pub data: Option<Bytes>,
/// Transaction's nonce
pub nonce: Option<U256>,
/// Pre-pay to warm storage access.
#[serde(default)]
pub access_list: Option<Vec<AccessListItem>>,
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ethereum::TransactionV0 as Transaction;
use ethereum::TransactionV0 as LegcayTransaction;
HarryLiIsMe marked this conversation as resolved.
Show resolved Hide resolved
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Action {
Transact(Transaction),
Transact(LegcayTransaction),
}
4 changes: 4 additions & 0 deletions src/components/contracts/primitives/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ fp-utils = { path = "../../primitives/utils" }
rlp = "0.5"
ruc = "1.0"
sha3 = "0.10"
serde = { version = "1.0.124", features = ["derive"] }
serde_json = "1.0"
wasm-bindgen = { version = "=0.2.73", features = ["serde-serialize"] }

# Must enable the "js"-feature,
# OR the compiling will fail.
getrandom = { version = "0.2", features = ["js"] }
baseapp = { path = "../../../contracts/baseapp" }
module-ethereum = { path = "../../modules/ethereum"}
fp-traits = { path = "../../primitives/traits" }

[lib]
crate-type = ["cdylib", "rlib"]
Expand Down
93 changes: 79 additions & 14 deletions src/components/contracts/primitives/wasm/src/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
#![allow(clippy::unused_unit)]

use core::fmt::Display;
use ethereum::{LegacyTransactionMessage, TransactionV0 as Transaction};
use ethereum::{
LegacyTransactionMessage, TransactionSignature, TransactionV0 as LegacyTransaction,
TransactionV2 as Transaction,
};
use ethereum_types::{H160, H256};
use serde::{Deserialize, Serialize};

use fp_types::{
actions::{ethereum::Action as EthAction, Action},
assemble::UncheckedTransaction,
crypto::secp256k1_ecdsa_recover,
actions::{evm, template, xhub},
crypto::{secp256k1_ecdsa_recover, Address, Signature},
transaction,
};
use fp_utils::tx::EvmRawTxWrapper;
use ruc::{d, err::RucResult};
use sha3::{Digest, Keccak256};
use wasm_bindgen::prelude::*;

use baseapp::BaseApp;
use fp_traits::evm::FeeCalculator;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EthAction {
Transact(Transaction),
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Action {
Ethereum(EthAction),
Evm(evm::Action),
XHub(xhub::Action),
Template(template::Action),
}

pub type UncheckedTransaction<Extra> =
transaction::UncheckedTransaction<Address, Action, Signature, Extra>;

#[inline(always)]
pub(crate) fn error_to_jsvalue<T: Display>(e: T) -> JsValue {
JsValue::from_str(&e.to_string())
}

#[inline(always)]
pub fn recover_signer(transaction: &Transaction) -> Option<H160> {
pub fn recover_signer(transaction: &LegacyTransaction) -> Option<H160> {
let mut sig = [0u8; 65];
let mut msg = [0u8; 32];
sig[0..32].copy_from_slice(&transaction.signature.r()[..]);
Expand All @@ -46,6 +70,10 @@ pub fn recover_tx_signer(raw_tx: String) -> Result<String, JsValue> {
.c(d!())
.map_err(error_to_jsvalue)?;
if let Action::Ethereum(EthAction::Transact(tx)) = unchecked_tx.function {
let tx = match new_tx2legcay_tx(tx) {
Some(tx) => tx,
None => return Err(error_to_jsvalue("invalid raw tx")),
};
let signer = recover_signer(&tx).c(d!()).map_err(error_to_jsvalue)?;
Ok(format!("{signer:?}",))
} else {
Expand All @@ -66,31 +94,67 @@ pub fn evm_tx_hash(raw_tx: String) -> Result<String, JsValue> {
.c(d!())
.map_err(error_to_jsvalue)?;
if let Action::Ethereum(EthAction::Transact(tx)) = unchecked_tx.function {
let tx = match new_tx2legcay_tx(tx) {
Some(tx) => tx,
None => return Err(error_to_jsvalue("invalid raw tx")),
};
let hash = H256::from_slice(Keccak256::digest(&rlp::encode(&tx)).as_slice());
Ok(format!("{hash:?}",))
} else {
Err(error_to_jsvalue("invalid raw tx"))
}
}

fn new_tx2legcay_tx(tx: Transaction) -> Option<LegacyTransaction> {
let transaction: LegacyTransaction = match tx {
ethereum::TransactionV2::Legacy(tx) => tx,
ethereum::TransactionV2::EIP1559(tx) => {
let min_gas_price_eip1559 =
<BaseApp as module_ethereum::Config>::FeeCalculator::min_gas_price(0);
let chain_id: u64 = <BaseApp as module_ethereum::Config>::ChainId::get();
let v: u64 = if tx.odd_y_parity {
chain_id * 2 + 36
} else {
chain_id * 2 + 35
};
let signature = match TransactionSignature::new(v, tx.r, tx.s) {
Some(sig) => sig,
None => return None,
};

ethereum::TransactionV0 {
nonce: tx.nonce,
gas_price: min_gas_price_eip1559,
gas_limit: tx.gas_limit,
action: tx.action,
value: tx.value,
input: tx.input,
signature,
}
}
_ => return None,
};

Some(transaction)
}

#[cfg(test)]
#[allow(missing_docs)]
mod test {
use super::*;
use fp_types::actions::Action;

#[test]
fn recover_signer_works() {
HarryLiIsMe marked this conversation as resolved.
Show resolved Hide resolved
let raw_tx = String::from("ZXZtOnsic2lnbmF0dXJlIjpudWxsLCJmdW5jdGlvbiI6eyJFdGhlcmV1bSI6eyJUcmFuc2FjdCI6eyJub25jZSI6IjB4MSIsImdhc19wcmljZSI6IjB4MTc0ODc2ZTgwMCIsImdhc19saW1pdCI6IjB4NTIwOCIsImFjdGlvbiI6eyJDYWxsIjoiMHgyYWQzMjg0NmM2ZGQyZmZkM2VkYWRiZTUxY2Q1YWUwNGFhNWU1NzVlIn0sInZhbHVlIjoiMHg1NmJjNzVlMmQ2MzEwMDAwMCIsImlucHV0IjpbXSwic2lnbmF0dXJlIjp7InYiOjEwODIsInIiOiIweGY4YWVmN2Y4MDUzZDg5ZmVlMzk1MGM0ZDcwMjA4MGJmM2E4MDcyYmVkNWQ4NGEzYWYxOWEzNjAwODFiNjM2YTIiLCJzIjoiMHgyOTYyOTlhOGYyNDMwYjg2ZmQzZWI5NzZlYWJjNzMwYWMxY2ZiYmJlMzZlYjY5ZWFlMzM4Y2ZmMzNjNGE5OGMxIn19fX19");
let raw_tx = String::from("eyJzaWduYXR1cmUiOm51bGwsImZ1bmN0aW9uIjp7IkV0aGVyZXVtIjp7IlRyYW5zYWN0Ijp7IkxlZ2FjeSI6eyJub25jZSI6IjB4MCIsImdhc19wcmljZSI6IjB4MjU0MGJlNDAwIiwiZ2FzX2xpbWl0IjoiMHgxMDAwMDAiLCJhY3Rpb24iOnsiQ2FsbCI6IjB4MzMyNWE3ODQyNWYxN2E3ZTQ4N2ViNTY2NmIyYmZkOTNhYmIwNmM3MCJ9LCJ2YWx1ZSI6IjB4YSIsImlucHV0IjpbXSwic2lnbmF0dXJlIjp7InYiOjQzNDAsInIiOiIweDZiMjBjNzIzNTEzOTk4ZThmYTQ4NWM1MmI4ZjlhZTRmZDdiMWUwYmQwZGZiNzk4NTIzMThiMGMxMDBlOTFmNWUiLCJzIjoiMHg0ZDRjOGMxZjJlMTdjMDJjNGE4OTZlMjYyMTI3YjhiZDZlYmZkNWY1YTc1NWEzZTkyMjBjZmM2OGI4YzY5ZDVkIn19fX19fQ==");
let tx_bytes = base64::decode_config(raw_tx, base64::URL_SAFE).unwrap();
let evm_tx = EvmRawTxWrapper::unwrap(&tx_bytes).unwrap();
let unchecked_tx: UncheckedTransaction<()> =
serde_json::from_slice(evm_tx).unwrap();
serde_json::from_slice(tx_bytes.as_slice()).unwrap();
if let Action::Ethereum(EthAction::Transact(tx)) = unchecked_tx.function {
let tx: LegacyTransaction = new_tx2legcay_tx(tx).unwrap();
let signer = recover_signer(&tx).unwrap();
assert_eq!(
format!("{signer:?}",),
"0xa5225cbee5052100ec2d2d94aa6d258558073757"
format!("{signer:?}"),
"0x5050a4f4b3f9338c3472dcc01a87c76a144b3c9c"
);
} else {
panic!()
Expand All @@ -99,15 +163,16 @@ mod test {

#[test]
fn evm_tx_hash_works() {
let raw_tx = String::from("eyJzaWduYXR1cmUiOm51bGwsImZ1bmN0aW9uIjp7IkV0aGVyZXVtIjp7IlRyYW5zYWN0Ijp7Im5vbmNlIjoiMHg5IiwiZ2FzX3ByaWNlIjoiMHhlOGQ0YTUxMDAwIiwiZ2FzX2xpbWl0IjoiMHg1MjA4IiwiYWN0aW9uIjp7IkNhbGwiOiIweGE1MjI1Y2JlZTUwNTIxMDBlYzJkMmQ5NGFhNmQyNTg1NTgwNzM3NTcifSwidmFsdWUiOiIweDk4YTdkOWI4MzE0YzAwMDAiLCJpbnB1dCI6W10sInNpZ25hdHVyZSI6eyJ2IjoxMDgyLCJyIjoiMHg4MDBjZjQ5ZTAzMmJhYzY4MjY3MzdhZGJhZDEzN2Y0MTk5OTRjNjgxZWE1ZDUyYjliMGJhZDJmNDAyYjMwMTI0IiwicyI6IjB4Mjk1Mjc3ZWY2NTYzNDAwY2VkNjFiODhkM2ZiNGM3YjMyY2NkNTcwYThiOWJiOGNiYmUyNTkyMTRhYjdkZTI1YSJ9fX19fQ==");
let raw_tx = String::from("eyJzaWduYXR1cmUiOm51bGwsImZ1bmN0aW9uIjp7IkV0aGVyZXVtIjp7IlRyYW5zYWN0Ijp7IkxlZ2FjeSI6eyJub25jZSI6IjB4MCIsImdhc19wcmljZSI6IjB4MjU0MGJlNDAwIiwiZ2FzX2xpbWl0IjoiMHgxMDAwMDAiLCJhY3Rpb24iOnsiQ2FsbCI6IjB4MzMyNWE3ODQyNWYxN2E3ZTQ4N2ViNTY2NmIyYmZkOTNhYmIwNmM3MCJ9LCJ2YWx1ZSI6IjB4YSIsImlucHV0IjpbXSwic2lnbmF0dXJlIjp7InYiOjQzNDAsInIiOiIweDZiMjBjNzIzNTEzOTk4ZThmYTQ4NWM1MmI4ZjlhZTRmZDdiMWUwYmQwZGZiNzk4NTIzMThiMGMxMDBlOTFmNWUiLCJzIjoiMHg0ZDRjOGMxZjJlMTdjMDJjNGE4OTZlMjYyMTI3YjhiZDZlYmZkNWY1YTc1NWEzZTkyMjBjZmM2OGI4YzY5ZDVkIn19fX19fQ==");
let tx_bytes = base64::decode_config(raw_tx, base64::URL_SAFE).unwrap();
let unchecked_tx: UncheckedTransaction<()> =
serde_json::from_slice(tx_bytes.as_slice()).unwrap();
if let Action::Ethereum(EthAction::Transact(tx)) = unchecked_tx.function {
let tx: LegacyTransaction = new_tx2legcay_tx(tx).unwrap();
let hash = H256::from_slice(Keccak256::digest(&rlp::encode(&tx)).as_slice());
assert_eq!(
format!("{hash:?}",),
"0x0eeb0ff455b1b57b821634cf853e7247e584a675610f13097cc49c2022505df3"
format!("{hash:?}"),
"0x83901d025accca27ee53fdf1ee354f4437418731e0995ee031beb99499405d26"
);
} else {
panic!()
Expand Down
Loading