diff --git a/node/Cargo.toml b/node/Cargo.toml index f000835..42e22fb 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -16,44 +16,202 @@ name = 'node-template' targets = ['x86_64-unknown-linux-gnu'] [build-dependencies] -substrate-build-script-utils = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} +substrate-build-script-utils = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = "master" } [dependencies] -jsonrpc-core = '15.1.0' +jsonrpc-core = '18.0.0' structopt = '0.3.8' +node-template-runtime = {version = '3.0.0', path = '../runtime'} +pallet-utxo-rpc = { path = "../pallets/utxo/rpc" } +pallet-utxo-rpc-runtime-api = { path = "../pallets/utxo/rpc/runtime-api" } + +[dependencies.frame-benchmarking] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-benchmarking-cli] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-transaction-payment-rpc] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sc-basic-authorship] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-cli] +features = ['wasmtime'] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-client-api] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sc-consensus] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-consensus-aura] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-executor] +features = ['wasmtime'] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-finality-grandpa] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-finality-grandpa-rpc] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-network] +git = 'https://github.com/paritytech/substrate.git' +version = "0.10.0-dev" +branch = "master" + +[dependencies.sc-consensus-babe] +git = 'https://github.com/paritytech/substrate.git' +version = "0.10.0-dev" +branch = "master" + +[dependencies.sc-consensus-babe-rpc] +git = 'https://github.com/paritytech/substrate.git' +version = "0.10.0-dev" +branch = "master" + +[dependencies.sc-keystore] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sc-rpc] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sc-rpc-api] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sc-service] +features = ['wasmtime'] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" -# Substrate dependencies -frame-benchmarking = {version = '3.1.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-benchmarking-cli = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-transaction-payment-rpc = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-basic-authorship = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-cli = {features = ['wasmtime'], version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-client-api = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-consensus = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-consensus-aura = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-executor = {features = ['wasmtime'], version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-finality-grandpa = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-keystore = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-rpc = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-rpc-api = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-service = {features = ['wasmtime'], version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-telemetry = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sc-transaction-pool = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-api = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-block-builder = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-blockchain = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-consensus = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-consensus-aura = {version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-core = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-finality-grandpa = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-inherents = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-runtime = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-transaction-pool = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -substrate-frame-rpc-system = {version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} +[dependencies.sc-telemetry] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sc-transaction-pool] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sc-transaction-pool-api] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-api] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-block-builder] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-blockchain] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-consensus] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sp-consensus-aura] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sp-core] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-finality-grandpa] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-inherents] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-runtime] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-timestamp] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.substrate-frame-rpc-system] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-contracts] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-contracts-rpc] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-authorship] +git = 'https://github.com/paritytech/substrate.git' +version = "4.0.0-dev" +branch = "master" + +[dependencies.sc-authority-discovery] +git = 'https://github.com/paritytech/substrate.git' +version = "0.10.0-dev" +branch = "master" # local dependencies -node-template-runtime = {version = '3.0.0', path = '../runtime'} -#bls_sigs_ref = {version = '0.3.0', path = '../../BLS'} +# bls_sigs_ref = {version = '0.3.0', path = '../../BLS'} [features] default = [] diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index b042815..4a160ae 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -2,7 +2,7 @@ use sp_core::{Pair, Public, sr25519}; use node_template_runtime::{ AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, SudoConfig, SystemConfig, WASM_BINARY, Signature, - pallet_utxo, UtxoConfig}; + pallet_utxo, UtxoConfig, PpConfig}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{Verify, IdentifyAccount}; @@ -155,29 +155,31 @@ fn testnet_genesis( }).unwrap(); GenesisConfig { - frame_system: SystemConfig { + system: SystemConfig { // Add Wasm runtime to storage. code: wasm_binary.to_vec(), changes_trie_config: Default::default(), }, - pallet_balances: BalancesConfig { + balances: BalancesConfig { // Configure endowed accounts with initial balance of 1 << 60. balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), }, - pallet_aura: AuraConfig { + aura: AuraConfig { authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), }, - pallet_grandpa: GrandpaConfig { + grandpa: GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), }, - pallet_sudo: SudoConfig { + sudo: SudoConfig { // Assign network admin rights. key: root_key, }, - - pallet_utxo: UtxoConfig { + utxo: UtxoConfig { genesis_utxos: vec![genesis], _marker: Default::default() + }, + pp: PpConfig { + _marker: Default::default() } } } diff --git a/node/src/command.rs b/node/src/command.rs index fefb8a0..d1ee79a 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -119,7 +119,7 @@ pub fn run() -> sc_cli::Result<()> { if cfg!(feature = "runtime-benchmarks") { let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run::(config)) + runner.sync_run(|config| cmd.run::(config)) } else { Err("Benchmarking wasn't enabled when building the node. \ You can enable it with `--features runtime-benchmarks`.".into()) diff --git a/node/src/rpc.rs b/node/src/rpc.rs index c1f0e0a..119a3bd 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -7,13 +7,12 @@ use std::sync::Arc; -use node_template_runtime::{opaque::Block, AccountId, Balance, Index}; +use node_template_runtime::{opaque::Block, AccountId, Balance, Index, BlockNumber, Hash}; use sp_api::ProvideRuntimeApi; use sp_blockchain::{Error as BlockChainError, HeaderMetadata, HeaderBackend}; use sp_block_builder::BlockBuilder; pub use sc_rpc_api::DenyUnsafe; -use sp_transaction_pool::TransactionPool; - +use sc_transaction_pool_api::TransactionPool; /// Full client dependencies. pub struct FullDeps { @@ -34,11 +33,15 @@ pub fn create_full( C: Send + Sync + 'static, C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, + C::Api: pallet_utxo_rpc::UtxoRuntimeApi, + C::Api: pallet_contracts_rpc::ContractsRuntimeApi, C::Api: BlockBuilder, P: TransactionPool + 'static, { use substrate_frame_rpc_system::{FullSystem, SystemApi}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; + use pallet_utxo_rpc::{Utxo, UtxoApi}; + use pallet_contracts_rpc::{Contracts, ContractsApi}; let mut io = jsonrpc_core::IoHandler::default(); let FullDeps { @@ -55,6 +58,9 @@ pub fn create_full( TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone())) ); + io.extend_with(UtxoApi::to_delegate(Utxo::new(client.clone()))); + io.extend_with(ContractsApi::to_delegate(Contracts::new(client.clone()))); + // Extend this RPC with a custom API by using the following syntax. // `YourRpcStruct` should have a reference to a client, which is needed // to call into the runtime. diff --git a/node/src/service.rs b/node/src/service.rs index 197a495..a625855 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -1,53 +1,64 @@ -//! Service and ServiceFactory implementation. Specialized wrapper over substrate service. - -use std::sync::Arc; -use std::time::Duration; -use sc_client_api::{ExecutorProvider, RemoteBackend}; use node_template_runtime::{self, opaque::Block, RuntimeApi}; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; -use sp_inherents::InherentDataProviders; -use sc_executor::native_executor_instance; -pub use sc_executor::NativeExecutor; -use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; -use sc_consensus_aura::{ImportQueueParams, StartAuraParams, SlotProportion}; +use sc_client_api::{ExecutorProvider, RemoteBackend}; +use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; +pub use sc_executor::NativeElseWasmExecutor; use sc_finality_grandpa::SharedVoterState; use sc_keystore::LocalKeystore; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; +use sp_consensus::SlotData; +use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; +use std::{sync::Arc, time::Duration}; // Our native executor instance. -native_executor_instance!( - pub Executor, - node_template_runtime::api::dispatch, - node_template_runtime::native_version, - frame_benchmarking::benchmarking::HostFunctions, -); - -type FullClient = sc_service::TFullClient; +pub struct ExecutorDispatch; + +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + node_template_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + node_template_runtime::native_version() + } +} + +type FullClient = + sc_service::TFullClient>; type FullBackend = sc_service::TFullBackend; type FullSelectChain = sc_consensus::LongestChain; -pub fn new_partial(config: &Configuration) -> Result, - sc_transaction_pool::FullPool, - ( - sc_consensus_aura::AuraBlockImport< - Block, - FullClient, - sc_finality_grandpa::GrandpaBlockImport, - AuraPair - >, - sc_finality_grandpa::LinkHalf, - Option, - ) ->, ServiceError> { +pub fn new_partial( + config: &Configuration, +) -> Result< + sc_service::PartialComponents< + FullClient, + FullBackend, + FullSelectChain, + sc_consensus::DefaultImportQueue, + sc_transaction_pool::FullPool, + ( + sc_finality_grandpa::GrandpaBlockImport< + FullBackend, + Block, + FullClient, + FullSelectChain, + >, + sc_finality_grandpa::LinkHalf, + Option, + ), + >, + ServiceError, +> { if config.keystore_remote.is_some() { - return Err(ServiceError::Other( - format!("Remote Keystores are not supported."))) + return Err(ServiceError::Other(format!("Remote Keystores are not supported."))) } - let inherent_data_providers = InherentDataProviders::new(); - let telemetry = config.telemetry_endpoints.clone() + let telemetry = config + .telemetry_endpoints + .clone() .filter(|x| !x.is_empty()) .map(|endpoints| -> Result<_, sc_telemetry::Error> { let worker = TelemetryWorker::new(16)?; @@ -56,18 +67,24 @@ pub fn new_partial(config: &Configuration) -> Result::new( + config.wasm_method, + config.default_heap_pages, + config.max_runtime_instances, + ); + let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( + sc_service::new_full_parts::( &config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, )?; let client = Arc::new(client); - let telemetry = telemetry - .map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", worker.run()); - telemetry - }); + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", worker.run()); + telemetry + }); let select_chain = sc_consensus::LongestChain::new(backend.clone()); @@ -75,7 +92,7 @@ pub fn new_partial(config: &Configuration) -> Result Result::new( - grandpa_block_import.clone(), client.clone(), - ); + let slot_duration = sc_consensus_aura::slot_duration(&*client)?.slot_duration(); - let import_queue = sc_consensus_aura::import_queue::( - ImportQueueParams { - block_import: aura_block_import.clone(), + let import_queue = + sc_consensus_aura::import_queue::(ImportQueueParams { + block_import: grandpa_block_import.clone(), justification_import: Some(Box::new(grandpa_block_import.clone())), client: client.clone(), - inherent_data_providers: inherent_data_providers.clone(), + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + *timestamp, + slot_duration, + ); + + Ok((timestamp, slot)) + }, spawner: &task_manager.spawn_essential_handle(), - can_author_with: sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()), - slot_duration: sc_consensus_aura::slot_duration(&*client)?, + can_author_with: sp_consensus::CanAuthorWithNativeVersion::new( + client.executor().clone(), + ), registry: config.prometheus_registry(), check_for_equivocation: Default::default(), telemetry: telemetry.as_ref().map(|x| x.handle()), - }, - )?; + })?; Ok(sc_service::PartialComponents { client, @@ -113,8 +138,7 @@ pub fn new_partial(config: &Configuration) -> Result Result mut keystore_container, select_chain, transaction_pool, - inherent_data_providers, other: (block_import, grandpa_link, mut telemetry), } = new_partial(&config)?; if let Some(url) = &config.keystore_remote { match remote_keystore(url) { Ok(k) => keystore_container.set_remote_keystore(k), - Err(e) => { - return Err(ServiceError::Other( - format!("Error hooking up remote keystore for {}: {}", url, e))) - } + Err(e) => + return Err(ServiceError::Other(format!( + "Error hooking up remote keystore for {}: {}", + url, e + ))), }; } config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); + let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + )); - let (network, network_status_sinks, system_rpc_tx, network_starter) = + let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -160,11 +188,15 @@ pub fn new_full(mut config: Configuration) -> Result import_queue, on_demand: None, block_announce_validator_builder: None, + warp_sync: Some(warp_sync), })?; if config.offchain_worker.enabled { sc_service::build_offchain_workers( - &config, task_manager.spawn_handle(), client.clone(), network.clone(), + &config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), ); } @@ -180,33 +212,27 @@ pub fn new_full(mut config: Configuration) -> Result let pool = transaction_pool.clone(); Box::new(move |deny_unsafe, _| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: pool.clone(), - deny_unsafe, - }; + let deps = + crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; - crate::rpc::create_full(deps) + Ok(crate::rpc::create_full(deps)) }) }; - let _rpc_handlers = sc_service::spawn_tasks( - sc_service::SpawnTasksParams { - network: network.clone(), - client: client.clone(), - keystore: keystore_container.sync_keystore(), - task_manager: &mut task_manager, - transaction_pool: transaction_pool.clone(), - rpc_extensions_builder, - on_demand: None, - remote_blockchain: None, - backend, - network_status_sinks, - system_rpc_tx, - config, - telemetry: telemetry.as_mut(), - }, - )?; + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + network: network.clone(), + client: client.clone(), + keystore: keystore_container.sync_keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + rpc_extensions_builder, + on_demand: None, + remote_blockchain: None, + backend, + system_rpc_tx, + config, + telemetry: telemetry.as_mut(), + })?; if role.is_authority() { let proposer_factory = sc_basic_authorship::ProposerFactory::new( @@ -220,20 +246,35 @@ pub fn new_full(mut config: Configuration) -> Result let can_author_with = sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()); - let aura = sc_consensus_aura::start_aura::( + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + let raw_slot_duration = slot_duration.slot_duration(); + + let aura = sc_consensus_aura::start_aura::( StartAuraParams { - slot_duration: sc_consensus_aura::slot_duration(&*client)?, + slot_duration, client: client.clone(), select_chain, block_import, proposer_factory, - inherent_data_providers: inherent_data_providers.clone(), + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + *timestamp, + raw_slot_duration, + ); + + Ok((timestamp, slot)) + }, force_authoring, backoff_authoring_blocks, keystore: keystore_container.sync_keystore(), can_author_with, sync_oracle: network.clone(), + justification_sync_link: network.clone(), block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), + max_block_proposal_slot_portion: None, telemetry: telemetry.as_ref().map(|x| x.handle()), }, )?; @@ -245,11 +286,8 @@ pub fn new_full(mut config: Configuration) -> Result // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. - let keystore = if role.is_authority() { - Some(keystore_container.sync_keystore()) - } else { - None - }; + let keystore = + if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; let grandpa_config = sc_finality_grandpa::Config { // FIXME #1578 make this available through chainspec @@ -258,7 +296,7 @@ pub fn new_full(mut config: Configuration) -> Result name: Some(name), observer_enabled: false, keystore, - is_authority: role.is_authority(), + local_role: role, telemetry: telemetry.as_ref().map(|x| x.handle()), }; @@ -283,7 +321,7 @@ pub fn new_full(mut config: Configuration) -> Result // if it fails we take down the service with it. task_manager.spawn_essential_handle().spawn_blocking( "grandpa-voter", - sc_finality_grandpa::run_grandpa_voter(grandpa_config)? + sc_finality_grandpa::run_grandpa_voter(grandpa_config)?, ); } @@ -293,7 +331,9 @@ pub fn new_full(mut config: Configuration) -> Result /// Builds a new service for a light client. pub fn new_light(mut config: Configuration) -> Result { - let telemetry = config.telemetry_endpoints.clone() + let telemetry = config + .telemetry_endpoints + .clone() .filter(|x| !x.is_empty()) .map(|endpoints| -> Result<_, sc_telemetry::Error> { let worker = TelemetryWorker::new(16)?; @@ -302,17 +342,23 @@ pub fn new_light(mut config: Configuration) -> Result }) .transpose()?; + let executor = NativeElseWasmExecutor::::new( + config.wasm_method, + config.default_heap_pages, + config.max_runtime_instances, + ); + let (client, backend, keystore_container, mut task_manager, on_demand) = - sc_service::new_light_parts::( + sc_service::new_light_parts::( &config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, )?; - let mut telemetry = telemetry - .map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", worker.run()); - telemetry - }); + let mut telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", worker.run()); + telemetry + }); config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); @@ -321,39 +367,49 @@ pub fn new_light(mut config: Configuration) -> Result let transaction_pool = Arc::new(sc_transaction_pool::BasicPool::new_light( config.transaction_pool.clone(), config.prometheus_registry(), - task_manager.spawn_handle(), + task_manager.spawn_essential_handle(), client.clone(), on_demand.clone(), )); - let (grandpa_block_import, _) = sc_finality_grandpa::block_import( + let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( client.clone(), &(client.clone() as Arc<_>), select_chain.clone(), telemetry.as_ref().map(|x| x.handle()), )?; - let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new( - grandpa_block_import.clone(), - client.clone(), - ); + let slot_duration = sc_consensus_aura::slot_duration(&*client)?.slot_duration(); - let import_queue = sc_consensus_aura::import_queue::( - ImportQueueParams { - block_import: aura_block_import.clone(), + let import_queue = + sc_consensus_aura::import_queue::(ImportQueueParams { + block_import: grandpa_block_import.clone(), justification_import: Some(Box::new(grandpa_block_import.clone())), client: client.clone(), - inherent_data_providers: InherentDataProviders::new(), + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + *timestamp, + slot_duration, + ); + + Ok((timestamp, slot)) + }, spawner: &task_manager.spawn_essential_handle(), can_author_with: sp_consensus::NeverCanAuthor, - slot_duration: sc_consensus_aura::slot_duration(&*client)?, registry: config.prometheus_registry(), check_for_equivocation: Default::default(), telemetry: telemetry.as_ref().map(|x| x.handle()), - }, - )?; + })?; + + let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + )); - let (network, network_status_sinks, system_rpc_tx, network_starter) = + let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -362,11 +418,35 @@ pub fn new_light(mut config: Configuration) -> Result import_queue, on_demand: Some(on_demand.clone()), block_announce_validator_builder: None, + warp_sync: Some(warp_sync), })?; if config.offchain_worker.enabled { sc_service::build_offchain_workers( - &config, task_manager.spawn_handle(), client.clone(), network.clone(), + &config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), + ); + } + + let enable_grandpa = !config.disable_grandpa; + if enable_grandpa { + let name = config.network.node_name.clone(); + + let config = sc_finality_grandpa::Config { + gossip_duration: std::time::Duration::from_millis(333), + justification_period: 512, + name: Some(name), + observer_enabled: false, + keystore: None, + local_role: config.role.clone(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + }; + + task_manager.spawn_handle().spawn_blocking( + "grandpa-observer", + sc_finality_grandpa::run_grandpa_observer(config, grandpa_link, network.clone())?, ); } @@ -375,18 +455,16 @@ pub fn new_light(mut config: Configuration) -> Result transaction_pool, task_manager: &mut task_manager, on_demand: Some(on_demand), - rpc_extensions_builder: Box::new(|_, _| ()), + rpc_extensions_builder: Box::new(|_, _| Ok(())), config, client, keystore: keystore_container.sync_keystore(), backend, network, - network_status_sinks, system_rpc_tx, telemetry: telemetry.as_mut(), })?; network_starter.start_network(); - Ok(task_manager) } diff --git a/pallets/pp/Cargo.toml b/pallets/pp/Cargo.toml new file mode 100644 index 0000000..c989966 --- /dev/null +++ b/pallets/pp/Cargo.toml @@ -0,0 +1,95 @@ +[package] +authors = ["RBB Lab"] +description = 'Programmable pools pallet' +edition = '2018' +license = 'MIT' +name = 'pallet-pp' +readme = 'README.md' +version = '3.0.0' + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] + +[dependencies] +log = "0.4.8" + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '2.0.0' + +[dependencies.contract-provider] +default-features = false +path = "../../traits/contract-provider/" + +[dependencies.frame-benchmarking] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +optional = true +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-contracts] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-contracts-primitives] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-core] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.serde] +version = '1.0.126' + +[dev-dependencies.sp-io] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.sp-core] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[features] +default = ['std'] +runtime-benchmarks = ['frame-benchmarking'] +std = [ + 'codec/std', + 'frame-support/std', + 'frame-system/std', + 'frame-benchmarking/std', + 'pallet-contracts/std', + 'pallet-contracts-primitives/std', +] +try-runtime = ['frame-support/try-runtime'] diff --git a/pallets/pp/LICENSE b/pallets/pp/LICENSE new file mode 100644 index 0000000..45fabc8 --- /dev/null +++ b/pallets/pp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 RBB S.r.l + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pallets/pp/README.md b/pallets/pp/README.md new file mode 100644 index 0000000..2fdf7c7 --- /dev/null +++ b/pallets/pp/README.md @@ -0,0 +1 @@ +License: MIT \ No newline at end of file diff --git a/pallets/pp/src/benchmarking.rs b/pallets/pp/src/benchmarking.rs new file mode 100644 index 0000000..d6326ab --- /dev/null +++ b/pallets/pp/src/benchmarking.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +//! Benchmarking setup for pallet-pp + +use super::*; + +#[allow(unused)] +use crate::Pallet as PP; +use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; +use frame_system::RawOrigin; + +benchmarks! { + do_something { + let s in 0 .. 100; + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), s) + verify { + assert_eq!(Something::::get(), Some(s)); + } +} + +impl_benchmark_test_suite!(PP, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/pallets/pp/src/lib.rs b/pallets/pp/src/lib.rs new file mode 100644 index 0000000..80fb386 --- /dev/null +++ b/pallets/pp/src/lib.rs @@ -0,0 +1,126 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +use contract_provider::ContractProvider; +use frame_support::{dispatch::Vec, weights::Weight}; +use sp_core::{crypto::UncheckedFrom, Bytes}; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_contracts::Config { + type Event: From> + IsType<::Event>; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::metadata(T::AccountId = "AccountId")] + pub enum Event {} + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub _marker: PhantomData, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + Self { + _marker: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) {} + } +} + +impl ContractProvider for Pallet +where + T::AccountId: UncheckedFrom + AsRef<[u8]>, +{ + type AccountId = T::AccountId; + + fn create( + caller: &T::AccountId, + gas_limit: Weight, + code: &Vec, + data: &Vec, + ) -> Result<(), &'static str> { + let code = pallet_contracts_primitives::Code::Upload(Bytes(code.to_vec())); + let endowment = pallet_contracts::Pallet::::subsistence_threshold(); + + let _ = pallet_contracts::Pallet::::bare_instantiate( + caller.clone(), + endowment * 100_u32.into(), // TODO + gas_limit, + code, + data.to_vec(), + Vec::new(), + false, // calculate rent projection + true, // enable debugging + ); + + Ok(()) + } + + fn call( + caller: &T::AccountId, + dest: &T::AccountId, + gas_limit: Weight, + input_data: &Vec, + ) -> Result<(), &'static str> { + let value = pallet_contracts::Pallet::::subsistence_threshold(); + + let _ = pallet_contracts::Pallet::::bare_call( + caller.clone(), + dest.clone(), + value * 0u32.into(), + gas_limit, + input_data.to_vec(), + true, // enable debugging + ); + + Ok(()) + } +} diff --git a/pallets/pp/src/mock.rs b/pallets/pp/src/mock.rs new file mode 100644 index 0000000..7d16fad --- /dev/null +++ b/pallets/pp/src/mock.rs @@ -0,0 +1,135 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +// use crate as pallet_pp; +// use sp_core::H256; +// use frame_support::parameter_types; +// use sp_runtime::{ +// traits::{BlakeTwo256, IdentityLookup}, testing::Header, +// }; +// use frame_system as system; + +// type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +// type Block = frame_system::mocking::MockBlock; + +// // Configure a mock runtime to test the pallet. +// frame_support::construct_runtime!( +// pub enum Test where +// Block = Block, +// NodeBlock = Block, +// UncheckedExtrinsic = UncheckedExtrinsic, +// { +// System: frame_system::{Pallet, Call, Config, Storage, Event}, +// PpModule: pallet_pp::{Pallet, Call, Storage, Event}, +// } +// ); + +// parameter_types! { +// pub const BlockHashCount: u64 = 250; +// pub const SS58Prefix: u8 = 42; +// } + +// impl system::Config for Test { +// type BaseCallFilter = frame_support::traits::AllowAll; +// type BlockWeights = (); +// type BlockLength = (); +// type DbWeight = (); +// type Origin = Origin; +// type Call = Call; +// type Index = u64; +// type BlockNumber = u64; +// type Hash = H256; +// type Hashing = BlakeTwo256; +// type AccountId = u64; +// type Lookup = IdentityLookup; +// type Header = Header; +// type Event = Event; +// type BlockHashCount = BlockHashCount; +// type Version = (); +// type PalletInfo = PalletInfo; +// type AccountData = (); +// type OnNewAccount = (); +// type OnKilledAccount = (); +// type SystemWeightInfo = (); +// type SS58Prefix = SS58Prefix; +// type OnSetCode = (); +// } + +// impl pallet_pp::Config for Test { +// type Event = Event; +// } + +// parameter_types! { +// pub TombstoneDeposit: Balance = deposit( +// 1, +// >::contract_info_size() +// ); +// pub DepositPerContract: Balance = TombstoneDeposit::get(); +// pub const DepositPerStorageByte: Balance = deposit(0, 1); +// pub const DepositPerStorageItem: Balance = deposit(1, 0); +// pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS); +// pub const SurchargeReward: Balance = 150 * MILLICENTS; +// pub const SignedClaimHandicap: u32 = 2; +// pub const MaxValueSize: u32 = 16 * 1024; + +// // The lazy deletion runs inside on_initialize. +// pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO * +// BlockWeights::get().max_block; + +// // The weight needed for decoding the queue should be less or equal than a fifth +// // of the overall weight dedicated to the lazy deletion. +// pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / ( +// ::WeightInfo::on_initialize_per_queue_item(1) - +// ::WeightInfo::on_initialize_per_queue_item(0) +// )) / 5) as u32; + +// pub Schedule: pallet_contracts::Schedule = Default::default(); +// } + +// impl pallet_contracts::Config for Runtime { +// type Time = Timestamp; +// type Randomness = RandomnessCollectiveFlip; +// type Currency = Balances; +// type Event = Event; +// type RentPayment = (); +// type SignedClaimHandicap = SignedClaimHandicap; +// type TombstoneDeposit = TombstoneDeposit; +// type DepositPerContract = DepositPerContract; +// type DepositPerStorageByte = DepositPerStorageByte; +// type DepositPerStorageItem = DepositPerStorageItem; +// type RentFraction = RentFraction; +// type SurchargeReward = SurchargeReward; +// type WeightPrice = pallet_transaction_payment::Pallet; +// type WeightInfo = pallet_contracts::weights::SubstrateWeight; +// type ChainExtension = (); +// type DeletionQueueDepth = DeletionQueueDepth; +// type DeletionWeightLimit = DeletionWeightLimit; +// type Call = Call; +// /// The safest default is to allow no calls at all. +// /// +// /// Runtimes should whitelist dispatchables that are allowed to be called from contracts +// /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to +// /// change because that would break already deployed contracts. The `Call` structure itself +// /// is not allowed to change the indices of existing pallets, too. +// type CallFilter = DenyAll; +// type Schedule = Schedule; +// type CallStack = [pallet_contracts::Frame; 31]; +// } + +// // Build genesis storage according to the mock runtime. +// pub fn new_test_ext() -> sp_io::TestExternalities { +// system::GenesisConfig::default().build_storage::().unwrap().into() +// } diff --git a/pallets/pp/src/tests.rs b/pallets/pp/src/tests.rs new file mode 100644 index 0000000..bb9a0b3 --- /dev/null +++ b/pallets/pp/src/tests.rs @@ -0,0 +1,39 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +// use crate::{Error, mock::*}; +// use frame_support::{assert_ok, assert_noop}; + +// #[test] +// fn it_works_for_default_value() { +// new_test_ext().execute_with(|| { +// // Dispatch a signed extrinsic. +// assert_ok!(PpModule::do_something(Origin::signed(1), 42)); +// // Read pallet storage and assert an expected result. +// assert_eq!(PpModule::something(), Some(42)); +// }); +// } + +// #[test] +// fn correct_error_for_none_value() { +// new_test_ext().execute_with(|| { +// // Ensure the expected error is thrown when no value is present. +// assert_noop!( +// PpModule::cause_error(Origin::signed(1)), +// Error::::NoneValue +// ); +// }); +// } diff --git a/pallets/template/Cargo.toml b/pallets/template/Cargo.toml index 688415c..971f543 100644 --- a/pallets/template/Cargo.toml +++ b/pallets/template/Cargo.toml @@ -12,20 +12,51 @@ version = '3.0.0' [package.metadata.docs.rs] targets = ['x86_64-unknown-linux-gnu'] -[dependencies] -# external dependencies -codec = {default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0'} - -# Substrate dependencies -frame-benchmarking = {default-features = false, optional = true, version = '3.1.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-support = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-system = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} - -[dev-dependencies] -serde = '1.0.119' -sp-core = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-io = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-runtime = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '2.0.0' + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-benchmarking] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +optional = true +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.serde] +version = '1.0.126' + +[dev-dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.sp-core] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.sp-io] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" [features] default = ['std'] diff --git a/pallets/template/src/mock.rs b/pallets/template/src/mock.rs index 8719bcb..e10f10a 100644 --- a/pallets/template/src/mock.rs +++ b/pallets/template/src/mock.rs @@ -27,7 +27,7 @@ parameter_types! { } impl system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/pallets/utxo/Cargo.toml b/pallets/utxo/Cargo.toml index 7f62abc..e0dc924 100644 --- a/pallets/utxo/Cargo.toml +++ b/pallets/utxo/Cargo.toml @@ -15,21 +15,68 @@ std = [ 'sp-core/std' ] - [dependencies] -codec = {default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0'} hex-literal = "0.2.1" log = "0.4.8" serde = '1.0.119' -# Substrate dependencies -frame-benchmarking = { default-features = false, version = '3.1.0', optional = true, git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-system = { default-features = false, version ='3.0.0' ,git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-core = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} - -[dev-dependencies] -sp-consensus-aura = { version = "0.9.0", git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-keystore = { version = "0.9.0", git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-aura = { version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-timestamp = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '2.0.0' + +[dependencies.contract-provider] +default-features = false +path = "../../traits/contract-provider/" + +[dependencies.frame-benchmarking] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +optional = true +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-core] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.sp-keystore] +version = "0.10.0-dev" +git = 'https://github.com/paritytech/substrate.git' +branch = "master" + +[dev-dependencies.pallet-aura] +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies.sp-consensus-aura] +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dev-dependencies.pallet-timestamp] +git = 'https://github.com/paritytech/substrate.git' +branch = "master" +version = '4.0.0-dev' diff --git a/pallets/utxo/rpc/Cargo.toml b/pallets/utxo/rpc/Cargo.toml new file mode 100644 index 0000000..6b90d18 --- /dev/null +++ b/pallets/utxo/rpc/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "pallet-utxo-rpc" +version = "0.1.0" +authors = ["RBB Lab"] +edition = "2018" + +[dependencies] +pallet-utxo-rpc-runtime-api = { path = "./runtime-api" } +jsonrpc-core = "18.0.0" +jsonrpc-core-client = "18.0.0" +jsonrpc-derive = "18.0.0" + +[dependencies.serde] +version = "1.0.104" +features = ["derive"] + +[dependencies.sp-blockchain] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.codec] +package = "parity-scale-codec" +version = "2.0.0" +default-features = false +features = ["derive"] + +[dependencies.sp-api] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" diff --git a/pallets/utxo/rpc/README.md b/pallets/utxo/rpc/README.md new file mode 100644 index 0000000..6766c31 --- /dev/null +++ b/pallets/utxo/rpc/README.md @@ -0,0 +1,3 @@ +# UTXO RPC interface + +TODO document calls and add examples diff --git a/pallets/utxo/rpc/runtime-api/Cargo.toml b/pallets/utxo/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000..86e5156 --- /dev/null +++ b/pallets/utxo/rpc/runtime-api/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "pallet-utxo-rpc-runtime-api" +version = "0.1.0" +authors = ["RBB Lab"] +edition = "2018" + +[dependencies.serde] +version = "1.0.104" +optional = true +features = ["derive"] + +[dependencies.codec] +package = "parity-scale-codec" +version = "2.0.0" +default-features = false +features = ["derive"] + +[dependencies.sp-api] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dev-dependencies] +serde_json = "1.0.48" + +[features] +default = ["std"] +std = [ + "serde", + "sp-api/std", + "codec/std", + "sp-runtime/std", + "frame-support/std", +] +try-runtime = ['frame-support/try-runtime'] diff --git a/pallets/utxo/rpc/runtime-api/src/lib.rs b/pallets/utxo/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000..add7b4f --- /dev/null +++ b/pallets/utxo/rpc/runtime-api/src/lib.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +#![cfg_attr(not(feature = "std"), no_std)] + +sp_api::decl_runtime_apis! { + pub trait UtxoApi { + fn send() -> u32; + } +} diff --git a/pallets/utxo/rpc/src/lib.rs b/pallets/utxo/rpc/src/lib.rs new file mode 100644 index 0000000..10d00ab --- /dev/null +++ b/pallets/utxo/rpc/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +use jsonrpc_core::{Error as RpcError, ErrorCode, Result}; +use jsonrpc_derive::rpc; +pub use pallet_utxo_rpc_runtime_api::UtxoApi as UtxoRuntimeApi; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use std::sync::Arc; + +#[rpc] +pub trait UtxoApi { + #[rpc(name = "utxo_send")] + fn send(&self, at: Option) -> Result; +} + +/// A struct that implements the [`UtxoApi`]. +pub struct Utxo { + client: Arc, + _marker: std::marker::PhantomData, +} + +impl Utxo { + /// Create new `Utxo` instance with the given reference to the client. + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: Default::default(), + } + } +} + +/// Error type of this RPC API. +pub enum Error { + /// The transaction was not decodable. + DecodeError = 1, + /// The call to runtime failed. + RuntimeError = 2, +} + +impl UtxoApi<::Hash> for Utxo +where + Block: BlockT, + C: Send + Sync + 'static, + C: ProvideRuntimeApi, + C: HeaderBackend, + C::Api: UtxoRuntimeApi, +{ + fn send(&self, at: Option<::Hash>) -> Result { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash)); + + api.send(&at).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError as i64), + message: "Unable to query dispatch info.".into(), + data: Some(format!("{:?}", e).into()), + }) + } +} diff --git a/pallets/utxo/src/lib.rs b/pallets/utxo/src/lib.rs index 59b7fc6..5571d5d 100644 --- a/pallets/utxo/src/lib.rs +++ b/pallets/utxo/src/lib.rs @@ -17,6 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub use script::*; pub use header::*; pub use pallet::*; @@ -30,271 +31,291 @@ mod tests; mod benchmarking; mod header; +mod script; pub mod weights; #[frame_support::pallet] pub mod pallet { - use core::marker::PhantomData; - #[cfg(feature = "std")] - use serde::{Deserialize, Serialize}; - - use crate::{validate_header, SignatureMethod, TXOutputHeader, TXOutputHeaderImpls, TokenType}; - use codec::{Decode, Encode}; - use frame_support::{ - dispatch::{DispatchResultWithPostInfo, Vec}, - pallet_prelude::*, - sp_io::crypto, - sp_runtime::traits::{BlakeTwo256, Dispatchable, Hash, SaturatedConversion}, - traits::IsSubType, - }; - use frame_system::pallet_prelude::*; - use sp_core::{ - sp_std::collections::btree_map::BTreeMap, - sr25519::{Public as SR25Pub, Signature as SR25Sig}, - H256, H512, - }; - - pub type Value = u128; - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet(PhantomData); - - /// runtime configuration - #[pallet::config] - pub trait Config: frame_system::Config { - type Event: From> + IsType<::Event>; - - /// The overarching call type. - type Call: Dispatchable + From> + IsSubType> + Clone; - - type WeightInfo: WeightInfo; - - fn authorities() -> Vec; - } +use core::marker::PhantomData; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +use crate::{validate_header, SignatureMethod, TXOutputHeader, TXOutputHeaderImpls, TokenType}; +use crate::{ScriptPubKey, ScriptType}; +use contract_provider::ContractProvider; +use codec::{Decode, Encode}; +use frame_support::{ + dispatch::{DispatchResultWithPostInfo, Vec}, + pallet_prelude::*, + sp_io::crypto, + sp_runtime::traits::{BlakeTwo256, Dispatchable, Hash, SaturatedConversion}, + traits::IsSubType, +}; +use frame_system::pallet_prelude::*; +use sp_core::{ + sp_std::collections::btree_map::BTreeMap, + sr25519::{Public as SR25Pub, Signature as SR25Sig}, + H256, H512, +}; + +pub type Value = u128; + +#[pallet::pallet] +#[pallet::generate_store(pub(super) trait Store)] +pub struct Pallet(PhantomData); + +/// runtime configuration +#[pallet::config] +pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + + /// The overarching call type. + type Call: Dispatchable + From> + IsSubType> + Clone; + + type WeightInfo: WeightInfo; + + type ProgrammablePool: ContractProvider; + + fn authorities() -> Vec; +} - pub trait WeightInfo { - fn spend(u: u32) -> Weight; - } +pub trait WeightInfo { + fn spend(u: u32) -> Weight; +} - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - #[derive( - Clone, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, RuntimeDebug, Hash, Default, - )] - pub struct TransactionInput { - pub(crate) outpoint: H256, - pub(crate) sig_script: H512, - } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive( + Clone, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, RuntimeDebug, Hash, Default, +)] +pub struct TransactionInput { + pub(crate) outpoint: H256, + pub(crate) sig_script: H512, +} - impl TransactionInput { - pub fn new(outpoint: H256, sig_script: H512) -> Self { - Self { - outpoint, - sig_script, - } +impl TransactionInput { + pub fn new(outpoint: H256, sig_script: H512) -> Self { + Self { + outpoint, + sig_script, } } +} - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - #[derive( - Clone, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, RuntimeDebug, Hash, Default, - )] - pub struct TransactionOutput { - pub(crate) value: Value, - pub(crate) pub_key: H256, - pub(crate) header: TXOutputHeader, - } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive( + Clone, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, RuntimeDebug, Hash, Default, +)] +pub struct TransactionOutput { + pub(crate) value: Value, + pub(crate) pub_key: H256, + pub(crate) header: TXOutputHeader, + pub(crate) script: ScriptPubKey, +} - impl TransactionOutput { - /// By default the header is 0: - /// token type for both the value and fee is MLT, - /// and the signature method is BLS. - /// functions are available in TXOutputHeaderImpls to update the header. - pub fn new(value: Value, pub_key: H256) -> Self { - Self { - value, - pub_key, - header: 0, - } +impl TransactionOutput { + /// By default the header is 0: + /// token type for both the value and fee is MLT, + /// and the signature method is BLS. + /// functions are available in TXOutputHeaderImpls to update the header. + pub fn new(value: Value, pub_key: H256) -> Self { + Self { + value, + pub_key, + header: 0, + script: ScriptPubKey::new(), } } +} - impl TXOutputHeaderImpls for TransactionOutput { - fn set_token_type(&mut self, value_token_type: TokenType) { - TokenType::insert(&mut self.header, value_token_type); - } - - fn set_signature_method(&mut self, signature_method: SignatureMethod) { - SignatureMethod::insert(&mut self.header, signature_method); - } +impl TXOutputHeaderImpls for TransactionOutput { + fn set_token_type(&mut self, value_token_type: TokenType) { + TokenType::insert(&mut self.header, value_token_type); + } - fn get_token_type(&self) -> Result { - TokenType::extract(self.header) - } + fn set_signature_method(&mut self, signature_method: SignatureMethod) { + SignatureMethod::insert(&mut self.header, signature_method); + } - fn get_signature_method(&self) -> Result { - SignatureMethod::extract(self.header) - } + fn get_token_type(&self) -> Result { + TokenType::extract(self.header) + } - fn validate_header(&self) -> Result<(), &'static str> { - validate_header(self.header) - } + fn get_signature_method(&self) -> Result { + SignatureMethod::extract(self.header) } - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Hash, Default)] - pub struct Transaction { - pub(crate) inputs: Vec, - pub(crate) outputs: Vec, + fn validate_header(&self) -> Result<(), &'static str> { + validate_header(self.header) } +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Hash, Default)] +pub struct Transaction { + pub(crate) inputs: Vec, + pub(crate) outputs: Vec, +} - #[pallet::storage] - #[pallet::getter(fn reward_total)] - pub(super) type RewardTotal = StorageValue<_, Value, ValueQuery>; +#[pallet::storage] +#[pallet::getter(fn reward_total)] +pub(super) type RewardTotal = StorageValue<_, Value, ValueQuery>; - #[pallet::storage] - #[pallet::getter(fn utxo_store)] - pub(super) type UtxoStore = - StorageMap<_, Blake2_256, H256, Option, ValueQuery>; +#[pallet::storage] +#[pallet::getter(fn utxo_store)] +pub(super) type UtxoStore = + StorageMap<_, Blake2_256, H256, Option, ValueQuery>; - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - TransactionSuccess(Transaction), +#[pallet::event] +#[pallet::generate_deposit(pub(super) fn deposit_event)] +pub enum Event { + TransactionSuccess(Transaction), +} + +#[pallet::hooks] +impl Hooks> for Pallet { + fn on_finalize(block_num: T::BlockNumber) { + disperse_reward::(&T::authorities(), block_num) } +} - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(block_num: T::BlockNumber) { - disperse_reward::(&T::authorities(), block_num) - } +// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. +pub fn get_simple_transaction(tx: &Transaction) -> Vec { + let mut trx = tx.clone(); + for input in trx.inputs.iter_mut() { + input.sig_script = H512::zero(); } - // Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. - pub fn get_simple_transaction(tx: &Transaction) -> Vec { - let mut trx = tx.clone(); - for input in trx.inputs.iter_mut() { - input.sig_script = H512::zero(); - } + trx.encode() +} - trx.encode() +fn disperse_reward(auths: &[H256], block_number: T::BlockNumber) { + let reward = >::take(); + let share_value: Value = reward + .checked_div(auths.len() as Value) + .ok_or("No authorities") + .unwrap(); + if share_value == 0 { + //put reward back if it can't be split nicely + >::put(reward as Value); + return; } - fn disperse_reward(auths: &[H256], block_number: T::BlockNumber) { - let reward = >::take(); - let share_value: Value = reward - .checked_div(auths.len() as Value) - .ok_or("No authorities") - .unwrap(); - if share_value == 0 { - //put reward back if it can't be split nicely - >::put(reward as Value); - return; + let remainder = reward + .checked_sub(share_value * auths.len() as Value) + .ok_or("Sub underflow") + .unwrap(); + + log::debug!("disperse_reward:: reward total: {:?}", remainder); + >::put(remainder as Value); + + for authority in auths { + // TODO: where do we get the header info? + // TODO: are the rewards always of MLT token type? + let utxo = TransactionOutput::new(share_value, *authority); + + let hash = { + let b_num = block_number.saturated_into::(); + BlakeTwo256::hash_of(&(&utxo, b_num)) + }; + + if !>::contains_key(hash) { + >::insert(hash, Some(utxo)); } + } +} - let remainder = reward - .checked_sub(share_value * auths.len() as Value) - .ok_or("Sub underflow") - .unwrap(); +pub fn create(caller: &T::AccountId, code: &Vec, data: &Vec) { + let weight: Weight = 6000000000; - log::debug!("disperse_reward:: reward total: {:?}", remainder); - >::put(remainder as Value); + match T::ProgrammablePool::create(caller, weight, code, data) { + Ok(_) => log::info!("success!"), + Err(e) => log::error!("failure: {:#?}", e), + } +} - for authority in auths { - // TODO: where do we get the header info? - // TODO: are the rewards always of MLT token type? - let utxo = TransactionOutput::new(share_value, *authority); +pub fn call(caller: &T::AccountId, dest: &T::AccountId, data: &Vec) { + let weight: Weight = 6000000000; - let hash = { - let b_num = block_number.saturated_into::(); - BlakeTwo256::hash_of(&(&utxo, b_num)) - }; + match T::ProgrammablePool::call(caller, dest, weight, data) { + Ok(_) => log::info!("success!"), + Err(e) => log::error!("failure: {:#?}", e), + } +} - if !>::contains_key(hash) { - >::insert(hash, Some(utxo)); - } - } +pub fn validate_transaction( + tx: &Transaction, +) -> Result { + //ensure rather than assert to avoid panic + //both inputs and outputs should contain at least 1 utxo + ensure!(!tx.inputs.is_empty(), "no inputs"); + ensure!(!tx.outputs.is_empty(), "no outputs"); + + //ensure each input is used only a single time + //maps each input into btree + //if map.len() > num of inputs then fail + //https://doc.rust-lang.org/std/collections/struct.BTreeMap.html + //WARNING workshop code has a bug here + //https://github.com/substrate-developer-hub/utxo-workshop/blob/workshop/runtime/src/utxo.rs + //input_map.len() > transaction.inputs.len() //THIS IS WRONG + { + let input_map: BTreeMap<_, ()> = tx.inputs.iter().map(|input| (input, ())).collect(); + //we want map size and input size to be equal to ensure each is used only once + ensure!( + input_map.len() == tx.inputs.len(), + "each input should be used only once" + ); + } + //ensure each output is unique + //map each output to btree to count unique elements + //WARNING example code has a bug here + //out_map.len() != transaction.outputs.len() //THIS IS WRONG + { + let out_map: BTreeMap<_, ()> = tx.outputs.iter().map(|output| (output, ())).collect(); + //check each output is defined only once + ensure!( + out_map.len() == tx.outputs.len(), + "each output should be used once" + ); } - pub fn validate_transaction( - tx: &Transaction, - ) -> Result { - //ensure rather than assert to avoid panic - //both inputs and outputs should contain at least 1 utxo - ensure!(!tx.inputs.is_empty(), "no inputs"); - ensure!(!tx.outputs.is_empty(), "no outputs"); - - //ensure each input is used only a single time - //maps each input into btree - //if map.len() > num of inputs then fail - //https://doc.rust-lang.org/std/collections/struct.BTreeMap.html - //WARNING workshop code has a bug here - //https://github.com/substrate-developer-hub/utxo-workshop/blob/workshop/runtime/src/utxo.rs - //input_map.len() > transaction.inputs.len() //THIS IS WRONG - { - let input_map: BTreeMap<_, ()> = tx.inputs.iter().map(|input| (input, ())).collect(); - //we want map size and input size to be equal to ensure each is used only once - ensure!( - input_map.len() == tx.inputs.len(), - "each input should be used only once" - ); - } - //ensure each output is unique - //map each output to btree to count unique elements - //WARNING example code has a bug here - //out_map.len() != transaction.outputs.len() //THIS IS WRONG - { - let out_map: BTreeMap<_, ()> = tx.outputs.iter().map(|output| (output, ())).collect(); - //check each output is defined only once + let mut total_input: Value = 0; + let mut total_output: Value = 0; + let mut output_index: u64 = 0; + let simple_tx = get_simple_transaction(tx); + + // In order to avoid race condition in network we maintain a list of required utxos for a tx + // Example of race condition: + // Assume both alice and bob have 10 coins each and bob owes charlie 20 coins + // In order to pay charlie alice must first send 10 coins to bob which creates a new utxo + // If bob uses the new utxo to try and send the coins to charlie before charlie receives the alice to bob 10 coins utxo + // then the tx from bob to charlie is invalid. By maintaining a list of required utxos we can ensure the tx can happen as and + // when the utxo is available. We use max longevity at the moment. That should be fixed. + + let mut missing_utxos = Vec::new(); + let mut new_utxos = Vec::new(); + let mut reward = 0; + + // Check that inputs are valid + for input in tx.inputs.iter() { + if let Some(input_utxo) = >::get(&input.outpoint) { ensure!( - out_map.len() == tx.outputs.len(), - "each output should be used once" + crypto::sr25519_verify( + &SR25Sig::from_raw(*input.sig_script.as_fixed_bytes()), + &simple_tx, + &SR25Pub::from_h256(input_utxo.pub_key) + ), + "signature must be valid" ); + total_input = total_input + .checked_add(input_utxo.value) + .ok_or("input value overflow")?; + } else { + missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } + } - let mut total_input: Value = 0; - let mut total_output: Value = 0; - let mut output_index: u64 = 0; - let simple_tx = get_simple_transaction(tx); - - // In order to avoid race condition in network we maintain a list of required utxos for a tx - // Example of race condition: - // Assume both alice and bob have 10 coins each and bob owes charlie 20 coins - // In order to pay charlie alice must first send 10 coins to bob which creates a new utxo - // If bob uses the new utxo to try and send the coins to charlie before charlie receives the alice to bob 10 coins utxo - // then the tx from bob to charlie is invalid. By maintaining a list of required utxos we can ensure the tx can happen as and - // when the utxo is available. We use max longevity at the moment. That should be fixed. - - let mut missing_utxos = Vec::new(); - let mut new_utxos = Vec::new(); - let mut reward = 0; - - // Check that inputs are valid - for input in tx.inputs.iter() { - if let Some(input_utxo) = >::get(&input.outpoint) { - ensure!( - crypto::sr25519_verify( - &SR25Sig::from_raw(*input.sig_script.as_fixed_bytes()), - &simple_tx, - &SR25Pub::from_h256(input_utxo.pub_key) - ), - "signature must be valid" - ); - total_input = total_input - .checked_add(input_utxo.value) - .ok_or("input value overflow")?; - } else { - missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); - } - } - - // Check that outputs are valid - for output in tx.outputs.iter() { - ensure!(output.value > 0, "output value must be nonzero"); - let hash = BlakeTwo256::hash_of(&(&tx, output_index)); - output_index = output_index.checked_add(1).ok_or("output index overflow")?; - ensure!(!>::contains_key(hash), "output already exists"); - + // Check that outputs are valid + for output in tx.outputs.iter() { // Check the header is valid let res = output.validate_header(); if let Err(e) = res { @@ -302,11 +323,26 @@ pub mod pallet { } ensure!(res.is_ok(), "header error. Please check the logs."); - // checked add bug in example cod where it uses checked_sub - total_output = total_output - .checked_add(output.value) - .ok_or("output value overflow")?; - new_utxos.push(hash.as_fixed_bytes().to_vec()); + match output.script.stype { + ScriptType::P2pkh => { + ensure!(output.value > 0, "output value must be nonzero"); + let hash = BlakeTwo256::hash_of(&(&tx, output_index)); + output_index = output_index.checked_add(1).ok_or("output index overflow")?; + ensure!(!>::contains_key(hash), "output already exists"); + + // checked add bug in example cod where it uses checked_sub + total_output = total_output + .checked_add(output.value) + .ok_or("output value overflow")?; + new_utxos.push(hash.as_fixed_bytes().to_vec()); + }, + ScriptType::OpCreate => { + log::info!("TODO validate OP_CREATE"); + }, + ScriptType::OpCall => { + log::info!("TODO validate OP_CALL"); + } + } } // if no race condition, check the math @@ -332,6 +368,7 @@ pub mod pallet { /// Update storage to reflect changes made by transaction /// Where each utxo key is a hash of the entire transaction and its order in the TransactionOutputs vector pub fn update_storage( + caller: &T::AccountId, tx: &Transaction, reward: Value, ) -> DispatchResultWithPostInfo { @@ -351,10 +388,23 @@ pub mod pallet { let mut index: u64 = 0; for output in &tx.outputs { - let hash = BlakeTwo256::hash_of(&(&tx, index)); - index = index.checked_add(1).ok_or("output index overflow")?; - log::debug!("inserting to UtxoStore {:?} as key {:?}", output, hash); - >::insert(hash, Some(output)); + match output.script.stype { + ScriptType::P2pkh => { + let hash = BlakeTwo256::hash_of(&(&tx, index)); + index = index.checked_add(1).ok_or("output index overflow")?; + log::debug!("inserting to UtxoStore {:?} as key {:?}", output, hash); + >::insert(hash, Some(output)); + }, + ScriptType::OpCreate => { + create::(caller, &output.script.script, &output.script.data); + }, + ScriptType::OpCall => { + // TODO convert pubkey of tx to the destination (contract transaction), fix this + let mut tmp = output.pub_key.as_bytes().clone(); + let id = T::AccountId::decode(&mut tmp).unwrap(); + call::(caller, &id, &output.script.data); + }, + } } Ok(().into()) @@ -363,11 +413,11 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::weight(T::WeightInfo::spend(tx.inputs.len().saturating_add(tx.outputs.len()) as u32))] - pub fn spend(_origin: OriginFor, tx: Transaction) -> DispatchResultWithPostInfo { + pub fn spend(origin: OriginFor, tx: Transaction) -> DispatchResultWithPostInfo { + let caller = ensure_signed(origin)?; let tx_validity = validate_transaction::(&tx)?; ensure!(tx_validity.requires.is_empty(), "missing inputs"); - - update_storage::(&tx, tx_validity.priority as Value)?; + update_storage::(&caller, &tx, tx_validity.priority as Value)?; Self::deposit_event(Event::::TransactionSuccess(tx)); Ok(().into()) @@ -399,3 +449,9 @@ pub mod pallet { } } } + +impl Pallet { + pub fn send() -> u32 { + 1337 + } +} diff --git a/pallets/utxo/src/mock.rs b/pallets/utxo/src/mock.rs index 7fc223e..8618c42 100644 --- a/pallets/utxo/src/mock.rs +++ b/pallets/utxo/src/mock.rs @@ -14,9 +14,9 @@ // limitations under the License. // // Author(s): C. Yap - use crate as pallet_utxo; use pallet_utxo::TransactionOutput; +use contract_provider::ContractProvider; use frame_support::{ parameter_types, @@ -28,8 +28,9 @@ use frame_support::{ traits::GenesisBuild, }; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{sp_std::vec, sr25519::Public, testing::SR25519, H256}; +use sp_core::{sp_std::{vec, marker::PhantomData}, sr25519::Public, testing::SR25519, H256}; use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStore}; +use frame_support::{dispatch::Vec, weights::Weight}; // need to manually import this crate since its no include by default use hex_literal::hex; @@ -42,7 +43,32 @@ pub const ALICE_PHRASE: &str = // BlakeHash of TransactionOutput::new(100, H256::from(alice_pub_key)) in fn new_test_ext() pub const GENESIS_UTXO: [u8; 32] = - hex!("931fe49afe365072e71771cd99e13cfb54fa28fad479e23556ff9de6a3dd19a9"); + hex!("0a08a0ef477ab5416c15bda44ff1938279e1677569b0cccaf73f0d30f4e935cb"); + +// Dummy programmable pool for testing +pub struct MockPool(PhantomData); + +impl ContractProvider for MockPool { + type AccountId = u64; + + fn create( + _origin: &Self::AccountId, + _weight: Weight, + _code: &Vec, + _data: &Vec, + ) -> Result<(), &'static str> { + Ok(()) + } + + fn call( + _caller: &Self::AccountId, + _dest: &Self::AccountId, + _gas_limit: Weight, + _input_data: &Vec, + ) -> Result<(), &'static str> { + Ok(()) + } +} // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( @@ -54,7 +80,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Utxo: pallet_utxo::{Pallet, Call, Config, Storage, Event}, - Aura: pallet_aura::{Pallet, Call, Config, Storage}, + Aura: pallet_aura::{Pallet, Config, Storage}, } ); @@ -69,7 +95,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); type BlockLength = (); type Origin = Origin; @@ -104,12 +130,14 @@ impl pallet_timestamp::Config for Test { impl pallet_aura::Config for Test { type AuthorityId = AuraId; + type DisabledValidators = (); } impl pallet_utxo::Config for Test { type Event = Event; type Call = Call; type WeightInfo = crate::weights::WeightInfo; + type ProgrammablePool = MockPool; fn authorities() -> Vec { Aura::authorities() diff --git a/pallets/utxo/src/script.rs b/pallets/utxo/src/script.rs new file mode 100644 index 0000000..8d85443 --- /dev/null +++ b/pallets/utxo/src/script.rs @@ -0,0 +1,100 @@ +// Copyright (c) 2021 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://spdx.org/licenses/MIT +// +// 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. +// +// Author(s): A. Altonen +use codec::{Decode, Encode}; +use frame_support::dispatch::Vec; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +// TODO what is the limit we want to set? +const SCRIPT_MAX_SIZE: u16 = 10_000; + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Clone, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, Hash, Debug)] +#[repr(u8)] +pub enum ScriptType { + P2pkh = 1, + OpCreate = 2, + OpCall = 3, +} + +impl Default for ScriptType { + fn default() -> Self { + return ScriptType::P2pkh; + } +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Clone, Encode, Decode, Eq, PartialEq, PartialOrd, Ord, Debug, Hash, Default)] +pub struct ScriptPubKey { + pub(crate) stype: ScriptType, + pub(crate) script: Vec, + pub(crate) data: Vec, +} + +impl ScriptPubKey { + /// Crete new ScriptPubKey which defaults to an empty script with type P2PKH + pub fn new() -> Self { + Self { + stype: ScriptType::default(), + script: Vec::new(), + data: Vec::new(), + } + } + + /// Set script and type for it + pub fn set_script(&mut self, stype: ScriptType, script: &Vec) -> Result<(), &'static str> { + if script.len() > SCRIPT_MAX_SIZE.into() { + return Err("Input script is too big!"); + } + + self.stype = stype; + self.script = script.clone(); + Ok(()) + } + + pub fn set_data(&mut self, data: &Vec) { + self.data = data.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::{assert_err, assert_ok}; + + #[test] + fn new_script() { + let script = ScriptPubKey::new(); + assert_eq!(script.stype, ScriptType::P2pkh); + assert_eq!(script.script.len(), 0); + assert_eq!(script.data.len(), 0); + } + + #[test] + fn edit_script() { + let mut script = ScriptPubKey::new(); + + let asm = vec![0u8; 128]; + assert_ok!(script.set_script(ScriptType::P2pkh, &asm)); + + let asm = vec![0u8; (SCRIPT_MAX_SIZE + 1).into()]; + assert_err!( + script.set_script(ScriptType::P2pkh, &asm), + "Input script is too big!" + ); + } +} diff --git a/pallets/utxo/src/tests.rs b/pallets/utxo/src/tests.rs index 583e8f4..8a0df29 100644 --- a/pallets/utxo/src/tests.rs +++ b/pallets/utxo/src/tests.rs @@ -15,7 +15,7 @@ // // Author(s): C. Yap -use crate::{mock::*, Transaction, TransactionInput, TransactionOutput, UtxoStore, Value, RewardTotal}; +use crate::{mock::*, Transaction, TransactionInput, TransactionOutput, UtxoStore, Value, RewardTotal, ScriptType}; use codec::Encode; use frame_support::{ assert_err, assert_noop, assert_ok, @@ -261,3 +261,19 @@ fn test_reward() { assert_eq!(reward, 10); }) } + +#[test] +fn test_script() { + execute_with_alice(|alice_pub_key| { + let mut tx = Transaction { + inputs: vec![tx_input_gen_no_signature()], + outputs: vec![TransactionOutput::new(90, H256::from(alice_pub_key))], + }; + + assert_ok!(tx.outputs[0].script.set_script(ScriptType::P2pkh, &Vec::new())); + + let alice_sig = crypto::sr25519_sign(SR25519, &alice_pub_key, &tx.encode()).unwrap(); + tx.inputs[0].sig_script = H512::from(alice_sig); + assert_ok!(Utxo::spend(Origin::signed(0), tx.clone())); + }) +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index a45a66c..5e7a413 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,44 +10,208 @@ version = '3.0.0' [package.metadata.docs.rs] targets = ['x86_64-unknown-linux-gnu'] -[build-dependencies] -substrate-wasm-builder={version = '4.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} - -[dependencies] -# external dependencies -codec = {default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0'} -hex-literal= {optional = true, version = '0.3.1'} - -# Substrate dependencies -frame-benchmarking = {default-features = false, optional = true, version = '3.1.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-executive = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-support = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-system = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-system-benchmarking = {default-features = false, optional = true, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -frame-system-rpc-runtime-api = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-aura = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-balances = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-grandpa = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-randomness-collective-flip = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-sudo = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-timestamp = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-transaction-payment = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -pallet-transaction-payment-rpc-runtime-api = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-api = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-block-builder = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-consensus-aura = {default-features = false, version = '0.9.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-core = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-inherents = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-offchain = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-runtime = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-session = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-std = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-transaction-pool = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} -sp-version = {default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-05'} - -# local dependencies -pallet-template = {default-features = false, version = '3.0.0', path = '../pallets/template'} -pallet-utxo = {default-features = false, path = "../pallets/utxo" } +[build-dependencies.substrate-wasm-builder] +git = 'https://github.com/paritytech/substrate.git' +version = '5.0.0-dev' +branch = "master" + +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '2.0.0' + +[dependencies.frame-benchmarking] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +optional = true +branch = "master" +version = '4.0.0-dev' + +[dependencies.frame-executive] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +branch = "master" +version = '4.0.0-dev' + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-system-benchmarking] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +optional = true +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-system-rpc-runtime-api] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.hex-literal] +optional = true +version = '0.3.1' + +[dependencies.pallet-aura] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-balances] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-grandpa] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-randomness-collective-flip] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-sudo] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-timestamp] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-transaction-payment] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-transaction-payment-rpc-runtime-api] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-api] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-block-builder] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-consensus-aura] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '0.10.0-dev' +branch = "master" + +[dependencies.sp-core] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-inherents] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-offchain] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +branch = "master" +version = '4.0.0-dev' + +[dependencies.sp-session] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-std] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-transaction-pool] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +branch = "master" +version = '4.0.0-dev' + +[dependencies.sp-version] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +# Local dependencies +[dependencies.pallet-contracts] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-contracts-primitives] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-contracts-rpc-runtime-api] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.pallet-template] +default-features = false +version = '3.0.0' +path = '../pallets/template' + +[dependencies.pallet-utxo] +default-features = false +path = "../pallets/utxo" + +[dependencies.pallet-utxo-rpc-runtime-api] +default-features = false +path = "../pallets/utxo/rpc/runtime-api" + +[dependencies.pallet-pp] +default-features = false +path = "../pallets/pp" [features] default = ['std'] @@ -78,7 +242,10 @@ std = [ 'pallet-timestamp/std', 'pallet-transaction-payment-rpc-runtime-api/std', 'pallet-transaction-payment/std', + 'pallet-utxo-rpc-runtime-api/std', 'pallet-utxo/std', + 'pallet-contracts/std', + 'pallet-contracts-primitives/std', 'sp-api/std', 'sp-block-builder/std', 'sp-consensus-aura/std', diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9ca0025..f8958a4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -31,18 +31,20 @@ pub use pallet_balances::Call as BalancesCall; pub use sp_runtime::{Permill, Perbill}; pub use frame_support::{ construct_runtime, parameter_types, StorageValue, - traits::{KeyOwnerProofSystem, Randomness, IsSubType}, + traits::{KeyOwnerProofSystem, Randomness, IsSubType, Nothing, Everything}, weights::{ Weight, IdentityFee, constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, }, }; use pallet_transaction_payment::CurrencyAdapter; +use pallet_contracts::weights::WeightInfo; /// Import the template pallet. pub use pallet_template; pub use pallet_utxo; +pub use pallet_pp; use sp_runtime::transaction_validity::{TransactionValidityError, InvalidTransaction}; /// An index to a block. @@ -126,6 +128,18 @@ pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; +pub const MILLICENTS: Balance = 1_000_000_000; +pub const CENTS: Balance = 1_000 * MILLICENTS; +pub const DOLLARS: Balance = 100 * CENTS; + +const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 15 * CENTS + (bytes as Balance) * 6 * CENTS +} + +/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. +/// This is used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); + /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { @@ -152,7 +166,7 @@ parameter_types! { impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. - type BaseCallFilter = (); + type BaseCallFilter = Everything; /// Block & extrinsics weights: base values and limits. type BlockWeights = BlockWeights; /// The maximum length of a block (in bytes). @@ -203,6 +217,7 @@ impl frame_system::Config for Runtime { impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; + type DisabledValidators = (); } impl pallet_grandpa::Config for Runtime { @@ -251,6 +266,8 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; } parameter_types! { @@ -278,6 +295,7 @@ impl pallet_utxo::Config for Runtime { type Event = Event; type Call = Call; type WeightInfo = pallet_utxo::weights::WeightInfo; + type ProgrammablePool = pallet_pp::Pallet; fn authorities() -> Vec { Aura::authorities() @@ -290,6 +308,67 @@ impl pallet_utxo::Config for Runtime { } } +impl pallet_pp::Config for Runtime { + type Event = Event; +} + +impl pallet_randomness_collective_flip::Config for Runtime {} + +parameter_types! { + pub TombstoneDeposit: Balance = deposit( + 1, + >::contract_info_size() + ); + pub DepositPerContract: Balance = TombstoneDeposit::get(); + pub const DepositPerStorageByte: Balance = deposit(0, 1); + pub const DepositPerStorageItem: Balance = deposit(1, 0); + pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS); + pub const SurchargeReward: Balance = 150 * MILLICENTS; + pub const SignedClaimHandicap: u32 = 2; + pub const MaxValueSize: u32 = 16 * 1024; + // The lazy deletion runs inside on_initialize. + pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO * + BlockWeights::get().max_block; + // The weight needed for decoding the queue should be less or equal than a fifth + // of the overall weight dedicated to the lazy deletion. + pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / ( + ::WeightInfo::on_initialize_per_queue_item(1) - + ::WeightInfo::on_initialize_per_queue_item(0) + )) / 5) as u32; + + pub Schedule: pallet_contracts::Schedule = Default::default(); +} + +impl pallet_contracts::Config for Runtime { + type Time = Timestamp; + type Randomness = RandomnessCollectiveFlip; + type Currency = Balances; + type Event = Event; + type RentPayment = (); + type SignedClaimHandicap = SignedClaimHandicap; + type TombstoneDeposit = TombstoneDeposit; + type DepositPerContract = DepositPerContract; + type DepositPerStorageByte = DepositPerStorageByte; + type DepositPerStorageItem = DepositPerStorageItem; + type RentFraction = RentFraction; + type SurchargeReward = SurchargeReward; + type WeightPrice = pallet_transaction_payment::Pallet; + type WeightInfo = pallet_contracts::weights::SubstrateWeight; + type ChainExtension = (); + type DeletionQueueDepth = DeletionQueueDepth; + type DeletionWeightLimit = DeletionWeightLimit; + type Call = Call; + /// The safest default is to allow no calls at all. + /// + /// Runtimes should whitelist dispatchables that are allowed to be called from contracts + /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to + /// change because that would break already deployed contracts. The `Call` structure itself + /// is not allowed to change the indices of existing pallets, too. + type CallFilter = Nothing; + type Schedule = Schedule; + type CallStack = [pallet_contracts::Frame; 31]; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -298,7 +377,7 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic { System: frame_system::{Pallet, Call, Config, Storage, Event}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Call, Storage}, + RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Aura: pallet_aura::{Pallet, Config}, Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, @@ -308,6 +387,8 @@ construct_runtime!( // Include the custom logic from the pallet-template in the runtime. TemplateModule: pallet_template::{Pallet, Call, Storage, Event}, Utxo: pallet_utxo::{Pallet, Call, Config, Storage, Event}, + Pp: pallet_pp::{Pallet, Call, Config, Storage, Event}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, } ); @@ -384,16 +465,13 @@ impl_runtime_apis! { ) -> sp_inherents::CheckInherentsResult { data.check_extrinsics(&block) } - - fn random_seed() -> ::Hash { - RandomnessCollectiveFlip::random_seed().0 - } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { fn validate_transaction( source: TransactionSource, tx: ::Extrinsic, + block_hash: ::Hash, ) -> TransactionValidity { if let Some(pallet_utxo::Call::spend(ref tx)) = IsSubType::>::is_sub_type(&tx.function) { @@ -405,7 +483,7 @@ impl_runtime_apis! { } } - Executive::validate_transaction(source, tx) + Executive::validate_transaction(source, tx, block_hash) } } @@ -442,6 +520,10 @@ impl_runtime_apis! { Grandpa::grandpa_authorities() } + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + fn submit_report_equivocation_unsigned_extrinsic( _equivocation_proof: fg_primitives::EquivocationProof< ::Hash, @@ -484,6 +566,54 @@ impl_runtime_apis! { } } + impl pallet_utxo_rpc_runtime_api::UtxoApi for Runtime { + fn send() -> u32 { + Utxo::send() + } + } + + impl pallet_contracts_rpc_runtime_api::ContractsApi< + Block, AccountId, Balance, BlockNumber, Hash + > for Runtime + { + fn call( + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: u64, + input_data: Vec, + ) -> pallet_contracts_primitives::ContractExecResult { + let debug = true; + Contracts::bare_call(origin, dest, value, gas_limit, input_data, debug) + } + + fn instantiate( + origin: AccountId, + endowment: Balance, + gas_limit: u64, + code: pallet_contracts_primitives::Code, + data: Vec, + salt: Vec, + ) -> pallet_contracts_primitives::ContractInstantiateResult { + let compute_rent_projection = true; + let debug = true; + Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, compute_rent_projection, debug) + } + + fn get_storage( + address: AccountId, + key: [u8; 32], + ) -> pallet_contracts_primitives::GetStorageResult { + Contracts::get_storage(address, key) + } + + fn rent_projection( + address: AccountId, + ) -> pallet_contracts_primitives::RentProjectionResult { + Contracts::rent_projection(address) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn dispatch_benchmark( @@ -515,6 +645,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_timestamp, Timestamp); add_benchmark!(params, batches, pallet_template, TemplateModule); add_benchmark!(params, batches, pallet_utxo, Utxo); + add_benchmark!(params, batches, pallet_pp, Pp); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/traits/contract-provider/Cargo.toml b/traits/contract-provider/Cargo.toml new file mode 100644 index 0000000..dce84b6 --- /dev/null +++ b/traits/contract-provider/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "contract-provider" +version = "3.0.0" +edition = "2018" +authors = ["RBB Lab"] +description = "Contract provider trait filled by programmable pools" +license = "MIT" + +[dependencies.frame-support] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-std] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.sp-runtime] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[dependencies.frame-system] +default-features = false +git = 'https://github.com/paritytech/substrate.git' +version = '4.0.0-dev' +branch = "master" + +[features] +default = ['std'] +std = [ + 'frame-support/std', + 'sp-std/std', +] diff --git a/traits/contract-provider/src/lib.rs b/traits/contract-provider/src/lib.rs new file mode 100644 index 0000000..4cf7803 --- /dev/null +++ b/traits/contract-provider/src/lib.rs @@ -0,0 +1,21 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{dispatch::Vec, weights::Weight}; + +pub trait ContractProvider { + type AccountId; + + fn create( + origin: &Self::AccountId, + gas: Weight, + code: &Vec, + data: &Vec, + ) -> Result<(), &'static str>; + + fn call( + caller: &Self::AccountId, + dest: &Self::AccountId, + gas_limit: Weight, + input_data: &Vec, + ) -> Result<(), &'static str>; +}