From 89d693c101e5e9d0009945537d6bfdb374877eeb Mon Sep 17 00:00:00 2001 From: str-mv Date: Thu, 02 Jul 2020 12:42:20 +0000 Subject: [PATCH] NFTPAR-47 + contracts module --- --- a/Cargo.lock +++ b/Cargo.lock @@ -2762,8 +2762,10 @@ version = "2.0.0-rc4" dependencies = [ "futures 0.3.5", + "jsonrpc-core", "log", "nft-runtime", + "pallet-contracts-rpc", "parking_lot 0.10.2", "sc-basic-authorship", "sc-cli", @@ -2773,6 +2775,7 @@ "sc-executor", "sc-finality-grandpa", "sc-network", + "sc-rpc", "sc-service", "sc-transaction-pool", "sp-consensus", @@ -2795,6 +2798,9 @@ "frame-system", "pallet-aura", "pallet-balances", + "pallet-contracts", + "pallet-contracts-primitives", + "pallet-contracts-rpc-runtime-api", "pallet-grandpa", "pallet-nft", "pallet-randomness-collective-flip", @@ -3013,6 +3019,67 @@ ] [[package]] +name = "pallet-contracts" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "pallet-contracts-primitives", + "parity-scale-codec", + "parity-wasm", + "pwasm-utils", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-sandbox", + "sp-std", + "wasmi-validation", +] + +[[package]] +name = "pallet-contracts-primitives" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "parity-scale-codec", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-contracts-rpc" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "pallet-contracts-primitives", + "pallet-contracts-rpc-runtime-api", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", +] + +[[package]] +name = "pallet-contracts-rpc-runtime-api" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "pallet-contracts-primitives", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] name = "pallet-finality-tracker" version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" @@ -3620,6 +3687,17 @@ checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" [[package]] +name = "pwasm-utils" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" +dependencies = [ + "byteorder", + "log", + "parity-wasm", +] + +[[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5576,6 +5654,19 @@ ] [[package]] +name = "sp-sandbox" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-std", + "sp-wasm-interface", + "wasmi", +] + +[[package]] name = "sp-serializer" version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?tag=v2.0.0-rc4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -20,6 +20,16 @@ log = '0.4.8' parking_lot = '0.10.0' structopt = '0.3.8' +jsonrpc-core = '14.0.5' + +[dependencies.pallet-contracts-rpc] +git = 'https://github.com/paritytech/substrate.git' +version = '0.8.0-rc4' +tag = 'v2.0.0-rc4' + +[dependencies.sc-rpc] +git = 'https://github.com/paritytech/substrate.git' +tag = 'v2.0.0-rc4' [dependencies.nft-runtime] path = '../runtime' --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -8,6 +8,8 @@ use sp_runtime::traits::{Verify, IdentifyAccount}; use sc_service::ChainType; +use nft_runtime::{ContractsConfig, ContractsSchedule}; + // Note this is the URL for the telemetry server //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; @@ -111,6 +113,12 @@ balances: Some(BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), }), + contracts: Some(ContractsConfig { + current_schedule: ContractsSchedule { + // enable_println, + ..Default::default() + }, + }), aura: Some(AuraConfig { authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), }), --- a/node/src/service.rs +++ b/node/src/service.rs @@ -27,6 +27,7 @@ /// be able to perform chain operations. macro_rules! new_full_start { ($config:expr) => {{ + use jsonrpc_core::IoHandler; use std::sync::Arc; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; @@ -86,7 +87,15 @@ import_setup = Some((grandpa_block_import, grandpa_link)); Ok(import_queue) - })?; + })? + .with_rpc_extensions(|builder| -> Result, _> { + let handler = pallet_contracts_rpc::Contracts::new(builder.client().clone()); + let delegate = pallet_contracts_rpc::ContractsApi::to_delegate(handler); + + let mut io = IoHandler::default(); + io.extend_with(delegate); + Ok(io) + })?; (builder, import_setup, inherent_data_providers) }} --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -58,10 +58,7 @@ /// Next available collection ID pub NextCollectionID get(fn next_collection_id): u64; - pub Collection get(fn collection): map hasher(identity) u64 => CollectionType; - //pub Collection get(collection): map hasher(identity) u64 => CollectionType; - pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec; /// Balance owner per collection map @@ -69,10 +66,10 @@ pub ApprovedList get(fn approved): map hasher(blake2_128_concat) (u64, u64) => Vec; pub ItemList get(fn item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType; - // pub ItemList get(item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType; + pub ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64; + + pub AddressTokens get(fn address_tokens): map hasher(blake2_128_concat) (u64, T::AccountId) => Vec; - pub ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64; - // pub ItemListIndex get(item_index): map hasher(blake2_128_concat) u64 => u64; } } @@ -247,7 +244,11 @@ data: properties, }; + let current_index = ::get(collection_id); + + Self::add_token_index(collection_id, current_index, new_item.owner.clone())?; + ::insert(collection_id, current_index); >::insert((collection_id, current_index), new_item); @@ -279,6 +280,8 @@ } >::remove((collection_id, item_id)); + Self::remove_token_index(collection_id, item_id, item.owner.clone())?; + // update balance let new_balance = >::get((collection_id, item.owner.clone())) - 1; >::insert((collection_id, item.owner.clone()), new_balance); @@ -319,9 +322,13 @@ >::insert((collection_id, new_owner.clone()), balance_new_owner); // change owner - item.owner = new_owner; + let old_owner = item.owner.clone(); + item.owner = new_owner.clone(); >::insert((collection_id, item_id), item); + // update index collection + Self::move_token_index(collection_id, item_id, old_owner, new_owner.clone())?; + // reset approved list let itm: Vec = Vec::new(); >::insert((collection_id, item_id), itm); @@ -401,3 +408,55 @@ } } } + + +impl Module { + fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult { + + let list_exists = >::contains_key((collection_id, owner.clone())); + if list_exists { + + let mut list = >::get((collection_id, owner.clone())); + let item_contains = list.contains(&item_index.clone()); + + if !item_contains { + list.push(item_index.clone()); + } + + >::insert((collection_id, owner.clone()), list); + + } else { + + let mut itm = Vec::new(); + itm.push(item_index.clone()); + >::insert((collection_id, owner), itm); + } + + Ok(()) + } + + fn remove_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult { + + let list_exists = >::contains_key((collection_id, owner.clone())); + if list_exists { + + let mut list = >::get((collection_id, owner.clone())); + let item_contains = list.contains(&item_index.clone()); + + if item_contains { + list.retain(|&item| item != item_index); + >::insert((collection_id, owner), list); + } + } + + Ok(()) + } + + fn move_token_index(collection_id: u64, item_index: u64, old_owner: T::AccountId, new_owner: T::AccountId) -> DispatchResult { + + Self::remove_token_index(collection_id, item_index, old_owner)?; + Self::add_token_index(collection_id, item_index, new_owner)?; + + Ok(()) + } +} \ No newline at end of file --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -1,7 +1,12 @@ // Creating mock runtime here use crate::{Module, Trait}; -use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; +use frame_support::{ + impl_outer_origin, parameter_types, + weights::{ + Weight, + constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND }, + }}; use frame_system as system; use sp_core::H256; use sp_runtime::{ @@ -11,7 +16,8 @@ }; impl_outer_origin! { - pub enum Origin for Test {} + pub enum Origin for Test { + } } // For testing the pallet, we construct most of a mock runtime. This means @@ -24,12 +30,17 @@ pub const MaximumBlockWeight: Weight = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); + pub MaximumExtrinsicWeight: Weight = 10 * WEIGHT_PER_SECOND; } + impl system::Trait for Test { type Origin = Origin; type Call = (); type Index = u64; type BlockNumber = u64; + type BaseCallFilter = (); + type DbWeight = RocksDbWeight; + type BlockExecutionWeight = BlockExecutionWeight; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -37,6 +48,8 @@ type Header = Header; type Event = (); type BlockHashCount = BlockHashCount; + type ExtrinsicBaseWeight = ExtrinsicBaseWeight; + type MaximumExtrinsicWeight = MaximumExtrinsicWeight; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; @@ -49,6 +62,7 @@ impl Trait for Test { type Event = (); } + pub type TemplateModule = Module; // This function basically just builds a genesis storage key/value store according to --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -297,3 +297,50 @@ assert_ok!(TemplateModule::transfer_from(origin1.clone(), 1, 1, 2)); }); } + +#[test] +fn index_list() { + new_test_ext().execute_with(|| { + let size = 1024; + let origin1 = Origin::signed(1); + let origin2 = Origin::signed(2); + let origin3 = Origin::signed(3); + + assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + + assert_eq!(TemplateModule::collection(1).owner, 1); + assert_eq!(TemplateModule::collection(2).owner, 2); + assert_eq!(TemplateModule::collection(3).owner, 3); + + // create items + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 1, 1].to_vec() + )); + + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 1, 2].to_vec() + )); + + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 2, 3].to_vec() + )); + + assert_eq!(TemplateModule::address_tokens((1, 1)).len(), 3); + + // burn one + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 2)); + assert_eq!(TemplateModule::address_tokens((1, 1)).len(), 2); + + // burn another one + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 3)); + assert_eq!(TemplateModule::address_tokens((1, 1))[0], 1); + }); +} \ No newline at end of file --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -4,6 +4,9 @@ 'aura/std', 'balances/std', 'codec/std', + 'contracts/std', + 'contracts-primitives/std', + 'contracts-rpc-runtime-api/std', 'frame-executive/std', 'frame-support/std', 'grandpa/std', @@ -47,6 +50,27 @@ package = 'parity-scale-codec' version = '1.3.1' +[dependencies.contracts] +git = 'https://github.com/paritytech/substrate.git' +default-features = false +package = 'pallet-contracts' +tag = 'v2.0.0-rc4' +version = '2.0.0-rc4' + +[dependencies.contracts-primitives] +git = 'https://github.com/paritytech/substrate.git' +default-features = false +package = 'pallet-contracts-primitives' +tag = 'v2.0.0-rc4' +version = '2.0.0-rc4' + +[dependencies.contracts-rpc-runtime-api] +git = 'https://github.com/paritytech/substrate.git' +default-features = false +package = 'pallet-contracts-rpc-runtime-api' +version = '0.8.0-rc4' +tag = 'v2.0.0-rc4' + [dependencies.frame-executive] default-features = false git = 'https://github.com/paritytech/substrate.git' --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -24,6 +24,7 @@ use sp_version::RuntimeVersion; #[cfg(feature = "std")] use sp_version::NativeVersion; +use contracts_rpc_runtime_api::ContractExecResult; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] @@ -31,6 +32,7 @@ pub use timestamp::Call as TimestampCall; pub use balances::Call as BalancesCall; pub use sp_runtime::{Permill, Perbill}; +pub use contracts::Schedule as ContractsSchedule; pub use frame_support::{ construct_runtime, parameter_types, StorageValue, traits::{KeyOwnerProofSystem, Randomness}, @@ -69,6 +71,10 @@ /// Digest item type. pub type DigestItem = generic::DigestItem; +pub const MILLICENTS: Balance = 1_000_000_000; +pub const CENTS: Balance = 1_000 * MILLICENTS; +pub const DOLLARS: Balance = 100 * CENTS; + /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades @@ -254,6 +260,32 @@ type Call = Call; } +parameter_types! { + pub const TombstoneDeposit: Balance = 16 * MILLICENTS; + pub const RentByteFee: Balance = 4 * MILLICENTS; + pub const RentDepositOffset: Balance = 1000 * MILLICENTS; + pub const SurchargeReward: Balance = 150 * MILLICENTS; +} + +impl contracts::Trait for Runtime { + type Time = Timestamp; + type Randomness = RandomnessCollectiveFlip; + type Currency = Balances; + type Event = Event; + type DetermineContractAddress = contracts::SimpleAddressDeterminer; + type TrieIdGenerator = contracts::TrieIdFromParentCounter; + type RentPayment = (); + type SignedClaimHandicap = contracts::DefaultSignedClaimHandicap; + type TombstoneDeposit = TombstoneDeposit; + type StorageSizeOffset = contracts::DefaultStorageSizeOffset; + type RentByteFee = RentByteFee; + type RentDepositOffset = RentDepositOffset; + type SurchargeReward = SurchargeReward; + type MaxDepth = contracts::DefaultMaxDepth; + type MaxValueSize = contracts::DefaultMaxValueSize; + type WeightPrice = transaction_payment::Module; +} + /// Used for the module template in `./template.rs` impl nft::Trait for Runtime { type Event = Event; @@ -267,6 +299,7 @@ { System: system::{Module, Call, Config, Storage, Event}, RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage}, + Contracts: contracts::{Module, Call, Config, Storage, Event}, Timestamp: timestamp::{Module, Call, Storage, Inherent}, Aura: aura::{Module, Config, Inherent(Timestamp)}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, @@ -413,4 +446,40 @@ None } } + + impl contracts_rpc_runtime_api::ContractsApi + for Runtime + { + fn call( + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: u64, + input_data: Vec, + ) -> ContractExecResult { + let exec_result = + Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data); + match exec_result { + Ok(v) => ContractExecResult::Success { + status: v.status, + data: v.data, + }, + Err(_) => ContractExecResult::Error, + } + } + + fn get_storage( + address: AccountId, + key: [u8; 32], + ) -> contracts_primitives::GetStorageResult { + Contracts::get_storage(address, key) + } + + fn rent_projection( + address: AccountId, + ) -> contracts_primitives::RentProjectionResult { + Contracts::rent_projection(address) + } + } + } -- gitstuff