difftreelog
Merge branch 'develop' into feature/NFTPAR-240
in: master
33 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1228,6 +1228,26 @@
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
+name = "enumflags2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0"
+dependencies = [
+ "enumflags2_derive",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3502,6 +3522,8 @@
"sc-rpc-api",
"sc-service",
"sc-transaction-pool",
+ "serde",
+ "serde_json",
"sp-api",
"sp-block-builder",
"sp-blockchain",
@@ -3541,6 +3563,7 @@
"pallet-transaction-payment",
"pallet-transaction-payment-rpc-runtime-api",
"pallet-treasury",
+ "pallet-vesting",
"parity-scale-codec",
"serde",
"sp-api",
@@ -4020,6 +4043,20 @@
]
[[package]]
+name = "pallet-vesting"
+version = "2.0.0"
+source = "git+https://github.com/usetech-llc/substrate.git?branch=release_flexi#59646c902484d9c5e8933a80cbed551228b81274"
+dependencies = [
+ "enumflags2",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "serde",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
name = "parity-db"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
LICENSEdiffbeforeafterboth--- a/LICENSE
+++ b/LICENSE
@@ -1,24 +1,15 @@
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
+USETECH PROFESSIONAL CONFIDENTIAL
+__________________
-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 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.
+ [2019] - [2020] UseTech Professional LTD.
+ All Rights Reserved.
-For more information, please refer to <http://unlicense.org>
+NOTICE: All information contained herein is, and remains
+the property of UseTech Professional LTD. and its suppliers,
+if any. The intellectual and technical concepts contained
+herein are proprietary to UseTech Professional LTD.
+and its suppliers and may be covered by U.S. and Foreign Patents,
+patents in process, and are protected by trade secret or copyright law.
+Dissemination of this information or reproduction of this material
+is strictly forbidden unless prior written permission is obtained
+from UseTech Professional LTD..
README.mddiffbeforeafterboth--- a/README.md
+++ b/README.md
@@ -52,28 +52,27 @@
2. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.
-3. Install Rust Toolchain 1.44.0:
+3. Install Toolchain and make it default:
```bash
-rustup install 1.44.0
+rustup toolchain install nightly-2020-10-01
+rustup default nightly-2020-10-01
```
-4. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)
+4. Add wasm target for default toolchain:
+
```bash
-rustup toolchain list
-rustup default 1.44.0-x86_64-unknown-linux-gnu
+rustup target add wasm32-unknown-unknown
```
-5. Install nightly toolchain and add wasm target for it:
-
+5. Build:
```bash
-rustup toolchain install nightly-2020-05-01
-rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu
+cargo build
```
-6. Build:
+optionally, build in release:
```bash
-cargo build
+cargo build --release
```
## Run
@@ -134,4 +133,8 @@
## UI custom types
-Moved to [runtime_types.json](./runtime_types.json).
\ No newline at end of file
+Moved to [runtime_types.json](./runtime_types.json).
+
+## Running Integration Tests
+
+See [tests/README.md](./tests/README.md).
\ No newline at end of file
node/Cargo.tomldiffbeforeafterboth--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -58,6 +58,9 @@
substrate-frame-rpc-system = {version = '2.0.0', git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi'}
pallet-contracts-rpc = {version = '0.8.0', git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi'}
+serde = { version = "1.0.102", features = ["derive"] }
+serde_json = "1.0.41"
+
[features]
default = []
runtime-benchmarks = ['nft-runtime/runtime-benchmarks']
node/build.rsdiffbeforeafterboth--- a/node/build.rs
+++ b/node/build.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
fn main() {
node/src/chain_spec.rsdiffbeforeafterboth--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
// use nft_runtime::{
// AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
// SystemConfig, WASM_BINARY,
@@ -9,6 +14,7 @@
use sp_core::{sr25519, Pair, Public};
use sp_finality_grandpa::AuthorityId as GrandpaId;
use sp_runtime::traits::{IdentifyAccount, Verify};
+use serde_json::map::Map;
// Note this is the URL for the telemetry server
//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
@@ -41,6 +47,11 @@
pub fn development_config() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?;
+ let mut properties = Map::new();
+ properties.insert("tokenSymbol".into(), "UniqueTest".into());
+ properties.insert("tokenDecimals".into(), 15.into());
+ properties.insert("ss58Format".into(), 42.into()); // Generic Substrate wildcard (SS58 checksum preimage)
+
Ok(ChainSpec::from_genesis(
// Name
"Development",
@@ -71,7 +82,7 @@
// Protocol ID
None,
// Properties
- None,
+ Some(properties),
// Extensions
None,
))
@@ -132,6 +143,11 @@
endowed_accounts: Vec<AccountId>,
enable_println: bool,
) -> GenesisConfig {
+
+ let vested_accounts = vec![
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ ];
+
GenesisConfig {
system: Some(SystemConfig {
code: wasm_binary.to_vec(),
@@ -154,7 +170,14 @@
.collect(),
}),
pallet_treasury: Some(Default::default()),
- pallet_sudo: Some(SudoConfig { key: root_key }),
+ pallet_sudo: Some(SudoConfig { key: root_key }),
+ pallet_vesting: Some(VestingConfig {
+ vesting: vested_accounts
+ .iter()
+ .cloned()
+ .map(|k| (k, 1000, 100, 1 << 98))
+ .collect(),
+ }),
pallet_nft: Some(NftConfig {
collection: vec![(
1,
node/src/main.rsdiffbeforeafterboth--- a/node/src/main.rs
+++ b/node/src/main.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
//! Substrate Node Template CLI library.
#![warn(missing_docs)]
node/src/service.rsdiffbeforeafterboth1//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.23use std::sync::Arc;4use std::time::Duration;5use sc_client_api::{ExecutorProvider, RemoteBackend};6use nft_runtime::{self, opaque::Block, RuntimeApi};7use sc_service::{error::Error as ServiceError, Configuration, TaskManager};8use sp_inherents::InherentDataProviders;9use sc_executor::native_executor_instance;10pub use sc_executor::NativeExecutor;11use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};12use sc_finality_grandpa::{FinalityProofProvider as GrandpaFinalityProofProvider, SharedVoterState};1314// Our native executor instance.15native_executor_instance!(16 pub Executor,17 nft_runtime::api::dispatch,18 nft_runtime::native_version,19 frame_benchmarking::benchmarking::HostFunctions,20);2122type FullClient = sc_service::TFullClient<Block, RuntimeApi, Executor>;23type FullBackend = sc_service::TFullBackend<Block>;24type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;2526pub fn new_partial(config: &Configuration) -> Result<sc_service::PartialComponents<27 FullClient, FullBackend, FullSelectChain,28 sp_consensus::DefaultImportQueue<Block, FullClient>,29 sc_transaction_pool::FullPool<Block, FullClient>,30 (31 sc_consensus_aura::AuraBlockImport<32 Block,33 FullClient,34 sc_finality_grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, FullSelectChain>,35 AuraPair36 >,37 sc_finality_grandpa::LinkHalf<Block, FullClient, FullSelectChain>38 )39>, ServiceError> {40 let inherent_data_providers = sp_inherents::InherentDataProviders::new();4142 let (client, backend, keystore, task_manager) =43 sc_service::new_full_parts::<Block, RuntimeApi, Executor>(&config)?;44 let client = Arc::new(client);4546 let select_chain = sc_consensus::LongestChain::new(backend.clone());4748 let transaction_pool = sc_transaction_pool::BasicPool::new_full(49 config.transaction_pool.clone(),50 config.prometheus_registry(),51 task_manager.spawn_handle(),52 client.clone(),53 );5455 let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import(56 client.clone(), &(client.clone() as Arc<_>), select_chain.clone(),57 )?;5859 let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(60 grandpa_block_import.clone(), client.clone(),61 );6263 let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _, _>(64 sc_consensus_aura::slot_duration(&*client)?,65 aura_block_import.clone(),66 Some(Box::new(grandpa_block_import.clone())),67 None,68 client.clone(),69 inherent_data_providers.clone(),70 &task_manager.spawn_handle(),71 config.prometheus_registry(),72 sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()),73 )?;7475 Ok(sc_service::PartialComponents {76 client, backend, task_manager, import_queue, keystore, select_chain, transaction_pool,77 inherent_data_providers,78 other: (aura_block_import, grandpa_link),79 })80}8182/// Builds a new service for a full client.83pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {84 let sc_service::PartialComponents {85 client, backend, mut task_manager, import_queue, keystore, select_chain, transaction_pool,86 inherent_data_providers,87 other: (block_import, grandpa_link),88 } = new_partial(&config)?;8990 let finality_proof_provider =91 GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone());9293 let (network, network_status_sinks, system_rpc_tx, network_starter) =94 sc_service::build_network(sc_service::BuildNetworkParams {95 config: &config,96 client: client.clone(),97 transaction_pool: transaction_pool.clone(),98 spawn_handle: task_manager.spawn_handle(),99 import_queue,100 on_demand: None,101 block_announce_validator_builder: None,102 finality_proof_request_builder: None,103 finality_proof_provider: Some(finality_proof_provider.clone()),104 })?;105106 if config.offchain_worker.enabled {107 sc_service::build_offchain_workers(108 &config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),109 );110 }111112 let role = config.role.clone();113 let force_authoring = config.force_authoring;114 let name = config.network.node_name.clone();115 let enable_grandpa = !config.disable_grandpa;116 let prometheus_registry = config.prometheus_registry().cloned();117 let telemetry_connection_sinks = sc_service::TelemetryConnectionSinks::default();118119 let rpc_extensions_builder = {120 let client = client.clone();121 let pool = transaction_pool.clone();122123 Box::new(move |deny_unsafe, _| {124 let deps = crate::rpc::FullDeps {125 client: client.clone(),126 pool: pool.clone(),127 deny_unsafe,128 };129130 crate::rpc::create_full(deps)131 })132 };133134 sc_service::spawn_tasks(sc_service::SpawnTasksParams {135 network: network.clone(),136 client: client.clone(),137 keystore: keystore.clone(),138 task_manager: &mut task_manager,139 transaction_pool: transaction_pool.clone(),140 telemetry_connection_sinks: telemetry_connection_sinks.clone(),141 rpc_extensions_builder: rpc_extensions_builder,142 on_demand: None,143 remote_blockchain: None,144 backend, network_status_sinks, system_rpc_tx, config,145 })?;146147 if role.is_authority() {148 let proposer = sc_basic_authorship::ProposerFactory::new(149 client.clone(),150 transaction_pool,151 prometheus_registry.as_ref(),152 );153154 let can_author_with =155 sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());156157 let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _>(158 sc_consensus_aura::slot_duration(&*client)?,159 client.clone(),160 select_chain,161 block_import,162 proposer,163 network.clone(),164 inherent_data_providers.clone(),165 force_authoring,166 keystore.clone(),167 can_author_with,168 )?;169170 // the AURA authoring task is considered essential, i.e. if it171 // fails we take down the service with it.172 task_manager.spawn_essential_handle().spawn_blocking("aura", aura);173 }174175 // if the node isn't actively participating in consensus then it doesn't176 // need a keystore, regardless of which protocol we use below.177 let keystore = if role.is_authority() {178 Some(keystore as sp_core::traits::BareCryptoStorePtr)179 } else {180 None181 };182183 let grandpa_config = sc_finality_grandpa::Config {184 // FIXME #1578 make this available through chainspec185 gossip_duration: Duration::from_millis(333),186 justification_period: 512,187 name: Some(name),188 observer_enabled: false,189 keystore,190 is_authority: role.is_network_authority(),191 };192193 if enable_grandpa {194 // start the full GRANDPA voter195 // NOTE: non-authorities could run the GRANDPA observer protocol, but at196 // this point the full voter should provide better guarantees of block197 // and vote data availability than the observer. The observer has not198 // been tested extensively yet and having most nodes in a network run it199 // could lead to finality stalls.200 let grandpa_config = sc_finality_grandpa::GrandpaParams {201 config: grandpa_config,202 link: grandpa_link,203 network,204 inherent_data_providers,205 telemetry_on_connect: Some(telemetry_connection_sinks.on_connect_stream()),206 voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(),207 prometheus_registry,208 shared_voter_state: SharedVoterState::empty(),209 };210211 // the GRANDPA voter task is considered infallible, i.e.212 // if it fails we take down the service with it.213 task_manager.spawn_essential_handle().spawn_blocking(214 "grandpa-voter",215 sc_finality_grandpa::run_grandpa_voter(grandpa_config)?216 );217 } else {218 sc_finality_grandpa::setup_disabled_grandpa(219 client,220 &inherent_data_providers,221 network,222 )?;223 }224225 network_starter.start_network();226 Ok(task_manager)227}228229/// Builds a new service for a light client.230pub fn new_light(config: Configuration) -> Result<TaskManager, ServiceError> {231 let (client, backend, keystore, mut task_manager, on_demand) =232 sc_service::new_light_parts::<Block, RuntimeApi, Executor>(&config)?;233234 let transaction_pool = Arc::new(sc_transaction_pool::BasicPool::new_light(235 config.transaction_pool.clone(),236 config.prometheus_registry(),237 task_manager.spawn_handle(),238 client.clone(),239 on_demand.clone(),240 ));241242 let grandpa_block_import = sc_finality_grandpa::light_block_import(243 client.clone(), backend.clone(), &(client.clone() as Arc<_>),244 Arc::new(on_demand.checker().clone()) as Arc<_>,245 )?;246 let finality_proof_import = grandpa_block_import.clone();247 let finality_proof_request_builder =248 finality_proof_import.create_finality_proof_request_builder();249250 let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _, _>(251 sc_consensus_aura::slot_duration(&*client)?,252 grandpa_block_import,253 None,254 Some(Box::new(finality_proof_import)),255 client.clone(),256 InherentDataProviders::new(),257 &task_manager.spawn_handle(),258 config.prometheus_registry(),259 sp_consensus::NeverCanAuthor,260 )?;261262 let finality_proof_provider =263 GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone());264265 let (network, network_status_sinks, system_rpc_tx, network_starter) =266 sc_service::build_network(sc_service::BuildNetworkParams {267 config: &config,268 client: client.clone(),269 transaction_pool: transaction_pool.clone(),270 spawn_handle: task_manager.spawn_handle(),271 import_queue,272 on_demand: Some(on_demand.clone()),273 block_announce_validator_builder: None,274 finality_proof_request_builder: Some(finality_proof_request_builder),275 finality_proof_provider: Some(finality_proof_provider),276 })?;277278 if config.offchain_worker.enabled {279 sc_service::build_offchain_workers(280 &config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),281 );282 }283284 sc_service::spawn_tasks(sc_service::SpawnTasksParams {285 remote_blockchain: Some(backend.remote_blockchain()),286 transaction_pool,287 task_manager: &mut task_manager,288 on_demand: Some(on_demand),289 rpc_extensions_builder: Box::new(|_, _| ()),290 telemetry_connection_sinks: sc_service::TelemetryConnectionSinks::default(),291 config,292 client,293 keystore,294 backend,295 network,296 network_status_sinks,297 system_rpc_tx,298 })?;299300 network_starter.start_network();301302 Ok(task_manager)303}1//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.23//4// This file is subject to the terms and conditions defined in5// file 'LICENSE', which is part of this source code package.6//78use std::sync::Arc;9use std::time::Duration;10use sc_client_api::{ExecutorProvider, RemoteBackend};11use nft_runtime::{self, opaque::Block, RuntimeApi};12use sc_service::{error::Error as ServiceError, Configuration, TaskManager};13use sp_inherents::InherentDataProviders;14use sc_executor::native_executor_instance;15pub use sc_executor::NativeExecutor;16use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};17use sc_finality_grandpa::{FinalityProofProvider as GrandpaFinalityProofProvider, SharedVoterState};1819// Our native executor instance.20native_executor_instance!(21 pub Executor,22 nft_runtime::api::dispatch,23 nft_runtime::native_version,24 frame_benchmarking::benchmarking::HostFunctions,25);2627type FullClient = sc_service::TFullClient<Block, RuntimeApi, Executor>;28type FullBackend = sc_service::TFullBackend<Block>;29type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;3031pub fn new_partial(config: &Configuration) -> Result<sc_service::PartialComponents<32 FullClient, FullBackend, FullSelectChain,33 sp_consensus::DefaultImportQueue<Block, FullClient>,34 sc_transaction_pool::FullPool<Block, FullClient>,35 (36 sc_consensus_aura::AuraBlockImport<37 Block,38 FullClient,39 sc_finality_grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, FullSelectChain>,40 AuraPair41 >,42 sc_finality_grandpa::LinkHalf<Block, FullClient, FullSelectChain>43 )44>, ServiceError> {45 let inherent_data_providers = sp_inherents::InherentDataProviders::new();4647 let (client, backend, keystore, task_manager) =48 sc_service::new_full_parts::<Block, RuntimeApi, Executor>(&config)?;49 let client = Arc::new(client);5051 let select_chain = sc_consensus::LongestChain::new(backend.clone());5253 let transaction_pool = sc_transaction_pool::BasicPool::new_full(54 config.transaction_pool.clone(),55 config.prometheus_registry(),56 task_manager.spawn_handle(),57 client.clone(),58 );5960 let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import(61 client.clone(), &(client.clone() as Arc<_>), select_chain.clone(),62 )?;6364 let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(65 grandpa_block_import.clone(), client.clone(),66 );6768 let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _, _>(69 sc_consensus_aura::slot_duration(&*client)?,70 aura_block_import.clone(),71 Some(Box::new(grandpa_block_import.clone())),72 None,73 client.clone(),74 inherent_data_providers.clone(),75 &task_manager.spawn_handle(),76 config.prometheus_registry(),77 sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()),78 )?;7980 Ok(sc_service::PartialComponents {81 client, backend, task_manager, import_queue, keystore, select_chain, transaction_pool,82 inherent_data_providers,83 other: (aura_block_import, grandpa_link),84 })85}8687/// Builds a new service for a full client.88pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {89 let sc_service::PartialComponents {90 client, backend, mut task_manager, import_queue, keystore, select_chain, transaction_pool,91 inherent_data_providers,92 other: (block_import, grandpa_link),93 } = new_partial(&config)?;9495 let finality_proof_provider =96 GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone());9798 let (network, network_status_sinks, system_rpc_tx, network_starter) =99 sc_service::build_network(sc_service::BuildNetworkParams {100 config: &config,101 client: client.clone(),102 transaction_pool: transaction_pool.clone(),103 spawn_handle: task_manager.spawn_handle(),104 import_queue,105 on_demand: None,106 block_announce_validator_builder: None,107 finality_proof_request_builder: None,108 finality_proof_provider: Some(finality_proof_provider.clone()),109 })?;110111 if config.offchain_worker.enabled {112 sc_service::build_offchain_workers(113 &config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),114 );115 }116117 let role = config.role.clone();118 let force_authoring = config.force_authoring;119 let name = config.network.node_name.clone();120 let enable_grandpa = !config.disable_grandpa;121 let prometheus_registry = config.prometheus_registry().cloned();122 let telemetry_connection_sinks = sc_service::TelemetryConnectionSinks::default();123124 let rpc_extensions_builder = {125 let client = client.clone();126 let pool = transaction_pool.clone();127128 Box::new(move |deny_unsafe, _| {129 let deps = crate::rpc::FullDeps {130 client: client.clone(),131 pool: pool.clone(),132 deny_unsafe,133 };134135 crate::rpc::create_full(deps)136 })137 };138139 sc_service::spawn_tasks(sc_service::SpawnTasksParams {140 network: network.clone(),141 client: client.clone(),142 keystore: keystore.clone(),143 task_manager: &mut task_manager,144 transaction_pool: transaction_pool.clone(),145 telemetry_connection_sinks: telemetry_connection_sinks.clone(),146 rpc_extensions_builder: rpc_extensions_builder,147 on_demand: None,148 remote_blockchain: None,149 backend, network_status_sinks, system_rpc_tx, config,150 })?;151152 if role.is_authority() {153 let proposer = sc_basic_authorship::ProposerFactory::new(154 client.clone(),155 transaction_pool,156 prometheus_registry.as_ref(),157 );158159 let can_author_with =160 sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());161162 let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _>(163 sc_consensus_aura::slot_duration(&*client)?,164 client.clone(),165 select_chain,166 block_import,167 proposer,168 network.clone(),169 inherent_data_providers.clone(),170 force_authoring,171 keystore.clone(),172 can_author_with,173 )?;174175 // the AURA authoring task is considered essential, i.e. if it176 // fails we take down the service with it.177 task_manager.spawn_essential_handle().spawn_blocking("aura", aura);178 }179180 // if the node isn't actively participating in consensus then it doesn't181 // need a keystore, regardless of which protocol we use below.182 let keystore = if role.is_authority() {183 Some(keystore as sp_core::traits::BareCryptoStorePtr)184 } else {185 None186 };187188 let grandpa_config = sc_finality_grandpa::Config {189 // FIXME #1578 make this available through chainspec190 gossip_duration: Duration::from_millis(333),191 justification_period: 512,192 name: Some(name),193 observer_enabled: false,194 keystore,195 is_authority: role.is_network_authority(),196 };197198 if enable_grandpa {199 // start the full GRANDPA voter200 // NOTE: non-authorities could run the GRANDPA observer protocol, but at201 // this point the full voter should provide better guarantees of block202 // and vote data availability than the observer. The observer has not203 // been tested extensively yet and having most nodes in a network run it204 // could lead to finality stalls.205 let grandpa_config = sc_finality_grandpa::GrandpaParams {206 config: grandpa_config,207 link: grandpa_link,208 network,209 inherent_data_providers,210 telemetry_on_connect: Some(telemetry_connection_sinks.on_connect_stream()),211 voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(),212 prometheus_registry,213 shared_voter_state: SharedVoterState::empty(),214 };215216 // the GRANDPA voter task is considered infallible, i.e.217 // if it fails we take down the service with it.218 task_manager.spawn_essential_handle().spawn_blocking(219 "grandpa-voter",220 sc_finality_grandpa::run_grandpa_voter(grandpa_config)?221 );222 } else {223 sc_finality_grandpa::setup_disabled_grandpa(224 client,225 &inherent_data_providers,226 network,227 )?;228 }229230 network_starter.start_network();231 Ok(task_manager)232}233234/// Builds a new service for a light client.235pub fn new_light(config: Configuration) -> Result<TaskManager, ServiceError> {236 let (client, backend, keystore, mut task_manager, on_demand) =237 sc_service::new_light_parts::<Block, RuntimeApi, Executor>(&config)?;238239 let transaction_pool = Arc::new(sc_transaction_pool::BasicPool::new_light(240 config.transaction_pool.clone(),241 config.prometheus_registry(),242 task_manager.spawn_handle(),243 client.clone(),244 on_demand.clone(),245 ));246247 let grandpa_block_import = sc_finality_grandpa::light_block_import(248 client.clone(), backend.clone(), &(client.clone() as Arc<_>),249 Arc::new(on_demand.checker().clone()) as Arc<_>,250 )?;251 let finality_proof_import = grandpa_block_import.clone();252 let finality_proof_request_builder =253 finality_proof_import.create_finality_proof_request_builder();254255 let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _, _>(256 sc_consensus_aura::slot_duration(&*client)?,257 grandpa_block_import,258 None,259 Some(Box::new(finality_proof_import)),260 client.clone(),261 InherentDataProviders::new(),262 &task_manager.spawn_handle(),263 config.prometheus_registry(),264 sp_consensus::NeverCanAuthor,265 )?;266267 let finality_proof_provider =268 GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone());269270 let (network, network_status_sinks, system_rpc_tx, network_starter) =271 sc_service::build_network(sc_service::BuildNetworkParams {272 config: &config,273 client: client.clone(),274 transaction_pool: transaction_pool.clone(),275 spawn_handle: task_manager.spawn_handle(),276 import_queue,277 on_demand: Some(on_demand.clone()),278 block_announce_validator_builder: None,279 finality_proof_request_builder: Some(finality_proof_request_builder),280 finality_proof_provider: Some(finality_proof_provider),281 })?;282283 if config.offchain_worker.enabled {284 sc_service::build_offchain_workers(285 &config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),286 );287 }288289 sc_service::spawn_tasks(sc_service::SpawnTasksParams {290 remote_blockchain: Some(backend.remote_blockchain()),291 transaction_pool,292 task_manager: &mut task_manager,293 on_demand: Some(on_demand),294 rpc_extensions_builder: Box::new(|_, _| ()),295 telemetry_connection_sinks: sc_service::TelemetryConnectionSinks::default(),296 config,297 client,298 keystore,299 backend,300 network,301 network_status_sinks,302 system_rpc_tx,303 })?;304305 network_starter.start_network();306307 Ok(task_manager)308}pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
#![recursion_limit = "1024"]
#![cfg_attr(not(feature = "std"), no_std)]
runtime/Cargo.tomldiffbeforeafterboth--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -45,6 +45,8 @@
pallet-timestamp = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
pallet-transaction-payment = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
pallet-transaction-payment-rpc-runtime-api = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
+pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
+pallet-vesting = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
sp-api = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
sp-block-builder = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
sp-consensus-aura = { default-features = false, version = '0.8.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
@@ -56,8 +58,6 @@
sp-std = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
sp-transaction-pool = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
sp-version = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
-
-pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'release_flexi' }
[features]
default = ['std']
@@ -90,6 +90,9 @@
'pallet-timestamp/std',
'pallet-transaction-payment/std',
'pallet-transaction-payment-rpc-runtime-api/std',
+ 'pallet-treasury/std',
+ 'pallet-vesting/std',
+
'pallet-nft/std',
'sp-api/std',
'sp-block-builder/std',
@@ -103,5 +106,4 @@
'sp-transaction-pool/std',
'sp-version/std',
- 'pallet-treasury/std',
]
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
@@ -17,7 +22,7 @@
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{
- Convert, BlakeTwo256, Block as BlockT, IdentifyAccount,
+ Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount,
IdentityLookup, NumberFor, Saturating, Verify,
},
transaction_validity::{TransactionSource, TransactionValidity},
@@ -412,6 +417,18 @@
type Call = Call;
}
+parameter_types! {
+ pub const MinVestedTransfer: Balance = 100 * DOLLARS;
+}
+
+impl pallet_vesting::Trait for Runtime {
+ type Event = Event;
+ type Currency = Balances;
+ type BlockNumberToBalance = ConvertInto;
+ type MinVestedTransfer = MinVestedTransfer;
+ type WeightInfo = ();
+}
+
/// Used for the module nft in `./nft.rs`
impl pallet_nft::Trait for Runtime {
type Event = Event;
@@ -435,6 +452,7 @@
Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},
Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},
+ Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},
}
);
runtime/src/nft_weights.rsdiffbeforeafterboth--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
pub struct WeightInfo;
tests/READMEdiffbeforeafterboth--- a/tests/README
+++ /dev/null
@@ -1,12 +0,0 @@
-# Tests
-
-## How to run
-
-1. Run `npm install`.
-2. Setup a test node. You can do it using `docker-compose up -d` in parent directory.
-3. Configure tests with env variables or by editing [configuration file](src/config.ts).
-4. Run `npm run test`.
-
-## Don't run on the same node twice
-
-Some tests fail when ran on the same blockchain node twice. Either always use a new node or purge the existing one with `nft purge-chain --dev`. There is also [a script](../purge-running-node.sh) to purge and restart a node, started with docker-compose.
tests/README.mddiffbeforeafterboth--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,9 @@
+# Tests
+
+## How to run
+
+1. Run `npm install`.
+2. Setup a test node. You can do it using `docker-compose up -d` in parent directory.
+3. Optional step - configure tests with env variables or by editing [configuration file](src/config.ts).
+4. Run `npm test`.
+
tests/src/accounts.tsdiffbeforeafterboth--- a/tests/src/accounts.ts
+++ b/tests/src/accounts.ts
@@ -1,3 +1,9 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
export const bobsPublicKey = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
export const alicesPublicKey = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
export const ferdiesPublicKey = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL';
+export const nullPublicKey = '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM';
\ No newline at end of file
tests/src/blocks-production.test.tsdiffbeforeafterboth--- a/tests/src/blocks-production.test.ts
+++ b/tests/src/blocks-production.test.ts
@@ -1,8 +1,13 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import usingApi from "./substrate/substrate-api";
import promisifySubstrate from "./substrate/promisify-substrate";
import { expect } from "chai";
-describe('Blocks Production', () => {
+describe('Blocks Production smoke test', () => {
it('Node produces new blocks', async () => {
await usingApi(async api => {
const blocksPromise = promisifySubstrate(api, () => {
tests/src/config.tsdiffbeforeafterboth--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import process from 'process';
const config = {
tests/src/connection.test.tsdiffbeforeafterboth--- a/tests/src/connection.test.ts
+++ b/tests/src/connection.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import usingApi from "./substrate/substrate-api";
import { WsProvider } from '@polkadot/api';
import * as chai from 'chai';
@@ -7,7 +12,7 @@
const expect = chai.expect;
-describe('Connection', () => {
+describe('Connection smoke test', () => {
it('Connection can be established', async () => {
await usingApi(async api => {
const health = await api.rpc.system.health();
@@ -16,11 +21,17 @@
});
it('Cannot connect to 255.255.255.255', async () => {
+ console.log = function () {};
+ console.error = function () {};
+
const neverConnectProvider = new WsProvider('ws://255.255.255.255:9944');
await expect((async () => {
await usingApi(async api => {
const health = await api.rpc.system.health();
}, { provider: neverConnectProvider });
})()).to.be.eventually.rejected;
+
+ delete console.log;
+ delete console.error;
});
});
\ No newline at end of file
tests/src/contracts.test.tsdiffbeforeafterboth--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.test.ts
@@ -1,13 +1,22 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { ApiPromise } from "@polkadot/api";
import { expect } from "chai";
-import usingApi from "./substrate/substrate-api";
+import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
import fs from "fs";
import { Abi, BlueprintPromise, CodePromise } from "@polkadot/api-contract";
import { IKeyringPair } from "@polkadot/types/types";
import { Keyring } from "@polkadot/api";
import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types";
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from './util/helpers'
const value = 0;
const gasLimit = 3000n * 1000000n;
+const endowment = `1000000000000000`;
function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<BlueprintPromise> {
return new Promise<BlueprintPromise>(async (resolve, reject) => {
@@ -25,7 +34,6 @@
function deployContract(alice: IKeyringPair, blueprint: BlueprintPromise) : Promise<any> {
return new Promise<any>(async (resolve, reject) => {
- const endowment = 1000000000000000n;
const initValue = true;
const unsub = await blueprint.tx
@@ -39,28 +47,25 @@
});
}
-function runTransaction(privateKey: IKeyringPair, extrinsic: SubmittableExtrinsic<ApiTypes>) {
- return new Promise<void>(async (resolve, reject) => {
- extrinsic.signAndSend(privateKey, async result => {
- if(!result.isInBlock) {
- return;
- }
+async function prepareDeployer(api: ApiPromise) {
+ // Find unused address
+ const deployer = await findUnusedAddress(api);
- if(result.findRecord('system', 'ExtrinsicSuccess')) {
- resolve();
- }
- else {
- reject('Failed to flip value.');
- }
- })
- });
+ // Transfer balance to it
+ const keyring = new Keyring({ type: 'sr25519' });
+ const alice = keyring.addFromUri(`//Alice`);
+ let amount = new BigNumber(endowment);
+ amount = amount.plus(1e15);
+ const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
+ await submitTransactionAsync(alice, tx);
+
+ return deployer;
}
-describe('Contracts', () => {
+describe('Contracts smoke test', () => {
it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {
await usingApi(async api => {
- const keyring = new Keyring({ type: 'sr25519' });
- const alice = keyring.addFromUri("//Alice");
+ const deployer = await prepareDeployer(api);
const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
@@ -69,11 +74,11 @@
const code = new CodePromise(api, abi, wasm);
- const blueprint = await deployBlueprint(alice, code);
- const contract = (await deployContract(alice, blueprint))['contract'];
+ const blueprint = await deployBlueprint(deployer, code);
+ const contract = (await deployContract(deployer, blueprint))['contract'];
const getFlipValue = async () => {
- const result = await contract.query.get(alice.address, value, gasLimit);
+ const result = await contract.query.get(deployer.address, value, gasLimit);
if(!result.result.isSuccess) {
throw `Failed to get flipper value`;
@@ -85,7 +90,7 @@
expect(initialGetResponse).to.be.true;
const flip = contract.exec('flip', value, gasLimit);
- await runTransaction(alice, flip);
+ await submitTransactionAsync(deployer, flip);
const afterFlipGetResponse = await getFlipValue();
@@ -112,7 +117,7 @@
// const bob = new GenericAccountId(api.registry, bobsPublicKey);
// const transfer = contractInstance.exec('balance_transfer', 0, 1000000000000n, [bob, new u128(api.registry, 1000000)]);
- // await runTransaction(alicesPrivateKey, transfer);
+ // await submitTransactionAsync(alicesPrivateKey, transfer);
// const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import { default as usingApi } from "./substrate/substrate-api";
tests/src/createMultipleItems.test.tsdiffbeforeafterboth--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { assert } from 'chai';
import { alicesPublicKey } from './accounts';
import privateKey from './substrate/privateKey';
tests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/creditFeesToTreasury.test.ts
@@ -0,0 +1,112 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
+import { alicesPublicKey, bobsPublicKey } from "./accounts";
+import privateKey from "./substrate/privateKey";
+import { BigNumber } from 'bignumber.js';
+import { createCollectionExpectSuccess, getGenericResult } from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const Treasury = "5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z";
+const saneMinimumFee = 0.0001;
+const saneMaximumFee = 0.01;
+
+describe('integration test: Fees must be credited to Treasury:', () => {
+ it('Total issuance does not change', async () => {
+ await usingApi(async (api) => {
+ const totalBefore = new BigNumber((await api.query.balances.totalIssuance()).toString());
+
+ const alicePrivateKey = privateKey('//Alice');
+ const amount = new BigNumber(1);
+ const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed());
+
+ const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer));
+
+ const totalAfter = new BigNumber((await api.query.balances.totalIssuance()).toString());
+
+ expect(result.success).to.be.true;
+ expect(totalAfter.toFixed()).to.be.equal(totalBefore.toFixed());
+ });
+ });
+
+ it('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async () => {
+ await usingApi(async (api) => {
+ const alicePrivateKey = privateKey('//Alice');
+ const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+
+ const amount = new BigNumber(1);
+ const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed());
+ const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer));
+
+ const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+ const fee = aliceBalanceBefore.minus(aliceBalanceAfter).minus(amount);
+ const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+ expect(result.success).to.be.true;
+ expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
+ });
+ });
+
+ it('Treasury balance increased by failed tx fee', async () => {
+ await usingApi(async (api) => {
+ const bobPrivateKey = privateKey('//Bob');
+ const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
+
+ const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0);
+ const result = getGenericResult(await submitTransactionAsync(bobPrivateKey, badTx));
+
+ const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
+ const fee = bobBalanceBefore.minus(bobBalanceAfter);
+ const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+ expect(result.success).to.be.false;
+ expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
+ });
+ });
+
+ it('NFT Transactions also send fees to Treasury', async () => {
+ await usingApi(async (api) => {
+ const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+
+ await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+
+ const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+ const fee = aliceBalanceBefore.minus(aliceBalanceAfter);
+ const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+ expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
+ });
+ });
+
+ it('Fees are sane', async () => {
+ await usingApi(async (api) => {
+ const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+
+ await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+
+ const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
+ const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
+ const fee = aliceBalanceBefore.minus(aliceBalanceAfter);
+ const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
+
+ expect(fee.dividedBy(1e15).toNumber()).to.be.lessThan(0.01);
+ expect(fee.dividedBy(1e15).toNumber()).to.be.greaterThan(0.0001);
+ });
+ });
+
+});
+
tests/src/crefitFeesToTreasury.test.tsdiffbeforeafterboth--- a/tests/src/crefitFeesToTreasury.test.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
-import { alicesPublicKey, bobsPublicKey } from "./accounts";
-import privateKey from "./substrate/privateKey";
-import { BigNumber } from 'bignumber.js';
-import { createCollectionExpectSuccess, getGenericResult } from './util/helpers';
-
-chai.use(chaiAsPromised);
-const expect = chai.expect;
-
-const Treasury = "5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z";
-const saneMinimumFee = 0.0001;
-const saneMaximumFee = 0.01;
-
-describe('integration test: Fees must be credited to Treasury:', () => {
- it('Total issuance does not change', async () => {
- await usingApi(async (api) => {
- const totalBefore = new BigNumber((await api.query.balances.totalIssuance()).toString());
-
- const alicePrivateKey = privateKey('//Alice');
- const amount = new BigNumber(1);
- const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed());
-
- const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer));
-
- const totalAfter = new BigNumber((await api.query.balances.totalIssuance()).toString());
-
- expect(result.success).to.be.true;
- expect(totalAfter.toFixed()).to.be.equal(totalBefore.toFixed());
- });
- });
-
- it('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async () => {
- await usingApi(async (api) => {
- const alicePrivateKey = privateKey('//Alice');
- const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
-
- const amount = new BigNumber(1);
- const transfer = api.tx.balances.transfer(bobsPublicKey, amount.toFixed());
- const result = getGenericResult(await submitTransactionAsync(alicePrivateKey, transfer));
-
- const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
- const fee = aliceBalanceBefore.minus(aliceBalanceAfter).minus(amount);
- const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
-
- expect(result.success).to.be.true;
- expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
- });
- });
-
- it('Treasury balance increased by failed tx fee', async () => {
- await usingApi(async (api) => {
- const bobPrivateKey = privateKey('//Bob');
- const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
-
- const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0);
- const result = getGenericResult(await submitTransactionAsync(bobPrivateKey, badTx));
-
- const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
- const fee = bobBalanceBefore.minus(bobBalanceAfter);
- const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
-
- expect(result.success).to.be.false;
- expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
- });
- });
-
- it('NFT Transactions also send fees to Treasury', async () => {
- await usingApi(async (api) => {
- const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
-
- await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
-
- const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
- const fee = aliceBalanceBefore.minus(aliceBalanceAfter);
- const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
-
- expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
- });
- });
-
- it('Fees are sane', async () => {
- await usingApi(async (api) => {
- const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
-
- await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
-
- const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
- const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
- const fee = aliceBalanceBefore.minus(aliceBalanceAfter);
- const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
-
- expect(fee.dividedBy(1e15).toNumber()).to.be.lessThan(0.01);
- expect(fee.dividedBy(1e15).toNumber()).to.be.greaterThan(0.0001);
- });
- });
-
-});
-
tests/src/destroyCollection.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/destroyCollection.test.ts
@@ -0,0 +1,87 @@
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
+import { createCollectionExpectSuccess, createCollectionExpectFailure } from "./util/helpers";
+import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import privateKey from './substrate/privateKey';
+import { nullPublicKey } from './accounts';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+function getDestroyResult(events: EventRecord[]): boolean {
+ let success: boolean = false;
+ events.forEach(({ phase, event: { data, method, section } }) => {
+ // console.log(` ${phase}: ${section}.${method}:: ${data}`);
+ if (method == 'ExtrinsicSuccess') {
+ success = true;
+ }
+ });
+ return success;
+}
+
+async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
+ await usingApi(async (api) => {
+ // Run the DestroyCollection transaction
+ const alicePrivateKey = privateKey(senderSeed);
+ const tx = api.tx.nft.destroyCollection(collectionId);
+ const events = await submitTransactionAsync(alicePrivateKey, tx);
+ const result = getDestroyResult(events);
+
+ // Get the collection
+ const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+
+ // What to expect
+ expect(result).to.be.true;
+ expect(collection).to.be.not.null;
+ expect(collection.Owner).to.be.equal(nullPublicKey);
+ });
+}
+
+async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
+ await usingApi(async (api) => {
+ // Run the DestroyCollection transaction
+ const alicePrivateKey = privateKey(senderSeed);
+ const tx = api.tx.nft.destroyCollection(collectionId);
+ const events = await submitTransactionAsync(alicePrivateKey, tx);
+ const result = getDestroyResult(events);
+
+ // What to expect
+ expect(result).to.be.false;
+ });
+}
+
+describe('integration test: ext. destroyCollection():', () => {
+ it('NFT collection can be destroyed', async () => {
+ const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+ it('Fungible collection can be destroyed', async () => {
+ const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+ it('ReFungible collection can be destroyed', async () => {
+ const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+});
+
+describe('(!negative test!) integration test: ext. destroyCollection():', () => {
+ it('(!negative test!) Destroy a collection that never existed', async () => {
+ await usingApi(async (api) => {
+ // Find the collection that never existed
+ const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+ await destroyCollectionExpectFailure(collectionId);
+ });
+ });
+ it('(!negative test!) Destroy a collection that has already been destroyed', async () => {
+ const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+ await destroyCollectionExpectSuccess(collectionId);
+ await destroyCollectionExpectFailure(collectionId);
+ });
+ it('(!negative test!) Destroy a collection using non-owner account', async () => {
+ const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');
+ await destroyCollectionExpectFailure(collectionId, '//Bob');
+ await destroyCollectionExpectSuccess(collectionId, '//Alice');
+ });
+});
tests/src/pallet-presence.test.tsdiffbeforeafterboth--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { ApiPromise } from "@polkadot/api";
import { expect } from "chai";
import usingApi from "./substrate/substrate-api";
@@ -6,20 +11,34 @@
return api.runtimeMetadata.asLatest.modules.map(m => m.name.toString().toLowerCase());
}
-describe('Pallet presence.', () => {
- it('NFT pallet is present.', async () => {
+// Pallets that must always be present
+const requiredPallets = [
+ 'nft', 'balances', 'contracts', 'randomnesscollectiveflip', 'system', 'timestamp', 'transactionpayment', 'treasury', 'vesting'
+];
+
+// Pallets that depend on consensus and governance configuration
+const consensusPallets = [
+ 'sudo', 'grandpa', 'aura'
+];
+
+describe('Pallet presence', () => {
+ it('Required pallets are present', async () => {
await usingApi(async api => {
- expect(getModuleNames(api)).to.include('nft');
+ for (let i=0; i<requiredPallets.length; i++) {
+ expect(getModuleNames(api)).to.include(requiredPallets[i]);
+ }
});
});
- it('Balances pallet is present.', async () => {
+ it('Governance and consensus pallets are present', async () => {
await usingApi(async api => {
- expect(getModuleNames(api)).to.include('balances');
+ for (let i=0; i<consensusPallets.length; i++) {
+ expect(getModuleNames(api)).to.include(consensusPallets[i]);
+ }
});
});
- it('Contracts pallet is present.', async () => {
+ it('No extra pallets are included', async () => {
await usingApi(async api => {
- expect(getModuleNames(api)).to.include('contracts');
+ expect(getModuleNames(api).length).to.be.equal(requiredPallets.length + consensusPallets.length);
});
});
});
tests/src/substrate/get-balance.tsdiffbeforeafterboth--- a/tests/src/substrate/get-balance.ts
+++ b/tests/src/substrate/get-balance.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { ApiPromise } from "@polkadot/api";
import promisifySubstrate from "./promisify-substrate";
import {AccountInfo} from "@polkadot/types/interfaces/system";
tests/src/substrate/privateKey.tsdiffbeforeafterboth--- a/tests/src/substrate/privateKey.ts
+++ b/tests/src/substrate/privateKey.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { Keyring } from "@polkadot/api";
import { IKeyringPair } from "@polkadot/types/types";
tests/src/substrate/promisify-substrate.tsdiffbeforeafterboth--- a/tests/src/substrate/promisify-substrate.ts
+++ b/tests/src/substrate/promisify-substrate.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import ApiPromise from "@polkadot/api/promise/Api";
type PromiseType<T> = T extends PromiseLike<infer TInner> ? TInner : T;
tests/src/substrate/substrate-api.tsdiffbeforeafterboth--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { WsProvider, ApiPromise } from "@polkadot/api";
import type { AccountId, Address, ApplyExtrinsicResult, DispatchError, DispatchInfo, EventRecord, Extrinsic, ExtrinsicStatus, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
import { IKeyringPair } from "@polkadot/types/types";
tests/src/substrate/wait-new-blocks.tsdiffbeforeafterboth--- a/tests/src/substrate/wait-new-blocks.ts
+++ b/tests/src/substrate/wait-new-blocks.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { ApiPromise } from "@polkadot/api";
export default function waitNewBlocks(api: ApiPromise, blocksCount: number = 1): Promise<void> {
tests/src/transfer.test.tsdiffbeforeafterboth--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -1,8 +1,15 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import { expect, assert } from "chai";
import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
import { alicesPublicKey, bobsPublicKey, ferdiesPublicKey } from "./accounts";
import privateKey from "./substrate/privateKey";
import getBalance from "./substrate/get-balance";
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from './util/helpers'
describe('Transfer', () => {
it('Balance transfers', async () => {
@@ -23,7 +30,8 @@
it('Inability to pay fees error message is correct', async () => {
await usingApi(async api => {
- const pk = privateKey('//Ferdie');
+ // Find unused address
+ const pk = await findUnusedAddress(api);
console.log = function () {};
console.error = function () {};
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -1,10 +1,18 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
-import type { EventRecord } from '@polkadot/types/interfaces';
+import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import { ApiPromise, Keyring } from "@polkadot/api";
import { default as usingApi, submitTransactionAsync } from "../substrate/substrate-api";
import privateKey from '../substrate/privateKey';
import { alicesPublicKey } from "../accounts";
import { strToUTF16, utf16ToStr, hexToStr } from '../util/util';
+import { IKeyringPair } from "@polkadot/types/types";
+import { BigNumber } from 'bignumber.js';
chai.use(chaiAsPromised);
const expect = chai.expect;
@@ -49,7 +57,8 @@
return result;
}
-export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string) {
+export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string): Promise<number> {
+ let collectionId: number = 0;
await usingApi(async (api) => {
// Get number of collections before the transaction
const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
@@ -75,7 +84,11 @@
expect(utf16ToStr(collection.Name)).to.be.equal(name);
expect(utf16ToStr(collection.Description)).to.be.equal(description);
expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);
+
+ collectionId = result.collectionId;
});
+
+ return collectionId;
}
export async function createCollectionExpectFailure(name: string, description: string, tokenPrefix: string, mode: string) {
@@ -98,3 +111,14 @@
});
}
+export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
+ let bal = new BigNumber(0);
+ let unused;
+ do {
+ const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000));
+ const keyring = new Keyring({ type: 'sr25519' });
+ unused = keyring.addFromUri(`//${randomSeed}`);
+ bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());
+ } while (bal.toFixed() != '0');
+ return unused;
+}
\ No newline at end of file
tests/src/util/util.tsdiffbeforeafterboth--- a/tests/src/util/util.ts
+++ b/tests/src/util/util.ts
@@ -1,3 +1,8 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
export function strToUTF16(str: string): any {
let buf: number[] = [];
for (let i=0, strLen=str.length; i < strLen; i++) {