From 48f7efd15a23d41da83615ae6790f669de4cb7c2 Mon Sep 17 00:00:00 2001 From: bear <2630582710@qq.com> Date: Tue, 23 Mar 2021 15:35:23 +0800 Subject: [PATCH] Dvm update from 304 (#559) * Companion for 304 * Companion for 302 * Companion for 324 * Companion for 326 --- Cargo.lock | 11 ++ Cargo.toml | 1 + client/dvm/db/src/lib.rs | 19 +- client/dvm/mapping-sync/src/lib.rs | 87 ++++----- frame/dvm/src/lib.rs | 172 +++++++++--------- .../precompile/contracts/sha3fips/Cargo.toml | 39 ++++ .../precompile/contracts/sha3fips/src/lib.rs | 147 +++++++++++++++ .../precompile/contracts/simple/src/lib.rs | 26 +++ frame/evm/src/runner/stack.rs | 2 - 9 files changed, 377 insertions(+), 127 deletions(-) create mode 100644 frame/evm/precompile/contracts/sha3fips/Cargo.toml create mode 100644 frame/evm/precompile/contracts/sha3fips/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 485abf8498..c1fa8447df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1552,6 +1552,17 @@ dependencies = [ "sp-io", ] +[[package]] +name = "darwinia-evm-precompile-sha3fips" +version = "2.0.0" +dependencies = [ + "dp-evm", + "evm", + "sp-core", + "sp-io", + "tiny-keccak", +] + [[package]] name = "darwinia-evm-precompile-simple" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index e3a638008a..d27dbdf9a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ members = [ "frame/evm/precompile/contracts/modexp", "frame/evm/precompile/contracts/simple", "frame/evm/precompile/contracts/withdraw", + "frame/evm/precompile/contracts/sha3fips", "frame/header-mmr", "frame/header-mmr/rpc", "frame/header-mmr/rpc/runtime-api", diff --git a/client/dvm/db/src/lib.rs b/client/dvm/db/src/lib.rs index 8361fa764a..854ab55567 100644 --- a/client/dvm/db/src/lib.rs +++ b/client/dvm/db/src/lib.rs @@ -64,11 +64,12 @@ impl DatabaseSettingsSrc { } pub(crate) mod columns { - pub const NUM_COLUMNS: u32 = 3; + pub const NUM_COLUMNS: u32 = 4; pub const META: u32 = 0; pub const BLOCK_MAPPING: u32 = 1; pub const TRANSACTION_MAPPING: u32 = 2; + pub const SYNCED_MAPPING: u32 = 3; } pub(crate) mod static_keys { @@ -161,6 +162,16 @@ pub struct MappingDb { } impl MappingDb { + pub fn is_synced(&self, block_hash: &Block::Hash) -> Result { + match self + .db + .get(crate::columns::SYNCED_MAPPING, &block_hash.encode()) + { + Some(raw) => Ok(bool::decode(&mut &raw[..]).map_err(|e| format!("{:?}", e))?), + None => Ok(false), + } + } + pub fn block_hashes(&self, ethereum_block_hash: &H256) -> Result, String> { match self .db @@ -218,6 +229,12 @@ impl MappingDb { ); } + transaction.set( + crate::columns::SYNCED_MAPPING, + &commitment.block_hash.encode(), + &true.encode(), + ); + self.db .commit(transaction) .map_err(|e| format!("{:?}", e))?; diff --git a/client/dvm/mapping-sync/src/lib.rs b/client/dvm/mapping-sync/src/lib.rs index 658c49f033..4344e01655 100644 --- a/client/dvm/mapping-sync/src/lib.rs +++ b/client/dvm/mapping-sync/src/lib.rs @@ -77,7 +77,7 @@ where Ok(()) } -pub fn sync_one_level( +pub fn sync_one_block( client: &C, substrate_backend: &B, frontier_backend: &dc_db::Backend, @@ -89,55 +89,58 @@ where { let mut current_syncing_tips = frontier_backend.meta().current_syncing_tips()?; - if current_syncing_tips.len() == 0 { - // Sync genesis block. - - let header = substrate_backend - .header(BlockId::Number(Zero::zero())) - .map_err(|e| format!("{:?}", e))? - .ok_or("Genesis header not found".to_string())?; - sync_genesis_block(client, frontier_backend, &header)?; - - current_syncing_tips.push(header.hash()); - frontier_backend - .meta() - .write_current_syncing_tips(current_syncing_tips)?; + if current_syncing_tips.is_empty() { + let mut leaves = substrate_backend.leaves().map_err(|e| format!("{:?}", e))?; + if leaves.is_empty() { + return Ok(false); + } - Ok(true) - } else { - let mut syncing_tip_and_children = None; + current_syncing_tips.append(&mut leaves); + } - for tip in ¤t_syncing_tips { - let children = substrate_backend - .children(*tip) - .map_err(|e| format!("{:?}", e))?; + let mut operating_tip = None; - if children.len() > 0 { - syncing_tip_and_children = Some((*tip, children)); - break; - } + while let Some(checking_tip) = current_syncing_tips.pop() { + if !frontier_backend + .mapping() + .is_synced(&checking_tip) + .map_err(|e| format!("{:?}", e))? + { + operating_tip = Some(checking_tip); + break; } + } - if let Some((syncing_tip, children)) = syncing_tip_and_children { - current_syncing_tips.retain(|tip| tip != &syncing_tip); - - for child in children { - let header = substrate_backend - .header(BlockId::Hash(child)) - .map_err(|e| format!("{:?}", e))? - .ok_or("Header not found".to_string())?; - - sync_block(frontier_backend, &header)?; - current_syncing_tips.push(child); - } + let operating_tip = match operating_tip { + Some(operating_tip) => operating_tip, + None => { frontier_backend .meta() .write_current_syncing_tips(current_syncing_tips)?; - - Ok(true) - } else { - Ok(false) + return Ok(false); } + }; + + let operating_header = substrate_backend + .header(BlockId::Hash(operating_tip)) + .map_err(|e| format!("{:?}", e))? + .ok_or("Header not found".to_string())?; + + if operating_header.number() == &Zero::zero() { + sync_genesis_block(client, frontier_backend, &operating_header)?; + + frontier_backend + .meta() + .write_current_syncing_tips(current_syncing_tips)?; + Ok(true) + } else { + sync_block(frontier_backend, &operating_header)?; + + current_syncing_tips.push(*operating_header.parent_hash()); + frontier_backend + .meta() + .write_current_syncing_tips(current_syncing_tips)?; + Ok(true) } } @@ -155,7 +158,7 @@ where let mut synced_any = false; for _ in 0..limit { - synced_any = synced_any || sync_one_level(client, substrate_backend, frontier_backend)?; + synced_any = synced_any || sync_one_block(client, substrate_backend, frontier_backend)?; } Ok(synced_any) diff --git a/frame/dvm/src/lib.rs b/frame/dvm/src/lib.rs index 65f7ff3f22..b5926714ea 100644 --- a/frame/dvm/src/lib.rs +++ b/frame/dvm/src/lib.rs @@ -30,13 +30,13 @@ use dp_evm::CallOrCreateInfo; use dp_storage::PALLET_ETHEREUM_SCHEMA; pub use dvm_rpc_runtime_api::TransactionStatus; // --- substrate --- +use frame_support::ensure; use frame_support::traits::Currency; use frame_support::{ decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResultWithPostInfo, traits::FindAuthor, traits::Get, weights::Weight, }; -use frame_support::{ensure, traits::UnfilteredDispatchable}; -use frame_system::{ensure_none, RawOrigin}; +use frame_system::ensure_none; use sp_runtime::{ generic::DigestItem, traits::{Saturating, UniqueSaturatedInto}, @@ -162,85 +162,7 @@ decl_module! { fn transact(origin, transaction: ethereum::Transaction) -> DispatchResultWithPostInfo { ensure_none(origin)?; - ensure!( - dp_consensus::find_pre_log(&frame_system::Module::::digest()).is_err(), - Error::::PreLogExists, - ); - - let source = Self::recover_signer(&transaction) - .ok_or_else(|| Error::::InvalidSignature)?; - - let transaction_hash = H256::from_slice( - Keccak256::digest(&rlp::encode(&transaction)).as_slice() - ); - let transaction_index = Pending::get().len() as u32; - - let (to, contract_address, info) = Self::execute( - source, - transaction.input.clone(), - transaction.value, - transaction.gas_limit, - Some(transaction.gas_price), - Some(transaction.nonce), - transaction.action, - None, - )?; - - let (reason, status, used_gas) = match info { - CallOrCreateInfo::Call(info) => { - (info.exit_reason, TransactionStatus { - transaction_hash, - transaction_index, - from: source, - to, - contract_address: None, - logs: info.logs.clone(), - logs_bloom: { - let mut bloom: Bloom = Bloom::default(); - Self::logs_bloom( - info.logs, - &mut bloom - ); - bloom - }, - }, info.used_gas) - }, - CallOrCreateInfo::Create(info) => { - (info.exit_reason, TransactionStatus { - transaction_hash, - transaction_index, - from: source, - to, - contract_address: Some(info.value), - logs: info.logs.clone(), - logs_bloom: { - let mut bloom: Bloom = Bloom::default(); - Self::logs_bloom( - info.logs, - &mut bloom - ); - bloom - }, - }, info.used_gas) - }, - }; - - let receipt = ethereum::Receipt { - state_root: match reason { - ExitReason::Succeed(_) => H256::from_low_u64_be(1), - ExitReason::Error(_) => H256::from_low_u64_le(0), - ExitReason::Revert(_) => H256::from_low_u64_le(0), - ExitReason::Fatal(_) => H256::from_low_u64_le(0), - }, - used_gas, - logs_bloom: status.clone().logs_bloom, - logs: status.clone().logs, - }; - - Pending::append((transaction, status, receipt)); - - Self::deposit_event(Event::Executed(source, contract_address.unwrap_or_default(), transaction_hash, reason)); - Ok(Some(T::GasWeightMapping::gas_to_weight(used_gas.unique_saturated_into())).into()) + Self::do_transact(transaction) } fn on_finalize(_block_number: T::BlockNumber) { @@ -255,7 +177,7 @@ decl_module! { let PreLog::Block(block) = log; for transaction in block.transactions { - let _ = Call::::transact(transaction).dispatch_bypass_filter(RawOrigin::None.into()); + Self::do_transact(transaction).expect("pre-block transaction verification failed; the block cannot be built"); } } 0 @@ -429,6 +351,92 @@ impl Module { } } + fn do_transact(transaction: ethereum::Transaction) -> DispatchResultWithPostInfo { + ensure!( + dp_consensus::find_pre_log(&frame_system::Module::::digest()).is_err(), + Error::::PreLogExists, + ); + let source = + Self::recover_signer(&transaction).ok_or_else(|| Error::::InvalidSignature)?; + + let transaction_hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + let transaction_index = Pending::get().len() as u32; + + let (to, contract_address, info) = Self::execute( + source, + transaction.input.clone(), + transaction.value, + transaction.gas_limit, + Some(transaction.gas_price), + Some(transaction.nonce), + transaction.action, + None, + )?; + + let (reason, status, used_gas) = match info { + CallOrCreateInfo::Call(info) => ( + info.exit_reason, + TransactionStatus { + transaction_hash, + transaction_index, + from: source, + to, + contract_address: None, + logs: info.logs.clone(), + logs_bloom: { + let mut bloom: Bloom = Bloom::default(); + Self::logs_bloom(info.logs, &mut bloom); + bloom + }, + }, + info.used_gas, + ), + CallOrCreateInfo::Create(info) => ( + info.exit_reason, + TransactionStatus { + transaction_hash, + transaction_index, + from: source, + to, + contract_address: Some(info.value), + logs: info.logs.clone(), + logs_bloom: { + let mut bloom: Bloom = Bloom::default(); + Self::logs_bloom(info.logs, &mut bloom); + bloom + }, + }, + info.used_gas, + ), + }; + + let receipt = ethereum::Receipt { + state_root: match reason { + ExitReason::Succeed(_) => H256::from_low_u64_be(1), + ExitReason::Error(_) => H256::from_low_u64_le(0), + ExitReason::Revert(_) => H256::from_low_u64_le(0), + ExitReason::Fatal(_) => H256::from_low_u64_le(0), + }, + used_gas, + logs_bloom: status.clone().logs_bloom, + logs: status.clone().logs, + }; + + Pending::append((transaction, status, receipt)); + + Self::deposit_event(Event::Executed( + source, + contract_address.unwrap_or_default(), + transaction_hash, + reason, + )); + Ok(Some(T::GasWeightMapping::gas_to_weight( + used_gas.unique_saturated_into(), + )) + .into()) + } + /// Get the author using the FindAuthor trait. pub fn find_author() -> H160 { let digest = >::digest(); diff --git a/frame/evm/precompile/contracts/sha3fips/Cargo.toml b/frame/evm/precompile/contracts/sha3fips/Cargo.toml new file mode 100644 index 0000000000..4f08290671 --- /dev/null +++ b/frame/evm/precompile/contracts/sha3fips/Cargo.toml @@ -0,0 +1,39 @@ +[package] +authors = ["Darwinia Network "] +description = "SHA3 FIPS202 precompile for EVM pallet." +edition = "2018" +homepage = "https://darwinia.network/" +license = "GPL-3.0" +name = "darwinia-evm-precompile-sha3fips" +readme = "README.md" +repository = "https://github.com/darwinia-network/darwinia-common/" +version = "2.0.0" + + +[dependencies] +# crates +dp-evm = { default-features = false, path = "../../../../../primitives/evm" } +evm = { version = "0.24.0", default-features = false, features = ["with-codec"] } +tiny-keccak = { version = "2.0", features = ["fips202"] } + +# substrate +sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", tag = "rococo-v1.1" } +sp-io = { default-features = false, git = "https://github.com/darwinia-network/substrate.git", tag = "rococo-v1.1" } + +[features] +default = ["std"] + +std = [ + "crates-std", + "substrate-std", +] + +crates-std = [ + "dp-evm/std", + "evm/std", +] + +substrate-std = [ + "sp-core/std", + "sp-io/std", +] diff --git a/frame/evm/precompile/contracts/sha3fips/src/lib.rs b/frame/evm/precompile/contracts/sha3fips/src/lib.rs new file mode 100644 index 0000000000..81c80df164 --- /dev/null +++ b/frame/evm/precompile/contracts/sha3fips/src/lib.rs @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::vec::Vec; +use tiny_keccak::Hasher; + +use dp_evm::LinearCostPrecompile; +use evm::{ExitError, ExitSucceed}; + +pub struct Sha3FIPS256; + +impl LinearCostPrecompile for Sha3FIPS256 { + const BASE: u64 = 60; + const WORD: u64 = 12; + + fn execute(input: &[u8], _: u64) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + let mut output = [0; 32]; + let mut sha3 = tiny_keccak::Sha3::v256(); + sha3.update(input); + sha3.finalize(&mut output); + Ok((ExitSucceed::Returned, output.to_vec())) + } +} + +pub struct Sha3FIPS512; + +impl LinearCostPrecompile for Sha3FIPS512 { + const BASE: u64 = 60; + const WORD: u64 = 12; + + fn execute(input: &[u8], _: u64) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + let mut output = [0; 64]; + let mut sha3 = tiny_keccak::Sha3::v512(); + sha3.update(input); + sha3.finalize(&mut output); + Ok((ExitSucceed::Returned, output.to_vec())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_input() -> std::result::Result<(), ExitError> { + let input: [u8; 0] = []; + let expected = b"\ + \xa7\xff\xc6\xf8\xbf\x1e\xd7\x66\x51\xc1\x47\x56\xa0\x61\xd6\x62\ + \xf5\x80\xff\x4d\xe4\x3b\x49\xfa\x82\xd8\x0a\x4b\x80\xf8\x43\x4a\ + "; + + let cost: u64 = 1; + + match Sha3FIPS256::execute(&input, cost) { + Ok((_, out)) => { + assert_eq!(out, expected); + Ok(()) + } + Err(e) => { + panic!("Test not expected to fail: {:?}", e); + } + } + } + + #[test] + fn hello_sha3_256() -> std::result::Result<(), ExitError> { + let input = b"hello"; + let expected = b"\ + \x33\x38\xbe\x69\x4f\x50\xc5\xf3\x38\x81\x49\x86\xcd\xf0\x68\x64\ + \x53\xa8\x88\xb8\x4f\x42\x4d\x79\x2a\xf4\xb9\x20\x23\x98\xf3\x92\ + "; + + let cost: u64 = 1; + + match Sha3FIPS256::execute(input, cost) { + Ok((_, out)) => { + assert_eq!(out, expected); + Ok(()) + } + Err(e) => { + panic!("Test not expected to fail: {:?}", e); + } + } + } + + #[test] + fn long_string_sha3_256() -> std::result::Result<(), ExitError> { + let input = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + let expected = b"\ + \xbd\xe3\xf2\x69\x17\x5e\x1d\xcd\xa1\x38\x48\x27\x8a\xa6\x04\x6b\ + \xd6\x43\xce\xa8\x5b\x84\xc8\xb8\xbb\x80\x95\x2e\x70\xb6\xea\xe0\ + "; + + let cost: u64 = 1; + + match Sha3FIPS256::execute(input, cost) { + Ok((_, out)) => { + assert_eq!(out, expected); + Ok(()) + } + Err(e) => { + panic!("Test not expected to fail: {:?}", e); + } + } + } + + #[test] + fn long_string_sha3_512() -> std::result::Result<(), ExitError> { + let input = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + let expected = b"\ + \xf3\x2a\x94\x23\x55\x13\x51\xdf\x0a\x07\xc0\xb8\xc2\x0e\xb9\x72\ + \x36\x7c\x39\x8d\x61\x06\x60\x38\xe1\x69\x86\x44\x8e\xbf\xbc\x3d\ + \x15\xed\xe0\xed\x36\x93\xe3\x90\x5e\x9a\x8c\x60\x1d\x9d\x00\x2a\ + \x06\x85\x3b\x97\x97\xef\x9a\xb1\x0c\xbd\xe1\x00\x9c\x7d\x0f\x09\ + "; + + let cost: u64 = 1; + + match Sha3FIPS512::execute(input, cost) { + Ok((_, out)) => { + assert_eq!(out, expected); + Ok(()) + } + Err(e) => { + panic!("Test not expected to fail: {:?}", e); + } + } + } +} diff --git a/frame/evm/precompile/contracts/simple/src/lib.rs b/frame/evm/precompile/contracts/simple/src/lib.rs index 02b94dfee3..b4af1fe8f3 100644 --- a/frame/evm/precompile/contracts/simple/src/lib.rs +++ b/frame/evm/precompile/contracts/simple/src/lib.rs @@ -98,3 +98,29 @@ impl LinearCostPrecompile for Sha256 { Ok((ExitSucceed::Returned, ret.to_vec())) } } + +/// The ecrecover precompile. +pub struct ECRecoverPublicKey; + +impl LinearCostPrecompile for ECRecoverPublicKey { + const BASE: u64 = 3000; + const WORD: u64 = 0; + + fn execute(i: &[u8], _: u64) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + let mut input = [0u8; 128]; + input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); + + let mut msg = [0u8; 32]; + let mut sig = [0u8; 65]; + + msg[0..32].copy_from_slice(&input[0..32]); + sig[0..32].copy_from_slice(&input[64..96]); + sig[32..64].copy_from_slice(&input[96..128]); + sig[64] = input[63]; + + let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) + .map_err(|_| ExitError::Other("Public key recover failed".into()))?; + + Ok((ExitSucceed::Returned, pubkey.to_vec())) + } +} diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 73f0b457c5..4b8e6e0362 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -282,7 +282,6 @@ impl<'config> SubstrateStackSubstate<'config> { mem::swap(&mut exited, self); self.metadata.swallow_revert(exited.metadata)?; - self.logs.append(&mut exited.logs); sp_io::storage::rollback_transaction(); Ok(()) @@ -293,7 +292,6 @@ impl<'config> SubstrateStackSubstate<'config> { mem::swap(&mut exited, self); self.metadata.swallow_discard(exited.metadata)?; - self.logs.append(&mut exited.logs); sp_io::storage::rollback_transaction(); Ok(())