diff --git a/Cargo.lock b/Cargo.lock index 9b146334b..edea8be08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1147,9 +1147,9 @@ dependencies = [ [[package]] name = "ethereum" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df706418ff7d3874b9506424b04ea0bef569a2b39412b43a27ea86e679be108e" +checksum = "01ea35e1b0845310ade8eb048d401b0b899b8eff05c4d2a3454d29ef4eb1b8af" dependencies = [ "ethereum-types", "hash-db", @@ -1185,9 +1185,9 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" [[package]] name = "evm" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fc70736bd5ec89622647ea9346b70567557917596a39538d76e8f2a17ff59e" +checksum = "5286da2277b078e7033491d62e674eeccd01d913a87bb60ec0cabbcf80ec62e9" dependencies = [ "ethereum", "evm-core", @@ -1523,10 +1523,12 @@ dependencies = [ "jsonrpc-core-client 14.2.0", "jsonrpc-derive 14.2.2", "jsonrpc-pubsub 15.1.0", + "libsecp256k1", "log", "pallet-ethereum", "pallet-evm", "parity-scale-codec", + "rand 0.7.3", "rlp", "rustc-hex", "sc-client-api", @@ -7611,7 +7613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59" dependencies = [ "cfg-if 0.1.10", - "rand 0.7.3", + "rand 0.3.23", "static_assertions", ] diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 383d8a74d..83061ef46 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -23,4 +23,4 @@ futures = { version = "0.3.1", features = ["compat"] } sp-timestamp = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", branch = "frontier" } derive_more = "0.99.2" prometheus-endpoint = { package = "substrate-prometheus-endpoint", git = "https://github.com/paritytech/substrate.git", branch = "frontier"} -ethereum = { version = "0.4", features = ["with-codec"] } \ No newline at end of file +ethereum = { version = "0.5", features = ["with-codec"] } \ No newline at end of file diff --git a/frame/ethereum/Cargo.toml b/frame/ethereum/Cargo.toml index 364f9cf7b..9f0747efe 100644 --- a/frame/ethereum/Cargo.toml +++ b/frame/ethereum/Cargo.toml @@ -20,7 +20,7 @@ sp-std = { version = "2.0.0-dev", default-features = false, git = "https://githu sp-io = { version = "2.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-evm = { version = "0.8.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } evm = { version = "0.18.0", features = ["with-codec"], default-features = false } -ethereum = { version = "0.4", default-features = false, features = ["with-codec"] } +ethereum = { version = "0.5", default-features = false, features = ["with-codec"] } ethereum-types = { version = "0.9", default-features = false } rlp = { version = "0.4", default-features = false } sha3 = { version = "0.8", default-features = false } diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 8ea394551..8474dafcd 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -43,7 +43,7 @@ use codec::Encode; use frontier_consensus_primitives::{FRONTIER_ENGINE_ID, ConsensusLog}; pub use frontier_rpc_primitives::TransactionStatus; -pub use ethereum::{Transaction, Log, Block, Receipt, TransactionAction}; +pub use ethereum::{Transaction, Log, Block, Receipt, TransactionAction, TransactionMessage}; #[cfg(all(feature = "std", test))] mod tests; @@ -236,7 +236,7 @@ impl Module { sig[0..32].copy_from_slice(&transaction.signature.r()[..]); sig[32..64].copy_from_slice(&transaction.signature.s()[..]); sig[64] = transaction.signature.standard_v(); - msg.copy_from_slice(&transaction.message_hash(Some(T::ChainId::get()))[..]); + msg.copy_from_slice(&TransactionMessage::from(transaction.clone()).hash()[..]); let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg).ok()?; Some(H160::from(H256::from_slice(Keccak256::digest(&pubkey).as_slice()))) diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 86face1a3..001ec93ce 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -28,9 +28,11 @@ sc-rpc = { git = "https://github.com/paritytech/substrate.git", branch = "fronti sc-network = { git = "https://github.com/paritytech/substrate.git", branch = "frontier" } pallet-evm = { git = "https://github.com/paritytech/substrate.git", branch = "frontier" } pallet-ethereum = { path = "../frame/ethereum" } -ethereum = { version = "0.4", features = ["with-codec"] } +ethereum = { version = "0.5", features = ["with-codec"] } codec = { package = "parity-scale-codec", version = "1.0.0" } rlp = "0.4" futures = { version = "0.3.1", features = ["compat"] } sha3 = "0.8" rustc-hex = { version = "2.1.0", default-features = false } +libsecp256k1 = "0.3" +rand = "0.7" \ No newline at end of file diff --git a/rpc/core/src/eth.rs b/rpc/core/src/eth.rs index eb8a579f5..b175cf553 100644 --- a/rpc/core/src/eth.rs +++ b/rpc/core/src/eth.rs @@ -22,7 +22,7 @@ use jsonrpc_derive::rpc; use crate::types::{ BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, Log, Receipt, - RichBlock, SyncStatus, Transaction, Work, + RichBlock, SyncStatus, Transaction, Work, TransactionRequest, }; pub use rpc_impl_EthApi::gen_server::EthApi as EthApiServer; @@ -107,6 +107,11 @@ pub trait EthApi { #[rpc(name = "eth_getCode")] fn code_at(&self, _: H160, _: Option) -> Result; + /// Sends transaction; will block waiting for signer to return the + /// transaction hash. + #[rpc(name = "eth_sendTransaction")] + fn send_transaction(&self, _: TransactionRequest) -> BoxFuture; + /// Sends signed transaction, returning its hash. #[rpc(name = "eth_sendRawTransaction")] fn send_raw_transaction(&self, _: Bytes) -> BoxFuture; diff --git a/rpc/core/src/eth_signing.rs b/rpc/core/src/eth_signing.rs deleted file mode 100644 index b7d97bd37..000000000 --- a/rpc/core/src/eth_signing.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Frontier. - -// Open Ethereum 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. - -// Open Ethereum 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 Open Ethereum. If not, see . - -//! Eth rpc interface. - -use jsonrpc_core::BoxFuture; -use jsonrpc_derive::rpc; - -use ethereum_types::{H160, H256, H520}; -use crate::types::{Bytes, TransactionRequest, RichRawTransaction}; - -/// Signing methods implementation relying on unlocked accounts. -#[rpc(server)] -pub trait EthSigningApi { - /// RPC Metadata - type Metadata; - - /// Signs the hash of data with given address signature. - #[rpc(meta, name = "eth_sign")] - fn sign(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture; - - /// Sends transaction; will block waiting for signer to return the - /// transaction hash. - /// If Signer is disable it will require the account to be unlocked. - #[rpc(meta, name = "eth_sendTransaction")] - fn send_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; - - /// Signs transactions without dispatching it to the network. - /// Returns signed transaction RLP representation and the transaction itself. - /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. - #[rpc(meta, name = "eth_signTransaction")] - fn sign_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; -} diff --git a/rpc/core/src/lib.rs b/rpc/core/src/lib.rs index 55d3f7c3e..a9c8f415b 100644 --- a/rpc/core/src/lib.rs +++ b/rpc/core/src/lib.rs @@ -18,12 +18,10 @@ pub mod types; mod eth; mod eth_pubsub; -mod eth_signing; mod net; mod web3; pub use eth::{EthApi, EthApiServer, EthFilterApi}; pub use eth_pubsub::{EthPubSubApi, EthPubSubApiServer}; -pub use eth_signing::EthSigningApi; pub use net::{NetApi, NetApiServer}; pub use web3::Web3Api; diff --git a/rpc/core/src/types/mod.rs b/rpc/core/src/types/mod.rs index e0cba1809..d5f7680ab 100644 --- a/rpc/core/src/types/mod.rs +++ b/rpc/core/src/types/mod.rs @@ -28,7 +28,6 @@ mod receipt; mod sync; mod transaction; mod transaction_request; -mod transaction_condition; mod work; pub mod pubsub; @@ -48,5 +47,4 @@ pub use self::sync::{ }; pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; pub use self::transaction_request::TransactionRequest; -pub use self::transaction_condition::TransactionCondition; pub use self::work::Work; diff --git a/rpc/core/src/types/transaction.rs b/rpc/core/src/types/transaction.rs index ddb11b4db..8f5817fbf 100644 --- a/rpc/core/src/types/transaction.rs +++ b/rpc/core/src/types/transaction.rs @@ -17,7 +17,7 @@ use serde::{Serialize, Serializer}; use serde::ser::SerializeStruct; use ethereum_types::{H160, H256, H512, U64, U256}; -use crate::types::{Bytes, TransactionCondition}; +use crate::types::Bytes; /// Transaction #[derive(Debug, Default, Clone, PartialEq, Serialize)] @@ -61,8 +61,6 @@ pub struct Transaction { pub r: U256, /// The S field of the signature. pub s: U256, - /// Transaction activates at specified block. - pub condition: Option, } /// Local Transaction Status diff --git a/rpc/core/src/types/transaction_condition.rs b/rpc/core/src/types/transaction_condition.rs deleted file mode 100644 index 874319b2e..000000000 --- a/rpc/core/src/types/transaction_condition.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Frontier. - -// Open Ethereum 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. - -// Open Ethereum 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 Open Ethereum. If not, see . - -use serde::{Serialize, Deserialize}; - -/// Represents condition on minimum block number or block timestamp. -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub enum TransactionCondition { - /// Valid at this minimum block number. - #[serde(rename = "block")] - Number(u64), - /// Valid at given unix time. - #[serde(rename = "time")] - Timestamp(u64), -} diff --git a/rpc/core/src/types/transaction_request.rs b/rpc/core/src/types/transaction_request.rs index 9ce207be1..c3c1721fa 100644 --- a/rpc/core/src/types/transaction_request.rs +++ b/rpc/core/src/types/transaction_request.rs @@ -18,7 +18,7 @@ use serde::{Serialize, Deserialize}; use ethereum_types::{H160, U256}; -use crate::types::{Bytes, TransactionCondition}; +use crate::types::Bytes; /// Transaction request coming from RPC #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] @@ -39,6 +39,4 @@ pub struct TransactionRequest { pub data: Option, /// Transaction's nonce pub nonce: Option, - /// Delay until this block condition. - pub condition: Option, } diff --git a/rpc/primitives/Cargo.toml b/rpc/primitives/Cargo.toml index e1eb6e932..98a15eeb1 100644 --- a/rpc/primitives/Cargo.toml +++ b/rpc/primitives/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0" sp-core = { version = "2.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-api = { version = "2.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-evm = { version = "0.8.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } -ethereum = { version = "0.4", default-features = false, features = ["with-codec"] } +ethereum = { version = "0.5", default-features = false, features = ["with-codec"] } ethereum-types = { version = "0.9", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } sp-runtime = { version = "2.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } diff --git a/rpc/src/eth.rs b/rpc/src/eth.rs index 1a22e9c0f..fa9758e00 100644 --- a/rpc/src/eth.rs +++ b/rpc/src/eth.rs @@ -16,7 +16,10 @@ use std::{marker::PhantomData, sync::Arc}; use std::collections::BTreeMap; -use ethereum::{Block as EthereumBlock, Transaction as EthereumTransaction}; +use ethereum::{ + Block as EthereumBlock, Transaction as EthereumTransaction, + TransactionMessage as EthereumTransactionMessage, +}; use ethereum_types::{H160, H256, H64, U256, U64, H512}; use jsonrpc_core::{BoxFuture, Result, futures::future::{self, Future}}; use futures::future::TryFutureExt; @@ -33,10 +36,11 @@ use sc_network::{NetworkService, ExHashT}; use frontier_rpc_core::{EthApi as EthApiT, NetApi as NetApiT}; use frontier_rpc_core::types::{ BlockNumber, Bytes, CallRequest, Filter, FilteredParams, Index, Log, Receipt, RichBlock, - SyncStatus, SyncInfo, Transaction, Work, Rich, Block, BlockTransactions, VariadicValue + SyncStatus, SyncInfo, Transaction, Work, Rich, Block, BlockTransactions, VariadicValue, + TransactionRequest, }; use frontier_rpc_primitives::{EthereumRuntimeRPCApi, ConvertTransaction, TransactionStatus}; -use crate::{internal_err, error_on_execution_failure}; +use crate::{internal_err, error_on_execution_failure, EthSigner}; pub use frontier_rpc_core::{EthApiServer, NetApiServer}; use codec::{self, Encode}; @@ -47,6 +51,7 @@ pub struct EthApi { convert_transaction: CT, network: Arc>, is_authority: bool, + signers: Vec>, _marker: PhantomData<(B, BE)>, } @@ -58,7 +63,15 @@ impl EthApi { network: Arc>, is_authority: bool ) -> Self { - Self { client, pool, convert_transaction, network, is_authority, _marker: PhantomData } + Self { + client, + pool, + convert_transaction, + network, + is_authority, + signers: Vec::new(), + _marker: PhantomData, + } } } @@ -132,9 +145,7 @@ fn transaction_build( sig[0..32].copy_from_slice(&transaction.signature.r()[..]); sig[32..64].copy_from_slice(&transaction.signature.s()[..]); sig[64] = transaction.signature.standard_v(); - msg.copy_from_slice(&transaction.message_hash( - transaction.signature.chain_id().map(u64::from) - )[..]); + msg.copy_from_slice(&EthereumTransactionMessage::from(transaction.clone()).hash()[..]); let pubkey = match sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) { Ok(p) => Some(H512::from(p)), @@ -169,7 +180,6 @@ fn transaction_build( v: U256::from(transaction.signature.v()), r: U256::from(transaction.signature.r().as_bytes()), s: U256::from(transaction.signature.s().as_bytes()), - condition: None // TODO } } @@ -304,7 +314,11 @@ impl EthApiT for EthApi where } fn accounts(&self) -> Result> { - Ok(vec![]) + let mut accounts = Vec::new(); + for signer in &self.signers { + accounts.append(&mut signer.accounts()); + } + Ok(accounts) } fn block_number(&self) -> Result { @@ -486,6 +500,83 @@ impl EthApiT for EthApi where Ok(Bytes(vec![])) } + fn send_transaction(&self, request: TransactionRequest) -> BoxFuture { + let from = match request.from { + Some(from) => from, + None => { + let accounts = match self.accounts() { + Ok(accounts) => accounts, + Err(e) => return Box::new(future::result(Err(e))), + }; + + match accounts.get(0) { + Some(account) => account.clone(), + None => return Box::new(future::result(Err(internal_err("no signer available")))), + } + }, + }; + + let nonce = match request.nonce { + Some(nonce) => nonce, + None => { + match self.transaction_count(from, None) { + Ok(nonce) => nonce, + Err(e) => return Box::new(future::result(Err(e))), + } + }, + }; + + let chain_id = match self.chain_id() { + Ok(chain_id) => chain_id, + Err(e) => return Box::new(future::result(Err(e))), + }; + + let message = ethereum::TransactionMessage { + nonce, + gas_price: request.gas_price.unwrap_or(U256::from(1)), + gas_limit: request.gas.unwrap_or(U256::max_value()), + value: request.value.unwrap_or(U256::zero()), + input: request.data.map(|s| s.into_vec()).unwrap_or_default(), + action: match request.to { + Some(to) => ethereum::TransactionAction::Call(to), + None => ethereum::TransactionAction::Create, + }, + chain_id: chain_id.map(|s| s.as_u64()), + }; + + let mut transaction = None; + + for signer in &self.signers { + if signer.accounts().contains(&from) { + match signer.sign(message) { + Ok(t) => transaction = Some(t), + Err(e) => return Box::new(future::result(Err(e))), + } + break + } + } + + let transaction = match transaction { + Some(transaction) => transaction, + None => return Box::new(future::result(Err(internal_err("no signer available")))), + }; + let transaction_hash = H256::from_slice( + Keccak256::digest(&rlp::encode(&transaction)).as_slice() + ); + let hash = self.client.info().best_hash; + Box::new( + self.pool + .submit_one( + &BlockId::hash(hash), + TransactionSource::Local, + self.convert_transaction.convert_transaction(transaction), + ) + .compat() + .map(move |_| transaction_hash) + .map_err(|err| internal_err(format!("submit transaction to pool failed: {:?}", err))) + ) + } + fn send_raw_transaction(&self, bytes: Bytes) -> BoxFuture { let transaction = match rlp::decode::(&bytes.0[..]) { Ok(transaction) => transaction, diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index a7814746f..647c6e419 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -20,6 +20,7 @@ mod eth_pubsub; pub use eth::{EthApi, EthApiServer, NetApi, NetApiServer}; pub use eth_pubsub::{EthPubSubApi, EthPubSubApiServer}; +use ethereum_types::H160; use jsonrpc_core::{ErrorCode, Error, Value}; use rustc_hex::ToHex; use pallet_evm::ExitReason; @@ -58,3 +59,11 @@ pub fn error_on_execution_failure(reason: &ExitReason, data: &[u8]) -> Result<() }, } } + +/// A generic Ethereum signer. +pub trait EthSigner: Send + Sync { + /// Available accounts from this signer. + fn accounts(&self) -> Vec; + /// Sign a transaction message using the given account in message. + fn sign(&self, message: ethereum::TransactionMessage) -> Result; +} diff --git a/ts-tests/tests/test-revert-receipt.ts b/ts-tests/tests/test-revert-receipt.ts index f46a4857e..b032f6224 100644 --- a/ts-tests/tests/test-revert-receipt.ts +++ b/ts-tests/tests/test-revert-receipt.ts @@ -52,7 +52,7 @@ describeWithFrontier("Frontier RPC (Constructor Revert)", `simple-specs.json`, ( id: 1, jsonrpc: "2.0", result: { - "blockHash": "0xfe01d44b7f1c13e36819ecc6daf39fc28c57e6e4f6646036d7d8b79ed940fb91", + "blockHash": "0x523389d52a34094ad401153dd8486fe6af7099da388a4728ed3b09e285ca8604", "blockNumber": "0x1", "contractAddress": "0xc2bf5f29a4384b1ab0c063e1c666f02121b6084a", "cumulativeGasUsed": "0x1069f", @@ -100,7 +100,7 @@ describeWithFrontier("Frontier RPC (Constructor Revert)", `simple-specs.json`, ( id: 1, jsonrpc: "2.0", result: { - "blockHash": "0x8761d0bf47b6644e9e420d16b6fe046420c609a9d990e9432c30b254e02902d0", + "blockHash": "0x459bb7c418c22239392a4a1e7c2190afbcc6fbd87cb6d547509deac588f27536", "blockNumber": "0x2", "contractAddress": "0x5c4242beb94de30b922f57241f1d02f36e906915", "cumulativeGasUsed": "0xd548",