From c091cef4268124fa8fe3c9a096c1a0f8df4595a8 Mon Sep 17 00:00:00 2001 From: Igor Kozyrev Date: Mon, 14 Feb 2022 18:06:34 +0000 Subject: [PATCH] Merge branch 'develop' into feature/create-collection-ex --- --- a/Cargo.lock +++ b/Cargo.lock @@ -382,9 +382,9 @@ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" @@ -634,9 +634,9 @@ [[package]] name = "block-buffer" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array 0.14.5", ] @@ -1097,9 +1097,9 @@ [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -1229,9 +1229,9 @@ [[package]] name = "crc32fast" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] @@ -1288,9 +1288,9 @@ [[package]] name = "crypto-common" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ "generic-array 0.14.5", ] @@ -1898,13 +1898,12 @@ [[package]] name = "digest" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" dependencies = [ - "block-buffer 0.10.1", + "block-buffer 0.10.2", "crypto-common", - "generic-array 0.14.5", ] [[package]] @@ -3241,9 +3240,9 @@ [[package]] name = "httparse" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" [[package]] name = "httpdate" @@ -4664,9 +4663,9 @@ [[package]] name = "memmap2" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3179b85e1fd8b14447cbebadb75e45a1002f541b925f0bfec366d56a81c56d" +checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" dependencies = [ "libc", ] @@ -8805,7 +8804,7 @@ source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#4aeb95f7f38fcd519e2628f32f79044a8fef99d5" dependencies = [ "impl-trait-for-tuples", - "memmap2 0.5.2", + "memmap2 0.5.3", "parity-scale-codec", "sc-chain-spec-derive", "sc-network", @@ -9912,7 +9911,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures 0.2.1", - "digest 0.10.1", + "digest 0.10.2", ] [[package]] @@ -11644,7 +11643,7 @@ [[package]] name = "unique-node" -version = "0.9.14" +version = "0.9.16" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", --- a/Dockerfile-parachain +++ b/Dockerfile-parachain @@ -90,7 +90,7 @@ nvm install v15.5.0 && \ nvm use v15.5.0 -RUN git clone https://github.com/PureStake/polkadot-launch -b jlm-use-para-id-in-specs +RUN git clone https://github.com/paritytech/polkadot-launch RUN export NVM_DIR="$HOME/.nvm" && \ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ --- a/README.md +++ b/README.md @@ -29,26 +29,11 @@ Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out! -## Hackusama Update - -During the Kusama Hackaphon the following changes were made: - -- Enabled Smart Contracts Pallet -- Enabled integration between Smart Contracts and Unique Pallet (required special edition of RC4 Substrate version) -- Fixed misc. bugs in Unique Pallet -- Deployed Unique TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README. -- New Features: - - Re-Fungible Token Mode - - Off-Chain Schema to store token image URLs - - Alternative economic model - - Allow Lists and Public Mint Permission -- Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and Unique Testnet - Blockchain. - ## Application Development If you are building an application that operates NFT tokens, use [this document](doc/application_development.md). + ## Building Building Unique chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything @@ -57,8 +42,8 @@ 1. Install Rust: ```bash +sudo apt-get install git curl libssl-dev llvm pkg-config libclang-dev clang curl https://sh.rustup.rs -sSf | sh -sudo apt-get install libssl-dev pkg-config libclang-dev clang ``` 2. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall `. @@ -86,111 +71,52 @@ cargo build --release ``` -## Run - -You can start a development chain with: - -```bash -cargo run -- --dev -``` - -Detailed logs may be shown by running the node with the following environment variables set: -`RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`. +## Building as Parachain locally -If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two -validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with -testnet units. Give each node a name and expose them so they are listed on the Polkadot -[telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open. +Note: checkout this project and all related projects (see below) in the sibling folders (both under the same folder) -We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at -`/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from -the `--node-key` value that we specify below: +### Polkadot launch utility -```bash -cargo run -- \ - --base-path /tmp/alice \ - --chain=local \ - --alice \ - --node-key 0000000000000000000000000000000000000000000000000000000000000001 \ - --telemetry-url ws://telemetry.polkadot.io:1024 \ - --validator ``` - -In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database -stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's -bootnode ID on TCP port 30333: - -```bash -cargo run -- \ - --base-path /tmp/bob \ - --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \ - --chain=local \ - --bob \ - --port 30334 \ - --telemetry-url ws://telemetry.polkadot.io:1024 \ - --validator +git clone https://github.com/paritytech/polkadot-launch ``` - -Additional CLI usage options are available and may be shown by running `cargo run -- --help`. - -## Building and Running as Parachain locally - -Rust toolchain: nightly-2021-11-11 -Note: checkout this project and polkadot project (see below) in the sibling folders (both under the same folder) ### Build relay ``` git clone https://github.com/paritytech/polkadot.git cd polkadot -git checkout release-v0.9.9 +git checkout release-v0.9.16 cargo build --release ``` -### Build parachain +### Build Unique parachain Run in the root of this project: ``` -cargo --build +cargo build --release ``` -### Run 4-node Relay - -1. Download `rococo-custom-4.json` chain spec here: https://substrate.dev/cumulus-workshop/shared/chainspecs/rococo-custom-4.json -2. Use these instructions to launch 4 nodes: https://substrate.dev/cumulus-workshop/#/en/2-relay-chain/1-launch +### Build Acala parachain (optional, full config only) -Example (Run in polkadot folder. Replace `12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on` with output from Alice node): ``` -./target/release/polkadot --alice --validator --base-path ./cumulus_relay0 --chain rococo-custom-4.json --port 50555 --ws-port 9944 -./target/release/polkadot --bob --validator --base-path ./cumulus_relay1 --chain rococo-custom-4.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on --port 50556 --ws-port 9945 -./target/release/polkadot --charlie --validator --base-path ./cumulus_relay1 --chain rococo-custom-4.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on --port 50557 --ws-port 9946 -./target/release/polkadot --dave --validator --base-path ./cumulus_relay1 --chain rococo-custom-4.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWNLAmKcyee3oqSgTMthaQVXaAcXeo8RrGCzMfMVA3B5on --port 50558 --ws-port 9947 - +git clone https://github.com/AcalaNetwork/Acala +cd Acala +git checkout 54db3acd409a0b787f116f20e163a3b24101ce38 +make build-release ``` -3. Export genesis state and runtime wasm from Unique parachain: +## Running as Parachain locally -Run from this project root: ``` -./target/release/unique-collator export-genesis-state --parachain-id 2000 > ./resources/para-2000-genesis -./target/release/unique-collator export-genesis-wasm > ./resources/para-2000-wasm +./launch-testnet.sh ``` -4. Run two parachain nodes: - -Replace `12D3KooWN1ah2bFQSysEFnwZqcmcVpDDR8UedXyo6xfzV1zDNMNg` with Alice or Bob relay ID - -Run from this project root: +Optional, full setup with Acala and Statemint ``` -./target/release/unique-collator --alice --collator --force-authoring --base-path ./tmp/parachain-alice --parachain-id 2000 --port 40333 --ws-port 9844 -- --execution wasm --chain ../polkadot/rococo-custom-4.json --port 30343 --ws-port 9977 -./target/release/unique-collator --bob --collator --force-authoring --parachain-id 2000 --base-path ./tmp/parachain/bob --port 40334 --ws-port 9845 -- --execution wasm --chain ../polkadot/rococo-custom-4.json --port 30344 --ws-port 9978 --bootnodes /ip4/127.0.0.1/tcp/50556/p2p/12D3KooWN1ah2bFQSysEFnwZqcmcVpDDR8UedXyo6xfzV1zDNMNg +./launch-testnet-full.sh ``` - -4. Reserve parachain ID as described here: https://substrate.dev/cumulus-workshop/#/en/2-relay-chain/2-reserve -5. Register parachain in relay as described here: https://substrate.dev/cumulus-workshop/#/en/3-parachains/2-register - - ## Run Integration Tests 1. Install all needed dependecies @@ -202,40 +128,11 @@ 2. Run tests ``` yarn test -``` - -## Benchmarks - -First of all, add rust toolchain and make it default. -```bash -rustup target add wasm32-unknown-unknown --toolchain nightly-2021-11-11 -``` - -Then in "/node/src" run build command below -```bash -cargo +nightly-2021-11-11 build --release --features runtime-benchmarks -``` - -Run benchmark -```bash -target/release/unique-collator benchmark --chain dev --pallet "pallet_unique" --extrinsic "*" --repeat 1 ``` - -## UI custom types - -Moved to [runtime_types.json](./runtime_types.json). - -## Running Integration Tests -See [tests/README.md](./tests/README.md). ## Code Formatting -### Get formatter and linter settings into your branch (if you forked before they were introduced) -```bash -git cherry-pick -n 8ff77c21b0d30b2a4648fa35dbf61dfa9d3948a7 -``` - ### Apply formatting and clippy fixes ```bash cargo clippy @@ -252,84 +149,57 @@ cd tests && yarn eslint --ext .ts,.js src/ ``` -## Re-Enabling Ink! Contracts -Uncomment following lies: -1. In node/rpc/Cargo.toml -``` -# pallet-contracts-rpc = { version = "3.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.9' } -``` +## Karura token transfer -2. In node/rpc/src/lib.rs -``` -// C::Api: pallet_contracts_rpc::ContractsRuntimeApi, -... -// use pallet_contracts_rpc::{Contracts, ContractsApi}; -... -// io.extend_with(ContractsApi::to_delegate(Contracts::new(client.clone()))); +To get started, you need to open inbound and outbound hrmp channels. +### Next, we need to register our asset at Karura. ``` +assetRegistry -> registerForeignAsset(location, metadata) +location: + V0(X2(Parent, Parachain(PARA_ID))) +metadata: + name OPL + symbol OPL + decimals 18 +minimalBalance 1 +``` -3. In runtime/Cargo.toml +### Next, we can send tokens from Opal to Karura: ``` - # 'pallet-contracts/std', - # 'pallet-contracts-primitives/std', - # 'pallet-contracts-rpc-runtime-api/std', - # 'pallet-contract-helpers/std', -... - # [dependencies.pallet-contracts] - # git = 'https://github.com/paritytech/substrate.git' - # default-features = false - # branch = 'polkadot-v0.9.9' - # version = '3.0.0' +polkadotXcm -> reserveTransferAssets +dest: + V0(X2(Parent, Parachain())) +beneficiary: + X1(AccountId(Any, )) +assets: + V1(Concrete(0,Here), Fungible()) +feeAssetItem: + 0 +weightLimit: + +``` - # [dependencies.pallet-contracts-primitives] - # git = 'https://github.com/paritytech/substrate.git' - # default-features = false - # branch = 'polkadot-v0.9.9' - # version = '3.0.0' +The result will be displayed in ChainState +tokens -> accounts - # [dependencies.pallet-contracts-rpc-runtime-api] - # git = 'https://github.com/paritytech/substrate.git' - # default-features = false - # branch = 'polkadot-v0.9.9' - # version = '3.0.0' -... - # pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' } +### To send tokens from Karura to Opal: ``` +xtokens -> transfer -4. runtime/src/lib.rs -``` -// use pallet_contracts::weights::WeightInfo; -... -// pub use pallet_timestamp::Call as TimestampCall; -... -// mod chain_extension; -// use crate::chain_extension::{NFTExtension, Imbalance}; -... -/* -parameter_types! { - pub TombstoneDeposit: Balance = deposit( - ... -} -*/ -... -//pallet_contract_helpers::ContractSponsorshipHandler, -... -// impl pallet_contract_helpers::Config for Runtime {} -... -// Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, -... -// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage}, -... -//pallet_contract_helpers::ContractHelpersExtension, -... -/* - impl pallet_contracts_rpc_runtime_api::ContractsApi - for Runtime - { - ... - } -*/ +currencyId: + ForeingAsset + -``` +amount: + +dest: + V1 + ( + Parents:1, + X2(Parachain(), AccountId(Any, ) + ) +destWeight: + +``` \ No newline at end of file --- /dev/null +++ b/doc/hackusama_update.md @@ -0,0 +1,15 @@ +## Hackusama Update + +During the Kusama Hackaphon the following changes were made: + +- Enabled Smart Contracts Pallet +- Enabled integration between Smart Contracts and Unique Pallet (required special edition of RC4 Substrate version) +- Fixed misc. bugs in Unique Pallet +- Deployed Unique TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README. +- New Features: + - Re-Fungible Token Mode + - Off-Chain Schema to store token image URLs + - Alternative economic model + - Allow Lists and Public Mint Permission +- Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and Unique Testnet + Blockchain. \ No newline at end of file --- /dev/null +++ b/doc/re-enable_ink_contracts.md @@ -0,0 +1,81 @@ +## Re-Enabling Ink! Contracts + +Uncomment following lies: +1. In node/rpc/Cargo.toml +``` +# pallet-contracts-rpc = { version = "3.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.9' } +``` + +2. In node/rpc/src/lib.rs +``` +// C::Api: pallet_contracts_rpc::ContractsRuntimeApi, +... +// use pallet_contracts_rpc::{Contracts, ContractsApi}; +... +// io.extend_with(ContractsApi::to_delegate(Contracts::new(client.clone()))); + +``` + +3. In runtime/Cargo.toml +``` + # 'pallet-contracts/std', + # 'pallet-contracts-primitives/std', + # 'pallet-contracts-rpc-runtime-api/std', + # 'pallet-contract-helpers/std', +... + # [dependencies.pallet-contracts] + # git = 'https://github.com/paritytech/substrate.git' + # default-features = false + # branch = 'polkadot-v0.9.9' + # version = '3.0.0' + + # [dependencies.pallet-contracts-primitives] + # git = 'https://github.com/paritytech/substrate.git' + # default-features = false + # branch = 'polkadot-v0.9.9' + # version = '3.0.0' + + # [dependencies.pallet-contracts-rpc-runtime-api] + # git = 'https://github.com/paritytech/substrate.git' + # default-features = false + # branch = 'polkadot-v0.9.9' + # version = '3.0.0' +... + # pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' } +``` + +4. runtime/src/lib.rs +``` +// use pallet_contracts::weights::WeightInfo; +... +// pub use pallet_timestamp::Call as TimestampCall; +... +// mod chain_extension; +// use crate::chain_extension::{NFTExtension, Imbalance}; +... +/* +parameter_types! { + pub TombstoneDeposit: Balance = deposit( + ... +} +*/ +... +//pallet_contract_helpers::ContractSponsorshipHandler, +... +// impl pallet_contract_helpers::Config for Runtime {} +... +// Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, +... +// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage}, +... +//pallet_contract_helpers::ContractHelpersExtension, +... +/* + impl pallet_contracts_rpc_runtime_api::ContractsApi + for Runtime + { + ... + } +*/ + +``` \ No newline at end of file --- /dev/null +++ b/launch-config-full.json @@ -0,0 +1,135 @@ +{ + "relaychain": { + "bin": "../polkadot/target/release/polkadot", + "chain": "rococo-local", + "nodes": [ + { + "name": "alice", + "wsPort": 9844, + "rpcPort": 9843, + "port": 30444, + "flags": [ + "-lparachain::candidate_validation=debug", + "-lxcm=trace" + ] + }, + { + "name": "bob", + "wsPort": 9855, + "rpcPort": 9854, + "port": 30555, + "flags": [ + "-lparachain::candidate_validation=debug", + "-lxcm=trace" + ] + }, + { + "name": "charlie", + "wsPort": 9866, + "rpcPort": 9865, + "port": 30666, + "flags": [ + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "dave", + "wsPort": 9877, + "rpcPort": 9876, + "port": 30777, + "flags": [ + "-lparachain::candidate_validation=debug" + ] + }, + { + "name": "eve", + "wsPort": 9888, + "rpcPort": 9887, + "port": 30888, + "flags": [ + "-lparachain::candidate_validation=debug" + ] + } + ], + "genesis": { + "runtime": { + "runtime_genesis_config": { + "parachainsConfiguration": { + "config": { + "validation_upgrade_frequency": 1, + "validation_upgrade_delay": 1 + } + } + } + } + } + }, + "parachains": [ + { + "bin": "../unique-chain/target/release/unique-collator", + "id": "1000", + "balance": "1000000000000000000000000", + "nodes": [ + { + "port": 31200, + "wsPort": 9944, + "rpcPort": 9933, + "name": "alice", + "flags": [ + "--rpc-cors=all", + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lxcm=trace" + ] + }, + { + "port": 31201, + "wsPort": 9945, + "rpcPort": 9934, + "name": "bob", + "flags": [ + "--rpc-cors=all", + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lxcm=trace" + ] + } + ] + }, + { + "bin": "../Acala/target/release/acala", + "id": "2000", + "chain": "karura-dev", + "balance": "1000000000000000000000", + "nodes": [ + { + "wsPort": 9946, + "port": 31202, + "name": "alice", + "flags": [ + "--rpc-cors=all", + "--unsafe-rpc-external", + "--unsafe-ws-external", + "-lxcm=trace" + ] + } + ] + } + ], + "simpleParachains": [], + "hrmpChannels": [ + { + "sender": 2000, + "recipient": 1000, + "maxCapacity": 8, + "maxMessageSize": 512 + }, + { + "sender": 1000, + "recipient": 2000, + "maxCapacity": 8, + "maxMessageSize": 512 + } + ], + "finalization": false +} --- a/launch-config-westend.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "relaychain": { - "bin": "../polkadot/target/release/polkadot", - "chain": "westend-local", - "nodes": [ - { - "name": "alice", - "wsPort": 9844, - "rpcPort": 9843, - "port": 30444, - "flags": [ - "-lparachain::candidate_validation=debug" - ] - }, - { - "name": "bob", - "wsPort": 9855, - "rpcPort": 9854, - "port": 30555, - "flags": [ - "-lparachain::candidate_validation=debug" - ] - }, - { - "name": "charlie", - "wsPort": 9866, - "rpcPort": 9865, - "port": 30666, - "flags": [ - "-lparachain::candidate_validation=debug" - ] - }, - { - "name": "dave", - "wsPort": 9877, - "rpcPort": 9876, - "port": 30777, - "flags": [ - "-lparachain::candidate_validation=debug" - ] - }, - { - "name": "eve", - "wsPort": 9888, - "rpcPort": 9886, - "port": 30888, - "flags": [ - "-lparachain::candidate_validation=debug" - ] - }, - { - "name": "ferdie", - "wsPort": 9897, - "rpcPort": 9896, - "port": 30999, - "flags": [ - "-lparachain::candidate_validation=debug" - ] - } - ], - "genesis": { - "runtime": { - "runtime_genesis_config": { - "parachainsConfiguration": { - "config": { - "validation_upgrade_frequency": 1, - "validation_upgrade_delay": 1 - } - } - } - } - } - }, - "parachains": [ - { - "bin": "../unique-chain/target/release/unique-collator", - "chain": "westend-local", - "balance": "1000000000000000000000000", - "nodes": [ - { - "port": 31200, - "wsPort": 9944, - "rpcPort": 9933, - "name": "alice", - "flags": [ - "--rpc-cors=all", - "--unsafe-rpc-external", - "--unsafe-ws-external" - ] - }, - { - "port": 31201, - "wsPort": 9945, - "rpcPort": 9934, - "name": "bob", - "flags": [ - "--rpc-cors=all", - "--unsafe-rpc-external", - "--unsafe-ws-external" - ] - }, - { - "port": 31202, - "wsPort": 9946, - "rpcPort": 9935, - "name": "charlie", - "flags": [ - "--rpc-cors=all", - "--unsafe-rpc-external", - "--unsafe-ws-external" - ] - }, - { - "port": 31203, - "wsPort": 9947, - "rpcPort": 9936, - "name": "dave", - "flags": [ - "--rpc-cors=all", - "--unsafe-rpc-external", - "--unsafe-ws-external" - ] - }, - { - "port": 31204, - "wsPort": 9948, - "rpcPort": 9937, - "name": "eve", - "flags": [ - "--rpc-cors=all", - "--unsafe-rpc-external", - "--unsafe-ws-external" - ] - } - ] - } - ], - "simpleParachains": [], - "hrmpChannels": [], - "finalization": false -} --- a/launch-config.json +++ b/launch-config.json @@ -9,7 +9,8 @@ "rpcPort": 9843, "port": 30444, "flags": [ - "-lparachain::candidate_validation=debug" + "-lparachain::candidate_validation=debug", + "-lxcm=trace" ] }, { @@ -18,7 +19,8 @@ "rpcPort": 9854, "port": 30555, "flags": [ - "-lparachain::candidate_validation=debug" + "-lparachain::candidate_validation=debug", + "-lxcm=trace" ] }, { @@ -38,6 +40,15 @@ "flags": [ "-lparachain::candidate_validation=debug" ] + }, + { + "name": "eve", + "wsPort": 9888, + "rpcPort": 9887, + "port": 30888, + "flags": [ + "-lparachain::candidate_validation=debug" + ] } ], "genesis": { @@ -67,7 +78,8 @@ "flags": [ "--rpc-cors=all", "--unsafe-rpc-external", - "--unsafe-ws-external" + "--unsafe-ws-external", + "-lxcm=trace" ] }, { @@ -78,7 +90,8 @@ "flags": [ "--rpc-cors=all", "--unsafe-rpc-external", - "--unsafe-ws-external" + "--unsafe-ws-external", + "-lxcm=trace" ] } ] --- a/launch-test-env.sh +++ /dev/null @@ -1,6 +0,0 @@ -cp launch-config.json ../polkadot-launch/launch-config.json - -cd ../polkadot-launch -yarn install -yarn build -yarn start launch-config.json --- /dev/null +++ b/launch-testnet-full.sh @@ -0,0 +1,6 @@ +cp launch-config-full.json ../polkadot-launch/launch-config-full.json + +cd ../polkadot-launch +yarn install +yarn build +yarn start launch-config-full.json --- /dev/null +++ b/launch-testnet.sh @@ -0,0 +1,6 @@ +cp launch-config.json ../polkadot-launch/launch-config.json + +cd ../polkadot-launch +yarn install +yarn build +yarn start launch-config.json --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -257,7 +257,7 @@ license = 'All Rights Reserved' name = 'unique-node' repository = 'https://github.com/UniqueNetwork/unique-chain' -version = '0.9.14' +version = '0.9.16' [[bin]] name = 'unique-collator' --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -23,7 +23,8 @@ use sp_runtime::{ Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys, traits::{ - AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion, + AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, + AccountIdConversion, Zero, }, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, MultiSignature, RuntimeAppPublic, @@ -45,8 +46,9 @@ dispatch::DispatchResult, PalletId, parameter_types, StorageValue, ConsensusEngineId, traits::{ - Everything, Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, - LockIdentifier, OnUnbalanced, Randomness, FindAuthor, + tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything, + Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier, + OnUnbalanced, Randomness, FindAuthor, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, @@ -69,8 +71,9 @@ use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall}; use fp_rpc::TransactionStatus; use sp_runtime::{ - traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf}, + traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating}, transaction_validity::TransactionValidityError, + SaturatedConversion, }; // pub use pallet_timestamp::Call as TimestampCall; @@ -82,13 +85,24 @@ use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*}; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, - ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + EnsureXcmOrigin, FixedWeightBounds, LocationInverter, NativeAsset, ParentAsSuperuser, + ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, }; -use xcm_executor::{Config, XcmExecutor}; +use xcm_executor::{Config, XcmExecutor, Assets}; +use sp_std::{marker::PhantomData}; +use xcm::latest::{ + // Xcm, + AssetId::{Concrete}, + Fungibility::Fungible as XcmFungible, + MultiAsset, + Error as XcmError, +}; +use xcm_executor::traits::{MatchesFungible, WeightTrader}; +//use xcm_executor::traits::MatchesFungible; +use sp_runtime::traits::CheckedConversion; + // mod chain_extension; // use crate::chain_extension::{NFTExtension, Imbalance}; @@ -146,10 +160,10 @@ spec_name: create_runtime_str!("opal"), impl_name: create_runtime_str!("opal"), authoring_version: 1, - spec_version: 914000, - impl_version: 1, + spec_version: 916001, + impl_version: 0, apis: RUNTIME_API_VERSIONS, - transaction_version: 0, + transaction_version: 1, state_version: 0, }; @@ -616,12 +630,22 @@ AccountId32Aliases, ); +pub struct OnlySelfCurrency; +impl> MatchesFungible for OnlySelfCurrency { + fn matches_fungible(a: &MultiAsset) -> Option { + match (&a.id, &a.fun) { + (Concrete(_), XcmFungible(ref amount)) => CheckedConversion::checked_from(*amount), + _ => None, + } + } +} + /// Means for transacting assets on this chain. pub type LocalAssetTransactor = CurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + OnlySelfCurrency, // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -677,6 +701,88 @@ // ^^^ Parent & its unit plurality gets free execution ); +pub struct UsingOnlySelfCurrencyComponents< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, +>( + Weight, + Currency::Balance, + PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>, +); +impl< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > WeightTrader + for UsingOnlySelfCurrencyComponents +{ + fn new() -> Self { + Self(0, Zero::zero(), PhantomData) + } + + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + let amount = WeightToFee::calc(&weight); + let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?; + + // location to this parachain through relay chain + let option1: xcm::v1::AssetId = Concrete(MultiLocation { + parents: 1, + interior: X1(Parachain(ParachainInfo::parachain_id().into())), + }); + // direct location + let option2: xcm::v1::AssetId = Concrete(MultiLocation { + parents: 0, + interior: Here, + }); + + let required = if payment.fungible.contains_key(&option1) { + (option1, u128_amount).into() + } else if payment.fungible.contains_key(&option2) { + (option2, u128_amount).into() + } else { + (Concrete(MultiLocation::default()), u128_amount).into() + }; + + let unused = payment + .checked_sub(required) + .map_err(|_| XcmError::TooExpensive)?; + self.0 = self.0.saturating_add(weight); + self.1 = self.1.saturating_add(amount); + Ok(unused) + } + + fn refund_weight(&mut self, weight: Weight) -> Option { + let weight = weight.min(self.0); + let amount = WeightToFee::calc(&weight); + self.0 -= weight; + self.1 = self.1.saturating_sub(amount); + let amount: u128 = amount.saturated_into(); + if amount > 0 { + Some((AssetId::get(), amount).into()) + } else { + None + } + } +} +impl< + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > Drop + for UsingOnlySelfCurrencyComponents +{ + fn drop(&mut self) { + OnUnbalanced::on_unbalanced(Currency::issue(self.1)); + } +} + pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; @@ -689,7 +795,13 @@ type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = UsingComponents, RelayLocation, AccountId, Balances, ()>; + type Trader = UsingOnlySelfCurrencyComponents< + IdentityFee, + RelayLocation, + AccountId, + Balances, + (), + >; type ResponseHandler = (); // Don't handle responses for now. type SubscriptionService = PolkadotXcm; --- a/tests/package.json +++ b/tests/package.json @@ -64,6 +64,7 @@ "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts", "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts", "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts", + "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts", "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts", "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts", "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts", --- /dev/null +++ b/tests/src/xcmTransfer.test.ts @@ -0,0 +1,178 @@ +// +// 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 {WsProvider} from '@polkadot/api'; +import {ApiOptions} from '@polkadot/api/types'; +import {IKeyringPair} from '@polkadot/types/types'; +import privateKey from './substrate/privateKey'; +import usingApi, {submitTransactionAsync} from './substrate/substrate-api'; +import {getGenericResult} from './util/helpers'; +import waitNewBlocks from './substrate/wait-new-blocks'; +import getBalance from './substrate/get-balance'; +import {alicesPublicKey} from './accounts'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const UNIQUE_CHAIN = 1000; +const KARURA_CHAIN = 2000; +const KARURA_PORT = '9946'; + +describe('Integration test: Exchanging OPL with Karura', () => { + let alice: IKeyringPair; + + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + }); + + const karuraApiOptions: ApiOptions = { + provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT), + }; + + await usingApi(async (api) => { + const destination = { + V0: { + X2: [ + 'Parent', + { + Parachain: UNIQUE_CHAIN, + }, + ], + }, + }; + + const metadata = + { + name: 'OPL', + symbol: 'OPL', + decimals: 18, + minimalBalance: 1, + }; + + const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata); + const sudoTx = api.tx.sudo.sudo(tx as any); + const events = await submitTransactionAsync(alice, sudoTx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, karuraApiOptions); + }); + + it('Should connect and send OPL to Karura', async () => { + let balanceOnKaruraBefore: bigint; + + await usingApi(async (api) => { + const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAsset: 0})).toJSON() as any; + balanceOnKaruraBefore = free; + }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)}); + + await usingApi(async (api) => { + const destination = { + V0: { + X2: [ + 'Parent', + { + Parachain: KARURA_CHAIN, + }, + ], + }, + }; + + const beneficiary = { + V0: { + X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + }, + }; + + const assets = { + V1: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: 5000000000, + }, + }, + ], + }; + + const feeAssetItem = 0; + + const weightLimit = { + Limited: 5000000000, + }; + + const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }); + + await usingApi(async (api) => { + // todo do something about instant sealing, where there might not be any new blocks + await waitNewBlocks(api, 3); + const {free} = (await api.query.tokens.accounts(alice.addressRaw, {ForeignAsset: 0})).toJSON() as any; + expect(free > balanceOnKaruraBefore).to.be.true; + }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)}); + }); + + it('Should connect to Karura and send OPL back', async () => { + let balanceBefore: bigint; + + await usingApi(async (api) => { + [balanceBefore] = await getBalance(api, [alicesPublicKey]); + }); + + await usingApi(async (api) => { + const destination = { + V0: { + X3: [ + 'Parent', + { + Parachain: UNIQUE_CHAIN, + }, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ], + }, + }; + + const id = { + ForeignAsset: 0, + }; + + const amount = 5000000000; + const destWeight = 50000000; + + const tx = api.tx.xTokens.transfer(id, amount, destination, destWeight); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + expect(result.success).to.be.true; + }, {provider: new WsProvider('ws://127.0.0.1:' + KARURA_PORT)}); + + await usingApi(async (api) => { + // todo do something about instant sealing, where there might not be any new blocks + await waitNewBlocks(api, 3); + const [balanceAfter] = await getBalance(api, [alicesPublicKey]); + expect(balanceAfter > balanceBefore).to.be.true; + }); + }); +}); \ No newline at end of file -- gitstuff