From 720e234ada201473d87ce92a1c7c92a9568b851d Mon Sep 17 00:00:00 2001 From: firke Date: Tue, 28 Dec 2021 16:20:04 +0000 Subject: [PATCH] Add EVM to unorthodox collator setup. (#115) * Add EVM to unorthodox collator setup. * Fix Ethereum block import on new sync. * Make tests run daily. * Cargo fmt run. * Update runtime versions on opportunity/standard. --- .github/workflows/fmt-checks-tests.yml | 2 +- Cargo.lock | 20 ++ node/opportunity/Cargo.toml | 1 + node/opportunity/src/main.rs | 3 - node/opportunity/src/rpc.rs | 8 +- node/opportunity/src/service.rs | 35 +-- node/standard/Cargo.toml | 18 +- node/standard/src/chain_spec.rs | 40 ++- node/standard/src/command.rs | 2 +- node/standard/src/lib.rs | 3 - node/standard/src/rpc.rs | 146 ++++++++-- node/standard/src/service.rs | 170 ++++++++---- primitives/src/lib.rs | 11 +- runtime/opportunity/src/constants.rs | 5 +- runtime/opportunity/src/lib.rs | 19 +- runtime/opportunity/src/precompiles.rs | 17 -- runtime/standard/Cargo.toml | 26 +- runtime/standard/src/constants.rs | 2 +- runtime/standard/src/lib.rs | 359 +++++++++++++++++++++---- runtime/standard/src/precompiles.rs | 60 +++++ 20 files changed, 741 insertions(+), 206 deletions(-) delete mode 100644 node/standard/src/lib.rs create mode 100644 runtime/standard/src/precompiles.rs diff --git a/.github/workflows/fmt-checks-tests.yml b/.github/workflows/fmt-checks-tests.yml index eb28fdc4..00a606da 100644 --- a/.github/workflows/fmt-checks-tests.yml +++ b/.github/workflows/fmt-checks-tests.yml @@ -22,7 +22,7 @@ on: - "primitives/**" - "runtime/**" schedule: - - cron: "0 10 * * FRI" + - cron: "0 10 * * *" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/Cargo.lock b/Cargo.lock index 2bad8305..6401e9fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10590,13 +10590,24 @@ dependencies = [ "cumulus-client-service", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", + "fc-consensus", + "fc-db", + "fc-mapping-sync", + "fc-rpc", + "fc-rpc-core", + "fp-rpc", "frame-benchmarking", "frame-benchmarking-cli", + "futures 0.3.19", "hex", "hex-literal", "jsonrpc-core", + "jsonrpc-pubsub", "lazy_static", "log", + "pallet-dynamic-fee", + "pallet-ethereum", + "pallet-evm", "pallet-transaction-payment-rpc", "parity-scale-codec", "polkadot-cli", @@ -10658,6 +10669,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-primitives-timestamp", "cumulus-primitives-utility", + "fp-rpc", "frame-benchmarking", "frame-election-provider-support", "frame-executive", @@ -10672,8 +10684,15 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-collator-selection", + "pallet-dynamic-fee", "pallet-election-provider-multi-phase", + "pallet-ethereum", + "pallet-evm", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", "pallet-im-online", + "pallet-indices", "pallet-offences", "pallet-scheduler", "pallet-session", @@ -10696,6 +10715,7 @@ dependencies = [ "primitives", "scale-info", "serde", + "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", diff --git a/node/opportunity/Cargo.toml b/node/opportunity/Cargo.toml index 8300e060..f5f6ea6b 100644 --- a/node/opportunity/Cargo.toml +++ b/node/opportunity/Cargo.toml @@ -11,6 +11,7 @@ description = 'Opportunity Node Client.' [[bin]] name = 'opportunity-standalone' +path = "src/main.rs" [package.metadata.docs.rs] targets = [ 'x86_64-unknown-linux-gnu' ] diff --git a/node/opportunity/src/main.rs b/node/opportunity/src/main.rs index 4449d28b..ef89b752 100644 --- a/node/opportunity/src/main.rs +++ b/node/opportunity/src/main.rs @@ -1,6 +1,3 @@ -//! Substrate Node Template CLI library. -#![warn(missing_docs)] - mod chain_spec; #[macro_use] mod service; diff --git a/node/opportunity/src/rpc.rs b/node/opportunity/src/rpc.rs index 70c58bf9..cbf20620 100644 --- a/node/opportunity/src/rpc.rs +++ b/node/opportunity/src/rpc.rs @@ -116,7 +116,7 @@ pub struct FullDeps { /// EthFilterApi pool. pub filter_pool: FilterPool, /// Backend. - pub backend: Arc>, + pub frontier_backend: Arc>, /// The Node authority flag pub is_authority: bool, /// Network service @@ -167,7 +167,7 @@ where grandpa, network, filter_pool, - backend, + frontier_backend, transaction_converter, is_authority, } = deps; @@ -237,7 +237,7 @@ where network.clone(), Default::default(), overrides.clone(), - backend.clone(), + frontier_backend.clone(), is_authority, max_past_logs, block_data_cache.clone(), @@ -245,7 +245,7 @@ where io.extend_with(EthFilterApiServer::to_delegate(EthFilterApi::new( client.clone(), - backend, + frontier_backend, filter_pool, max_stored_filters, overrides.clone(), diff --git a/node/opportunity/src/service.rs b/node/opportunity/src/service.rs index 1cf4e23a..346e1a59 100644 --- a/node/opportunity/src/service.rs +++ b/node/opportunity/src/service.rs @@ -81,11 +81,7 @@ pub fn new_partial( sc_rpc::SubscriptionTaskExecutor, ) -> RpcResult, ( - sc_consensus_babe::BabeBlockImport< - Block, - FullClient, - FrontierBlockImport, - >, + sc_consensus_babe::BabeBlockImport, sc_finality_grandpa::LinkHalf, sc_consensus_babe::BabeLink, ), @@ -137,8 +133,6 @@ pub fn new_partial( client.clone(), ); - let frontier_backend = open_frontier_backend(config)?; - let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( client.clone(), &(client.clone() as Arc<_>), @@ -148,6 +142,8 @@ pub fn new_partial( let justification_import = grandpa_block_import.clone(); + let frontier_backend = open_frontier_backend(config)?; + let frontier_block_import = FrontierBlockImport::new( grandpa_block_import.clone(), client.clone(), @@ -156,16 +152,10 @@ pub fn new_partial( let (babe_import, babe_link) = sc_consensus_babe::block_import( sc_consensus_babe::Config::get_or_compute(&*client)?, - frontier_block_import, + grandpa_block_import, client.clone(), )?; - // let (babe_import, babe_link) = sc_consensus_babe::block_import( - // sc_consensus_babe::Config::get_or_compute(&*client)?, - // grandpa_block_import, - // client.clone(), - // )?; - let slot_duration = babe_link.config().slot_duration(); let import_queue = sc_consensus_babe::import_queue( @@ -215,8 +205,6 @@ pub fn new_partial( let chain_spec = config.chain_spec.cloned_box(); let backend = frontier_backend.clone(); let filter_pool: FilterPool = Arc::new(std::sync::Mutex::new(BTreeMap::new())); - // let filter_pool = filter_pool.clone(); - // let pending_transactions = pending_transactions.clone(); move |deny_unsafe, is_authority, network, subscription_executor| -> RpcResult { let deps = FullDeps { client: client.clone(), @@ -227,7 +215,7 @@ pub fn new_partial( deny_unsafe, transaction_converter: opportunity_runtime::TransactionConverter, filter_pool: filter_pool.clone(), - backend: backend.clone(), + frontier_backend: backend.clone(), is_authority, network, babe: BabeDeps { @@ -264,11 +252,7 @@ pub fn new_partial( pub fn new_full_base( mut config: Configuration, with_startup_data: impl FnOnce( - &sc_consensus_babe::BabeBlockImport< - Block, - FullClient, - FrontierBlockImport, - >, + &sc_consensus_babe::BabeBlockImport, &sc_consensus_babe::BabeLink, ), ) -> Result< @@ -334,6 +318,8 @@ pub fn new_full_base( let prometheus_registry = config.prometheus_registry().cloned(); let chain_spec = config.chain_spec.cloned_box(); + // Frontier offchain DB task. Essential. + // Maps emulated ethereum data to substrate native data. task_manager.spawn_essential_handle().spawn( "frontier-mapping-sync-worker", MappingSyncWorker::new( @@ -357,6 +343,11 @@ pub fn new_full_base( EthTask::filter_pool_task(client.clone(), filter_pool.clone(), FILTER_RETAIN_THRESHOLD), ); + task_manager.spawn_essential_handle().spawn( + "frontier-schema-cache-task", + EthTask::ethereum_schema_cache_task(client.clone(), frontier_backend.clone()), + ); + let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { config, backend: backend.clone(), diff --git a/node/standard/Cargo.toml b/node/standard/Cargo.toml index b0948f68..3e69a68a 100644 --- a/node/standard/Cargo.toml +++ b/node/standard/Cargo.toml @@ -16,9 +16,6 @@ path = "src/main.rs" [package.metadata.docs.rs] targets = [ 'x86_64-unknown-linux-gnu' ] -[lib] -crate-type = [ "cdylib", "rlib" ] - [build-dependencies] substrate-build-script-utils = { version = "3.0.0", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } @@ -39,9 +36,11 @@ hex-literal = "0.3.3" hex = "0.4.3" lazy_static = "1.4" log = "0.4.14" +futures = { version = "0.3.4", features = ["compat"] } # RPC related Dependencies jsonrpc-core = "18.0.0" +jsonrpc-pubsub = "18.0.0" # Local Dependencies standard-runtime = { path = "../../runtime/standard" } @@ -105,4 +104,15 @@ cumulus-primitives-parachain-inherent = { git = 'https://github.com/paritytech/c polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } -polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } \ No newline at end of file +polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.12" } + +# Frontier Dependencies +pallet-dynamic-fee = { version = "4.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-ethereum = { version = "4.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-evm = { version = "6.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fc-db = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fc-rpc = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fc-rpc-core = { version = "1.1.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fc-mapping-sync = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fc-consensus = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fp-rpc = { version = "3.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } diff --git a/node/standard/src/chain_spec.rs b/node/standard/src/chain_spec.rs index 0a43abfa..b91399fb 100644 --- a/node/standard/src/chain_spec.rs +++ b/node/standard/src/chain_spec.rs @@ -3,19 +3,18 @@ use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; use sp_core::{sr25519, Pair, Public}; -use standard_runtime::{ - AccountId, AssetRegistryConfig, AuraConfig, AuraId, BalancesConfig, CollatorSelectionConfig, - GenesisConfig, ImOnlineConfig, ImOnlineId, OracleConfig, ParachainInfoConfig, SessionConfig, - SessionKeys, Signature, StakerStatus, StakingConfig, SudoConfig, SystemConfig, VestingConfig, - EXISTENTIAL_DEPOSIT, WASM_BINARY, -}; - use sp_runtime::{ traits::{IdentifyAccount, Verify}, Perbill, }; +use standard_runtime::{ + AssetRegistryConfig, AuraConfig, AuraId, BalancesConfig, CollatorSelectionConfig, EVMConfig, + EthereumConfig, GenesisConfig, ImOnlineConfig, ImOnlineId, OracleConfig, ParachainInfoConfig, + Precompiles, SessionConfig, SessionKeys, StakerStatus, StakingConfig, SudoConfig, SystemConfig, + VestingConfig, EXISTENTIAL_DEPOSIT, WASM_BINARY, +}; -use primitives::AssetId; +use primitives::{AccountId, AssetId, Signature}; pub const CORE_ASSET_ID: AssetId = 1; @@ -251,6 +250,11 @@ fn testnet_genesis( endowed_accounts: Vec, id: ParaId, ) -> GenesisConfig { + // This is supposed the be the simplest bytecode to revert without returning any data. + // We will pre-deploy it under all of our precompiles to ensure they can be called from + // within contracts. + // (PUSH1 0x00 PUSH1 0x00 REVERT) + let revert_bytecode = vec![0x60, 0x00, 0x60, 0x00, 0xFD]; GenesisConfig { system: SystemConfig { code: WASM_BINARY.expect("WASM binary was not build, please build it!").to_vec(), @@ -310,5 +314,25 @@ fn testnet_genesis( oracles: [get_account_id_from_seed::("Alice")].to_vec(), provider_count: 5, }, + evm: EVMConfig { + // We need _some_ code inserted at the precompile address so that + // the evm will actually call the address. + accounts: Precompiles::used_addresses() + .iter() + .map(|addr| { + ( + addr.clone(), + pallet_evm::GenesisAccount { + nonce: Default::default(), + balance: Default::default(), + storage: Default::default(), + code: revert_bytecode.clone(), + }, + ) + }) + .collect(), + }, + ethereum: EthereumConfig {}, + dynamic_fee: Default::default(), } } diff --git a/node/standard/src/command.rs b/node/standard/src/command.rs index 47f1cdf5..b6326e66 100644 --- a/node/standard/src/command.rs +++ b/node/standard/src/command.rs @@ -137,7 +137,7 @@ macro_rules! construct_async_run { _ >( &$config, - crate::service::parachain_build_import_queue, + crate::service::build_import_queue, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) diff --git a/node/standard/src/lib.rs b/node/standard/src/lib.rs deleted file mode 100644 index f117b8aa..00000000 --- a/node/standard/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod chain_spec; -pub mod rpc; -pub mod service; diff --git a/node/standard/src/rpc.rs b/node/standard/src/rpc.rs index 7b91a6c4..23111c18 100644 --- a/node/standard/src/rpc.rs +++ b/node/standard/src/rpc.rs @@ -7,52 +7,166 @@ use std::sync::Arc; -use primitives::Balance; -use standard_runtime::{opaque::Block, AccountId, Index as Nonce}; +use primitives::{AccountId, Balance, Block, Hash, Index as Nonce}; -use sc_client_api::AuxStore; +use fc_rpc_core::types::FilterPool; +use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; +use sc_client_api::{AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider}; +use sc_network::NetworkService; pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; +use sc_service::Error as ServiceError; +use sc_transaction_pool::{ChainApi, Pool}; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; -use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, +}; +use sp_runtime::traits::BlakeTwo256; +use substrate_frame_rpc_system::{FullSystem, SystemApi}; + +use fc_rpc::{ + EthApi, EthApiServer, EthBlockDataCache, EthFilterApi, EthFilterApiServer, EthPubSubApi, + EthPubSubApiServer, HexEncodedIdProvider, NetApi, NetApiServer, OverrideHandle, + RuntimeApiStorageOverride, SchemaV1Override, SchemaV2Override, StorageOverride, Web3Api, + Web3ApiServer, +}; +use jsonrpc_pubsub::manager::SubscriptionManager; +use pallet_ethereum::EthereumStorageSchema; +use std::collections::BTreeMap; /// A type representing all RPC extensions. pub type RpcExtension = jsonrpc_core::IoHandler; +/// RPC result. +pub type RpcResult = Result; /// Full client dependencies -pub struct FullDeps { +pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, /// Whether to deny unsafe calls pub deny_unsafe: DenyUnsafe, + /// Graph pool instance. + pub graph: Arc>, + /// Network service + pub network: Arc>, + /// The Node authority flag + pub is_authority: bool, + /// Frontier Backend. + pub frontier_backend: Arc>, + /// Ethereum transaction conversion helper. + pub transaction_converter: T, + /// EthFilterApi pool. + pub filter_pool: FilterPool, } /// Instantiate all RPC extensions. -pub fn create_full(deps: FullDeps) -> RpcExtension +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, +) -> RpcExtension where C: ProvideRuntimeApi + HeaderBackend + AuxStore + + StorageProvider + HeaderMetadata + + BlockchainEvents + Send + Sync + 'static, - C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, - C::Api: substrate_frame_rpc_system::AccountNonceApi, - C::Api: BlockBuilder, - P: TransactionPool + Sync + Send + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi + + BlockBuilder, + P: TransactionPool + Sync + Send + 'static, + T: fp_rpc::ConvertTransaction + Sync + Send + 'static, + B: Backend + 'static, + B::State: StateBackend, + B::Blockchain: BlockchainBackend, + A: ChainApi + 'static, { - use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; - use substrate_frame_rpc_system::{FullSystem, SystemApi}; - let mut io = jsonrpc_core::IoHandler::default(); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { + client, + pool, + deny_unsafe, + graph, + network, + is_authority, + frontier_backend, + transaction_converter, + filter_pool, + } = deps; + + io.extend_with(SystemApi::to_delegate(FullSystem::new( + client.clone(), + pool.clone(), + deny_unsafe, + ))); + io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone()))); + + let mut overrides_map = BTreeMap::new(); + overrides_map.insert( + EthereumStorageSchema::V1, + Box::new(SchemaV1Override::new(client.clone())) + as Box + Send + Sync>, + ); + overrides_map.insert( + EthereumStorageSchema::V2, + Box::new(SchemaV2Override::new(client.clone())) + as Box + Send + Sync>, + ); + + let overrides = Arc::new(OverrideHandle { + schemas: overrides_map, + fallback: Box::new(RuntimeApiStorageOverride::new(client.clone())), + }); + + let max_past_logs: u32 = 10_000; + let max_stored_filters: usize = 500; + let block_data_cache = Arc::new(EthBlockDataCache::new(50, 50)); + + io.extend_with(EthApiServer::to_delegate(EthApi::new( + client.clone(), + pool.clone(), + graph, + transaction_converter, + network.clone(), + Default::default(), + overrides.clone(), + frontier_backend.clone(), + is_authority, + max_past_logs, + block_data_cache.clone(), + ))); + + io.extend_with(EthFilterApiServer::to_delegate(EthFilterApi::new( + client.clone(), + frontier_backend, + filter_pool, + max_stored_filters, + overrides.clone(), + max_past_logs, + block_data_cache.clone(), + ))); + + io.extend_with(NetApiServer::to_delegate(NetApi::new(client.clone(), network.clone(), true))); + + io.extend_with(Web3ApiServer::to_delegate(Web3Api::new(client.clone()))); - io.extend_with(SystemApi::to_delegate(FullSystem::new(client.clone(), pool, deny_unsafe))); - io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new(client))); + io.extend_with(EthPubSubApiServer::to_delegate(EthPubSubApi::new( + pool, + client.clone(), + network, + SubscriptionManager::::with_id_provider( + HexEncodedIdProvider::default(), + Arc::new(subscription_task_executor), + ), + overrides, + ))); io } diff --git a/node/standard/src/service.rs b/node/standard/src/service.rs index 4be13387..6c0ddc2b 100644 --- a/node/standard/src/service.rs +++ b/node/standard/src/service.rs @@ -1,9 +1,8 @@ -// std -use std::sync::Arc; - // Local Types -use primitives::Balance; -use standard_runtime::{AccountId, Index as Nonce, RuntimeApi}; +use crate::rpc::{create_full, FullDeps, RpcResult}; +use futures::StreamExt; +use primitives::{AccountId, Balance, Block, Hash, Index as Nonce}; +use standard_runtime::{self, RuntimeApi}; // Cumulus Imports use cumulus_client_consensus_aura::{ @@ -17,23 +16,24 @@ use cumulus_client_service::{ use cumulus_primitives_core::ParaId; // Substrate Imports -use sc_client_api::ExecutorProvider; +use fc_consensus::FrontierBlockImport; +use fc_mapping_sync::{MappingSyncWorker, SyncStrategy}; +use fc_rpc::EthTask; +use fc_rpc_core::types::FilterPool; +use sc_client_api::{BlockchainEvents, ExecutorProvider}; use sc_executor::NativeElseWasmExecutor; use sc_network::NetworkService; -use sc_service::{Configuration, PartialComponents, Role, TFullBackend, TFullClient, TaskManager}; +use sc_service::{ + BasePath, Configuration, PartialComponents, Role, TFullBackend, TFullClient, TaskManager, +}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; use sp_api::ConstructRuntimeApi; use sp_consensus::SlotData; use sp_keystore::SyncCryptoStorePtr; use sp_runtime::traits::BlakeTwo256; +use std::{collections::BTreeMap, sync::Arc, time::Duration}; use substrate_prometheus_endpoint::Registry; -// Runtime type overrides -type BlockNumber = u32; -type Header = sp_runtime::generic::Header; -pub type Block = sp_runtime::generic::Block; -type Hash = sp_core::H256; - /// Native executor instance. pub struct StandardRuntimeExecutor; @@ -49,6 +49,26 @@ impl sc_executor::NativeExecutionDispatch for StandardRuntimeExecutor { } } +pub fn frontier_database_dir(config: &Configuration) -> std::path::PathBuf { + let config_dir = config + .base_path + .as_ref() + .map(|base_path| base_path.config_dir(config.chain_spec.id())) + .unwrap_or_else(|| { + BasePath::from_project("", "", "standard").config_dir(config.chain_spec.id()) + }); + config_dir.join("frontier").join("db") +} + +pub fn open_frontier_backend(config: &Configuration) -> Result>, String> { + Ok(Arc::new(fc_db::Backend::::new(&fc_db::DatabaseSettings { + source: fc_db::DatabaseSettingsSrc::RocksDb { + path: frontier_database_dir(&config), + cache_size: 0, + }, + })?)) +} + /// Starts a `ServiceBuilder` for a full service. /// /// Use this macro if you don't actually need the full service, but just the builder in order to @@ -70,7 +90,7 @@ pub fn new_partial( Block, TFullClient>, >, - (Option, Option), + (Option, Option, Arc>), >, sc_service::Error, > @@ -86,11 +106,17 @@ where Block, StateBackend = sc_client_api::StateBackendFor, Block>, > + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder, + + sp_block_builder::BlockBuilder + + fp_rpc::EthereumRuntimeRPCApi, sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, Executor: sc_executor::NativeExecutionDispatch + 'static, BIQ: FnOnce( Arc>>, + FrontierBlockImport< + Block, + Arc>>, + TFullClient>, + >, &Configuration, Option, &TaskManager, @@ -121,7 +147,7 @@ where let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( - config, + &config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, )?; @@ -142,8 +168,13 @@ where client.clone(), ); + let frontier_backend = open_frontier_backend(config)?; + let frontier_block_import = + FrontierBlockImport::new(client.clone(), client.clone(), frontier_backend.clone()); + let import_queue = build_import_queue( client.clone(), + frontier_block_import, config, telemetry.as_ref().map(|telemetry| telemetry.handle()), &task_manager, @@ -157,7 +188,7 @@ where task_manager, transaction_pool, select_chain: (), - other: (telemetry, telemetry_worker_handle), + other: (telemetry, telemetry_worker_handle, frontier_backend), }; Ok(params) @@ -167,11 +198,10 @@ where /// /// This is the actual implementation that is abstract over the executor and the runtime api. #[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node_impl( +async fn start_node_impl( parachain_config: Configuration, polkadot_config: Configuration, id: ParaId, - _rpc_ext_builder: RB, build_import_queue: BIQ, build_consensus: BIC, ) -> sc_service::error::Result<( @@ -191,28 +221,29 @@ where StateBackend = sc_client_api::StateBackendFor, Block>, > + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo + + substrate_frame_rpc_system::AccountNonceApi + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + substrate_frame_rpc_system::AccountNonceApi, + + fp_rpc::EthereumRuntimeRPCApi + + cumulus_primitives_core::CollectCollationInfo, sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, Executor: sc_executor::NativeExecutionDispatch + 'static, - RB: Fn( - Arc>, - ) -> Result, sc_service::Error> - + Send - + 'static, BIQ: FnOnce( + Arc>>, + FrontierBlockImport< + Block, Arc>>, - &Configuration, - Option, - &TaskManager, - ) -> Result< - sc_consensus::DefaultImportQueue< - Block, - TFullClient>, - >, - sc_service::Error, - > + 'static, + TFullClient>, + >, + &Configuration, + Option, + &TaskManager, + ) -> Result< + sc_consensus::DefaultImportQueue< + Block, + TFullClient>, + >, + sc_service::Error, + >, BIC: FnOnce( Arc>>, Option<&Registry>, @@ -237,7 +268,7 @@ where let parachain_config = prepare_node_config(parachain_config); let params = new_partial::(¶chain_config, build_import_queue)?; - let (mut telemetry, telemetry_worker_handle) = params.other; + let (mut telemetry, telemetry_worker_handle, frontier_backend) = params.other; let relay_chain_full_node = cumulus_client_service::build_polkadot_full_node(polkadot_config, telemetry_worker_handle) @@ -255,8 +286,8 @@ where relay_chain_full_node.backend.clone(), ); + let is_authority = parachain_config.role.is_authority(); let force_authoring = parachain_config.force_authoring; - let validator = parachain_config.role.is_authority(); let prometheus_registry = parachain_config.prometheus_registry().cloned(); let transaction_pool = params.transaction_pool.clone(); let mut task_manager = params.task_manager; @@ -273,18 +304,60 @@ where warp_sync: None, })?; + // Frontier offchain DB task. Essential. + // Maps emulated ethereum data to substrate native data. + task_manager.spawn_essential_handle().spawn( + "frontier-mapping-sync-worker", + MappingSyncWorker::new( + client.import_notification_stream(), + Duration::new(6, 0), + client.clone(), + backend.clone(), + frontier_backend.clone(), + SyncStrategy::Parachain, + ) + .for_each(|()| futures::future::ready(())), + ); + + let filter_pool: FilterPool = Arc::new(std::sync::Mutex::new(BTreeMap::new())); + + let subscription_task_executor = + sc_rpc::SubscriptionTaskExecutor::new(task_manager.spawn_handle()); + + // Frontier `EthFilterApi` maintenance. Manages the pool of user-created Filters. + // Each filter is allowed to stay in the pool for 100 blocks. + const FILTER_RETAIN_THRESHOLD: u64 = 100; + task_manager.spawn_essential_handle().spawn( + "frontier-filter-pool", + EthTask::filter_pool_task(client.clone(), filter_pool.clone(), FILTER_RETAIN_THRESHOLD), + ); + + task_manager.spawn_essential_handle().spawn( + "frontier-schema-cache-task", + fc_rpc::EthTask::ethereum_schema_cache_task(client.clone(), frontier_backend.clone()), + ); + let rpc_extensions_builder = { let client = client.clone(); let transaction_pool = transaction_pool.clone(); + let backend = frontier_backend.clone(); + let network = network.clone(); - Box::new(move |deny_unsafe, _| { - let deps = crate::rpc::FullDeps { + Box::new(move |deny_unsafe, subscription_task_executor| { + let deps = FullDeps { client: client.clone(), pool: transaction_pool.clone(), + graph: transaction_pool.pool().clone(), + network: network.clone(), + is_authority, deny_unsafe, + frontier_backend: backend.clone(), + transaction_converter: standard_runtime::TransactionConverter, + filter_pool: filter_pool.clone(), }; - Ok(crate::rpc::create_full(deps)) + Ok(create_full(deps, subscription_task_executor)) + // create_full(deps, subscription_task_executor.clone()).map_err(Into::into) }) }; @@ -308,7 +381,7 @@ where Arc::new(move |hash, data| network.announce_block(hash, data)) }; - if validator { + if is_authority { let parachain_consensus = build_consensus( client.clone(), prometheus_registry.as_ref(), @@ -354,9 +427,13 @@ where } /// Build the import queue for the parachain runtime. -#[allow(clippy::type_complexity)] -pub fn parachain_build_import_queue( +pub fn build_import_queue( client: Arc>>, + block_import: FrontierBlockImport< + Block, + Arc>>, + TFullClient>, + >, config: &Configuration, telemetry: Option, task_manager: &TaskManager, @@ -408,12 +485,11 @@ pub async fn start_parachain_node( TaskManager, Arc>>, )> { - start_node_impl::( + start_node_impl::( parachain_config, polkadot_config, id, - |_| Ok(Default::default()), - parachain_build_import_queue, + build_import_queue, |client, prometheus_registry, telemetry, diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index d2731ac9..e766673e 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -3,7 +3,7 @@ use sp_runtime::{ generic, traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiSignature, OpaqueExtrinsic, + MultiSignature, }; /// Some way of identifying an account on the chain. We intentionally make it equivalent @@ -21,8 +21,6 @@ pub type Amount = i128; pub type CurrencyId = u32; /// Header type. pub type Header = generic::Header; -/// Block type. -pub type Block = generic::Block; /// Counter for the number of eras that have passed. pub type EraIndex = u64; /// Index for oracle to provide information @@ -35,3 +33,10 @@ pub type Signature = MultiSignature; pub type Index = u32; /// A hash of some data used by the chain. pub type Hash = sp_core::H256; +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; +/// Type used for expressing timestamp. +pub type Moment = u64; +/// Block type. +pub type Block = sp_runtime::generic::Block; diff --git a/runtime/opportunity/src/constants.rs b/runtime/opportunity/src/constants.rs index 439d476c..02ab6fe4 100644 --- a/runtime/opportunity/src/constants.rs +++ b/runtime/opportunity/src/constants.rs @@ -32,10 +32,7 @@ pub mod currency { /// Time. pub mod time { - // Moment/BlockNumber type created directly here instead of importing the whole node_primitives - // module - type Moment = u64; - type BlockNumber = u32; + use primitives::{BlockNumber, Moment}; pub const MILLISECS_PER_BLOCK: Moment = 6000; pub const SECS_PER_BLOCK: Moment = MILLISECS_PER_BLOCK / 1000; diff --git a/runtime/opportunity/src/lib.rs b/runtime/opportunity/src/lib.rs index 757ce215..3303e2f5 100644 --- a/runtime/opportunity/src/lib.rs +++ b/runtime/opportunity/src/lib.rs @@ -67,30 +67,23 @@ use precompiles::FrontierPrecompiles; mod precompiles; pub type Precompiles = FrontierPrecompiles; -use primitives::{AccountId, AssetId, Balance, Hash, Index, Signature}; +use primitives::{ + AccountId, AccountIndex, AssetId, Balance, BlockNumber, Hash, Header, Index, Moment, Signature, +}; pub mod constants; /// Constant values used within the runtime. use constants::{currency::*, time::*}; mod voter_bags; -/// The type for looking up accounts. We don't expect more than 4 billion of them, but you -/// never know... -pub type AccountIndex = u32; /// The address format for describing accounts. pub type Address = ::Source; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; /// Block type as expected by this runtime. pub type Block = generic::Block; -/// An index to a block. -pub type BlockNumber = u32; /// A Block signed with a Justification pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// A timestamp -pub type Moment = u64; /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckSpecVersion, @@ -130,7 +123,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("opportunity"), impl_name: create_runtime_str!("opportunity10"), authoring_version: 1, - spec_version: 9123, + spec_version: 9124, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 6, @@ -855,7 +848,7 @@ pub struct FindAuthorTruncated(sp_std::marker::PhantomData); impl> FindAuthor for FindAuthorTruncated { fn find_author<'a, I>(digests: I) -> Option where - I: 'a + IntoIterator, + I: 'a + IntoIterator, { if let Some(author_index) = F::find_author(digests) { if (author_index as usize) < Babe::authorities().len() { @@ -908,7 +901,7 @@ impl pallet_dynamic_fee::Config for Runtime { construct_runtime!( pub enum Runtime where Block = Block, - NodeBlock = primitives::Block, + NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { System: frame_system::{Pallet, Call, Config, Storage, Event}, diff --git a/runtime/opportunity/src/precompiles.rs b/runtime/opportunity/src/precompiles.rs index 242fd565..18552ea9 100644 --- a/runtime/opportunity/src/precompiles.rs +++ b/runtime/opportunity/src/precompiles.rs @@ -8,18 +8,6 @@ use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripe pub struct FrontierPrecompiles(PhantomData); -// impl FrontierPrecompiles -// where -// R: pallet_evm::Config, -// { -// pub fn new() -> Self { -// Self(Default::default()) -// } -// pub fn used_addresses() -> sp_std::vec::Vec { -// sp_std::vec![1, 2, 3, 4, 5, 1024, 1025].into_iter().map(|x| hash(x)).collect() -// } -// } - impl FrontierPrecompiles where R: pallet_evm::Config, @@ -29,11 +17,6 @@ where } /// Return all addresses that contain precompiles. This can be used to populate dummy code /// under the precompile. - // pub fn used_addresses() -> impl Iterator { - // sp_std::vec![1, 2, 3, 4, 5, 1024, 1025] - // .into_iter() - // .map(|x| R::AddressMapping::into_account_id(hash(x))) - // } pub fn used_addresses() -> sp_std::vec::Vec { sp_std::vec![1, 2, 3, 4, 5, 1024, 1025].into_iter().map(|x| hash(x)).collect() } diff --git a/runtime/standard/Cargo.toml b/runtime/standard/Cargo.toml index 9334e957..da3c1de2 100644 --- a/runtime/standard/Cargo.toml +++ b/runtime/standard/Cargo.toml @@ -9,13 +9,14 @@ repository = 'https://github.com/digitalnativeinc/standard-substrate' description = 'Standard Parachain Runtime.' [build-dependencies] -substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } +substrate-wasm-builder = { version = '5.0.0-dev', git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" } [dependencies] -codec = { package = 'parity-scale-codec', version = '2.0.0', default-features = false, features = ['derive']} +parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } log = { version = "0.4.14", default-features = false } serde = { version = '1.0.130', optional = true, features = ['derive'] } scale-info = { version = "1.0.0", default-features = false, features = ["derive"] } +smallvec = "1.7.0" # Local Dependencies primitives = { default-features = false, path = "../../primitives" } @@ -72,6 +73,7 @@ pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } pallet-vesting = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12", default-features = false } pallet-scheduler = { version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.12", default-features = false } +pallet-indices = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.12" } # Cumulus Dependencies cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.12", default-features = false } @@ -93,6 +95,16 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", default-feature pallet-xcm = { git = 'https://github.com/paritytech/polkadot', branch = 'release-v0.9.12', default-features = false } runtime-common = { package = "polkadot-runtime-common", git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.12" } +# EVM Dependencies +# pallet-base-fee = { version = "1.0.0", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-dynamic-fee = { version = "4.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-ethereum = { version = "4.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-evm = { version = "6.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-evm-precompile-modexp = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-evm-precompile-sha3fips = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +pallet-evm-precompile-simple = { version = "2.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } +fp-rpc = { version = "3.0.0-dev", default-features = false, git = "https://github.com/digitalnativeinc/frontier", branch = "feature/opportunity-integration" } + [features] default = [ "std" ] runtime-benchmarks = [ @@ -107,7 +119,7 @@ runtime-benchmarks = [ ] std = [ "serde", - "codec/std", + 'parity-scale-codec/std', "log/std", "sp-std/std", "sp-api/std", @@ -131,6 +143,7 @@ std = [ "pallet-im-online/std", "pallet-offences/std", "pallet-scheduler/std", + 'pallet-indices/std', "frame-election-provider-support/std", "pallet-election-provider-multi-phase/std", "pallet-vesting/std", @@ -162,4 +175,11 @@ std = [ 'pallet-standard-market/std', 'pallet-standard-vault/std', 'pallet-standard-chainbridge/std', + 'pallet-ethereum/std', + 'pallet-dynamic-fee/std', + 'pallet-evm/std', + 'fp-rpc/std', + 'pallet-evm-precompile-simple/std', + 'pallet-evm-precompile-sha3fips/std', + 'pallet-evm-precompile-modexp/std', ] diff --git a/runtime/standard/src/constants.rs b/runtime/standard/src/constants.rs index 4f4cfb3d..5edbaac1 100644 --- a/runtime/standard/src/constants.rs +++ b/runtime/standard/src/constants.rs @@ -35,7 +35,7 @@ pub mod currency { /// Time. pub mod time { - use crate::{BlockNumber, Moment}; + use primitives::{BlockNumber, Moment}; pub const MILLISECS_PER_BLOCK: Moment = 12000; pub const SECS_PER_BLOCK: Moment = MILLISECS_PER_BLOCK / 1000; diff --git a/runtime/standard/src/lib.rs b/runtime/standard/src/lib.rs index 56b9fa9b..6f457f14 100644 --- a/runtime/standard/src/lib.rs +++ b/runtime/standard/src/lib.rs @@ -4,30 +4,39 @@ use frame_support::{ construct_runtime, match_type, parameter_types, - traits::{Everything, Nothing, U128CurrencyToVote}, + traits::{Everything, FindAuthor, Nothing, U128CurrencyToVote}, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, DispatchClass, IdentityFee, Weight, }, - PalletId, + ConsensusEngineId, PalletId, }; use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; use pallet_session::historical as pallet_session_historical; +use parity_scale_codec::{Decode, Encode}; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{ + crypto::{KeyTypeId, Public}, + OpaqueMetadata, H160, H256, U256, +}; use sp_inherents::InherentData; use sp_runtime::{ create_runtime_str, curve::PiecewiseLinear, - generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Verify}, + generic, + generic::Era, + impl_opaque_keys, + traits::{ + BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic, SaturatedConversion, StaticLookup, + Verify, + }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, }; -use sp_std::prelude::*; +use sp_std::{marker::PhantomData, prelude::*}; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -54,12 +63,22 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -use primitives::{AssetId, Balance}; +use fp_rpc::TransactionStatus; +use pallet_ethereum::{Call::transact, Transaction as EthereumTransaction}; +use pallet_evm::{Account as EVMAccount, EnsureAddressTruncated, HashedAddressMapping, Runner}; + +use primitives::{ + AccountId, AccountIndex, AssetId, Balance, BlockNumber, Hash, Header, Index, Signature, +}; /// Constant values used within the runtime. pub mod constants; use constants::{currency::*, time::*}; +use precompiles::FrontierPrecompiles; +mod precompiles; +pub type Precompiles = FrontierPrecompiles; + // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -79,10 +98,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("standard"), impl_name: create_runtime_str!("standard10"), authoring_version: 1, - spec_version: 9090, + spec_version: 9124, impl_version: 1, apis: RUNTIME_API_VERSIONS, - transaction_version: 4, + transaction_version: 6, }; /// Native version. @@ -91,28 +110,10 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = sp_runtime::MultiSignature; -/// Some way of identifying an account on the chain. We intentionally make it equivalent -/// to the public key of our transaction signing scheme. -pub type AccountId = - <::Signer as sp_runtime::traits::IdentifyAccount>::AccountId; -/// Index of a transaction in the chain. -pub type Index = u32; -/// Type used for expressing timestamp. -pub type Moment = u64; -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; -/// An index to a block. -pub type BlockNumber = u32; /// The address format for describing accounts. -pub type Address = sp_runtime::MultiAddress; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; -/// Block type as expected by this runtime. +pub type Address = ::Source; +/// Opaque block type. pub type Block = generic::Block; -pub type NodeBlock = generic::Block; - /// A Block signed with a Justification pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. @@ -142,23 +143,6 @@ pub type Executive = frame_executive::Executive< AllPallets, >; -/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know -/// the specifics of the runtime. They can then be made to be agnostic over specific formats -/// of data like extrinsics, allowing for them to continue syncing the network through upgrades -/// to even the core data structures. -pub mod opaque { - use super::*; - use sp_runtime::{generic, traits::BlakeTwo256}; - - pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; - /// Opaque block header type. - pub type Header = generic::Header; - /// Opaque block type. - pub type Block = generic::Block; - /// Opaque block identifier type. - pub type BlockId = generic::BlockId; -} - impl_opaque_keys! { pub struct SessionKeys { pub aura: Aura, @@ -209,7 +193,7 @@ impl frame_system::Config for Runtime { /// The aggregated dispatch type that is available for extrinsics. type Call = Call; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = AccountIdLookup; + type Lookup = Indices; /// The index type for storing how many extrinsics an account has signed. type Index = Index; /// The index type for blocks. @@ -442,6 +426,18 @@ impl pallet_staking::Config for Runtime { type WeightInfo = pallet_staking::weights::SubstrateWeight; } +parameter_types! { + pub const IndexDeposit: Balance = 1 * DOLLARS; +} + +impl pallet_indices::Config for Runtime { + type AccountIndex = AccountIndex; + type Currency = Balances; + type Deposit = IndexDeposit; + type Event = Event; + type WeightInfo = pallet_indices::weights::SubstrateWeight; +} + parameter_types! { // phase durations. 1/4 of the last session for each. pub const SignedPhase: u32 = EPOCH_DURATION_IN_BLOCKS / 4; @@ -745,6 +741,11 @@ parameter_types! { pub const StakingUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 2; } +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} + impl frame_system::offchain::SendTransactionTypes for Runtime where Call: From, @@ -794,10 +795,61 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } +pub struct FindAuthorTruncated(PhantomData); +impl> FindAuthor for FindAuthorTruncated { + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + if let Some(author_index) = F::find_author(digests) { + let authority_id = Aura::authorities()[author_index as usize].clone(); + return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24])) + } + None + } +} + +parameter_types! { + pub const ChainId: u64 = 100; + pub BlockGasLimit: U256 = U256::from(u32::max_value()); + pub PrecompilesValue: FrontierPrecompiles = FrontierPrecompiles::<_>::new(); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = pallet_dynamic_fee::Pallet; + type GasWeightMapping = (); + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = EnsureAddressTruncated; + type WithdrawOrigin = EnsureAddressTruncated; + type AddressMapping = HashedAddressMapping; + type Currency = Balances; + type Event = Event; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = FrontierPrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = ChainId; + type BlockGasLimit = BlockGasLimit; + type OnChargeTransaction = (); + type FindAuthor = FindAuthorTruncated; +} + +impl pallet_ethereum::Config for Runtime { + type Event = Event; + type StateRoot = pallet_ethereum::IntermediateStateRoot; +} + +frame_support::parameter_types! { + pub BoundDivision: U256 = U256::from(1024); +} + +impl pallet_dynamic_fee::Config for Runtime { + type MinGasPriceBoundDivisor = BoundDivision; +} + construct_runtime!( pub enum Runtime where Block = Block, - NodeBlock = generic::Block, + NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { // System pallets @@ -816,15 +868,16 @@ construct_runtime!( // Staking pallets Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 40, Staking: pallet_staking::{Pallet, Call, Config, Storage, Event} = 41, + Indices: pallet_indices::{Pallet, Call, Storage, Event} = 42, // Consensus pallets - Aura: pallet_aura::{Pallet, Config} = 42, - AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 43, - Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent} = 44, - ImOnline: pallet_im_online::{Pallet, Call, Storage, Event, ValidateUnsigned, Config} = 45, - ElectionProviderMultiPhase: pallet_election_provider_multi_phase::{Pallet, Call, Storage, Event, ValidateUnsigned} = 46, - Offences: pallet_offences::{Pallet, Storage, Event} = 47, - Historical: pallet_session_historical::{Pallet} = 48, - CollatorSelection: pallet_collator_selection::{Pallet, Call, Storage, Event, Config} = 49, + Aura: pallet_aura::{Pallet, Config} = 50, + AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 51, + Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent} = 52, + ImOnline: pallet_im_online::{Pallet, Call, Storage, Event, ValidateUnsigned, Config} = 53, + ElectionProviderMultiPhase: pallet_election_provider_multi_phase::{Pallet, Call, Storage, Event, ValidateUnsigned} = 54, + Offences: pallet_offences::{Pallet, Storage, Event} = 55, + Historical: pallet_session_historical::{Pallet} = 56, + CollatorSelection: pallet_collator_selection::{Pallet, Call, Storage, Event, Config} = 57, // XCM pallets XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 60, PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin} = 61, @@ -837,9 +890,78 @@ construct_runtime!( Vault: pallet_standard_vault::{Pallet, Call, Storage, Event} = 73, // Chainbridge pallets ChainBridge: pallet_standard_chainbridge::{Pallet, Call, Storage, Event}= 74, + // EVM pallets + Ethereum: pallet_ethereum::{Pallet, Call, Storage, Event, Origin, Config} = 80, + EVM: pallet_evm::{Pallet, Config, Call, Storage, Event} = 81, + DynamicFee: pallet_dynamic_fee::{Pallet, Call, Storage, Config, Inherent} = 82, } ); +pub struct TransactionConverter; + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } +} + +impl fp_rpc::ConvertTransaction for TransactionConverter { + fn convert_transaction( + &self, + transaction: pallet_ethereum::Transaction, + ) -> sp_runtime::OpaqueExtrinsic { + let extrinsic = UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ); + let encoded = extrinsic.encode(); + sp_runtime::OpaqueExtrinsic::decode(&mut &encoded[..]) + .expect("Encoded extrinsic is always valid") + } +} + +impl frame_system::offchain::CreateSignedTransaction for Runtime +where + Call: From, +{ + fn create_transaction>( + call: Call, + public: ::Signer, + account: AccountId, + nonce: Index, + ) -> Option<(Call, ::SignaturePayload)> { + let tip = 0; + // take the biggest period possible. + let period = + BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + let current_block = System::block_number() + .saturated_into::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let era = Era::mortal(period, current_block); + let extra = ( + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(era), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + ); + let raw_payload = SignedPayload::new(call, extra) + .map_err(|e| { + log::warn!("Unable to create signed payload: {:?}", e); + }) + .ok()?; + let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; + let address = Indices::unlookup(account); + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (address, signature.into(), extra))) + } +} + impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -948,6 +1070,131 @@ impl_runtime_apis! { TransactionPayment::query_fee_details(uxt, len) } } + + impl fp_rpc::EthereumRuntimeRPCApi for Runtime { + fn chain_id() -> u64 { + ::ChainId::get() + } + + fn account_basic(address: H160) -> EVMAccount { + EVM::account_basic(&address) + } + + fn gas_price() -> U256 { + ::FeeCalculator::min_gas_price() + } + + fn account_code_at(address: H160) -> Vec { + EVM::account_codes(address) + } + + fn author() -> H160 { + >::find_author() + } + + fn storage_at(address: H160, index: U256) -> H256 { + let mut tmp = [0u8; 32]; + index.to_big_endian(&mut tmp); + EVM::account_storages(address, H256::from_slice(&tmp[..])) + } + + fn call( + from: H160, + to: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + estimate: bool, + ) -> Result { + let config = if estimate { + let mut config = ::config().clone(); + config.estimate = true; + Some(config) + } else { + None + }; + + ::Runner::call( + from, + to, + data, + value, + gas_limit.low_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + Vec::new(), + config.as_ref().unwrap_or(::config()), + ).map_err(|err| err.into()) + } + + fn create( + from: H160, + data: Vec, + value: U256, + gas_limit: U256, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + estimate: bool, + ) -> Result { + let config = if estimate { + let mut config = ::config().clone(); + config.estimate = true; + Some(config) + } else { + None + }; + + ::Runner::create( + from, + data, + value, + gas_limit.low_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + Vec::new(), + config.as_ref().unwrap_or(::config()), + ).map_err(|err| err.into()) + } + + fn current_transaction_statuses() -> Option> { + Ethereum::current_transaction_statuses() + } + + fn current_block() -> Option { + Ethereum::current_block() + } + + fn current_receipts() -> Option> { + Ethereum::current_receipts() + } + + fn current_all() -> ( + Option, + Option>, + Option> + ) { + ( + Ethereum::current_block(), + Ethereum::current_receipts(), + Ethereum::current_transaction_statuses() + ) + } + + fn extrinsic_filter( + xts: Vec<::Extrinsic>, + ) -> Vec { + xts.into_iter().filter_map(|xt| match xt.function { + Call::Ethereum(transact{ transaction }) => Some(transaction), + _ => None + }).collect::>() + } + } } struct CheckInherents; diff --git a/runtime/standard/src/precompiles.rs b/runtime/standard/src/precompiles.rs new file mode 100644 index 00000000..18552ea9 --- /dev/null +++ b/runtime/standard/src/precompiles.rs @@ -0,0 +1,60 @@ +use pallet_evm::{Context, Precompile, PrecompileResult, PrecompileSet}; +use sp_core::H160; +use sp_std::marker::PhantomData; + +use pallet_evm_precompile_modexp::Modexp; +use pallet_evm_precompile_sha3fips::Sha3FIPS256; +use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; + +pub struct FrontierPrecompiles(PhantomData); + +impl FrontierPrecompiles +where + R: pallet_evm::Config, +{ + pub fn new() -> Self { + Self(Default::default()) + } + /// Return all addresses that contain precompiles. This can be used to populate dummy code + /// under the precompile. + pub fn used_addresses() -> sp_std::vec::Vec { + sp_std::vec![1, 2, 3, 4, 5, 1024, 1025].into_iter().map(|x| hash(x)).collect() + } +} + +impl PrecompileSet for FrontierPrecompiles +where + R: pallet_evm::Config, +{ + fn execute( + &self, + address: H160, + input: &[u8], + target_gas: Option, + context: &Context, + is_static: bool, + ) -> Option { + match address { + // Ethereum precompiles : + a if a == hash(1) => Some(ECRecover::execute(input, target_gas, context, is_static)), + a if a == hash(2) => Some(Sha256::execute(input, target_gas, context, is_static)), + a if a == hash(3) => Some(Ripemd160::execute(input, target_gas, context, is_static)), + a if a == hash(4) => Some(Identity::execute(input, target_gas, context, is_static)), + a if a == hash(5) => Some(Modexp::execute(input, target_gas, context, is_static)), + // Non-Frontier specific nor Ethereum precompiles : + a if a == hash(1024) => + Some(Sha3FIPS256::execute(input, target_gas, context, is_static)), + a if a == hash(1025) => + Some(ECRecoverPublicKey::execute(input, target_gas, context, is_static)), + _ => None, + } + } + + fn is_precompile(&self, address: H160) -> bool { + Self::used_addresses().contains(&address) + } +} + +fn hash(a: u64) -> H160 { + H160::from_low_u64_be(a) +}