diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b98e859182bc3bd93ee9e457d5413dbfea822428..62a6c2de32ddfa86fdbe6736737f7f0f3dc1768d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -65,6 +65,7 @@ variables: only: - master - /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - /^pre-v[0-9]+\.[0-9]+-[0-9a-f]+$/ - web @@ -235,15 +236,6 @@ check-web-wasm: - time cargo build --manifest-path=bin/node/cli/Cargo.toml --no-default-features --features "browser" --target=wasm32-unknown-unknown - sccache -s -node-exits: - stage: test - <<: *docker-env - except: - - /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 - script: - - ./.maintain/check_for_exit.sh - - test-full-crypto-feature: stage: test <<: *docker-env @@ -374,10 +366,14 @@ check_polkadot: trigger-contracts-ci: stage: publish needs: - - build-linux-substrate + - job: build-linux-substrate + artifacts: false + - job: test-linux-stable + artifacts: false trigger: project: parity/srml-contracts-waterfall - branch: "master" + branch: master + strategy: depend only: - master - schedules @@ -520,7 +516,7 @@ publish-gh-doc: insteadOf = "https://github.com/" EOC - unset GITHUB_TOKEN - - git clone https://github.com/substrate-developer-hub/rustdocs.git + - git clone --depth 1 https://github.com/substrate-developer-hub/rustdocs.git - rsync -ax --delete ./crate-docs/ ./rustdocs/${CI_COMMIT_REF_NAME}/ - cd ./rustdocs; git add . - git commit -m "update rustdoc ${CI_COMMIT_REF_NAME}" diff --git a/.maintain/check_for_exit.sh b/.maintain/check_for_exit.sh deleted file mode 100755 index c5a54eb83b80051e1a4186626ecb73146899ff74..0000000000000000000000000000000000000000 --- a/.maintain/check_for_exit.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -# Script that checks that a node exits after `SIGINT` was send. - -set -e - -cargo build --release -./target/release/substrate --dev & -PID=$! - -# Let the chain running for 60 seconds -sleep 60 - -# Send `SIGINT` and give the process 30 seconds to end -kill -INT $PID -timeout 30 tail --pid=$PID -f /dev/null diff --git a/.maintain/gitlab/check_runtime.sh b/.maintain/gitlab/check_runtime.sh index f989904cb5cdef9913035d985a4596d005b2f565..8c0720843803b61e2e2c71649c531f3d858eb57b 100755 --- a/.maintain/gitlab/check_runtime.sh +++ b/.maintain/gitlab/check_runtime.sh @@ -3,17 +3,18 @@ # # check for any changes in the node/src/runtime, frame/ and primitives/sr_* trees. if # there are any changes found, it should mark the PR breaksconsensus and -# "auto-fail" the PR if there isn't a change in the runtime/src/lib.rs file +# "auto-fail" the PR if there isn't a change in the runtime/src/lib.rs file # that alters the version. set -e # fail on any error -# give some context -git log --graph --oneline --decorate=short -n 10 VERSIONS_FILE="bin/node/runtime/src/lib.rs" +boldprint () { printf "|\n| \033[1m${@}\033[0m\n|\n" ; } +boldcat () { printf "|\n"; while read l; do printf "| \033[1m${l}\033[0m\n"; done; printf "|\n" ; } + github_label () { echo echo "# run github-api job for labeling it ${1}" @@ -26,22 +27,20 @@ github_label () { } +boldprint "latest 10 commits of ${CI_COMMIT_REF_NAME}" +git log --graph --oneline --decorate=short -n 10 +boldprint "make sure the master branch is available in shallow clones" git fetch --depth=${GIT_DEPTH:-100} origin master -# check if master is part of this checkout -if ! git log -n 1 origin/master -then - echo "unable to check for runtime changes: checkout does not contain origin/master branch" - exit 3 -fi -# check if the wasm sources changed +boldprint "check if the wasm sources changed" if ! git diff --name-only origin/master...${CI_COMMIT_SHA} \ - | grep -q -e '^bin/node/src/runtime' -e '^frame/' -e '^primitives/sr-' | grep -v -e '^primitives/sr-arithmetic/fuzzer' + | grep -v -e '^primitives/sr-arithmetic/fuzzer' \ + | grep -q -e '^bin/node/src/runtime' -e '^frame/' -e '^primitives/sr-' then - cat <<-EOT - + boldcat <<-EOT + no changes to the runtime source code detected EOT @@ -61,16 +60,16 @@ sub_spec_version="$(git diff origin/master...${CI_COMMIT_SHA} ${VERSIONS_FILE} \ | sed -n -r "s/^\-[[:space:]]+spec_version: +([0-9]+),$/\1/p")" -# see if the version and the binary blob changed + if [ "${add_spec_version}" != "${sub_spec_version}" ] then github_label "B2-breaksapi" - cat <<-EOT - + boldcat <<-EOT + changes to the runtime sources and changes in the spec version. - + spec_version: ${sub_spec_version} -> ${add_spec_version} EOT @@ -89,8 +88,8 @@ else # see if the impl version changed if [ "${add_impl_version}" != "${sub_impl_version}" ] then - cat <<-EOT - + boldcat <<-EOT + changes to the runtime sources and changes in the impl version. impl_version: ${sub_impl_version} -> ${add_impl_version} @@ -100,11 +99,10 @@ else fi - cat <<-EOT + boldcat <<-EOT - wasm source files changed but not the spec/impl version and the runtime - binary blob. If changes made do not alter logic, just bump 'impl_version'. - If they do change logic, bump 'spec_version' and rebuild wasm. + wasm source files changed but not the spec/impl version. If changes made do not alter logic, + just bump 'impl_version'. If they do change logic, bump 'spec_version'. source file directories: - bin/node/src/runtime @@ -114,8 +112,6 @@ else versions file: ${VERSIONS_FILE} EOT - - # drop through into pushing `gotissues` and exit 1... fi # dropped through. there's something wrong; exit 1. diff --git a/.maintain/node-template-release/Cargo.toml b/.maintain/node-template-release/Cargo.toml index 8a43435b20d83be0f69007eedb6888acbc2c3282..606def19bb99f1002058d1d868181cbc9c0fbb56 100644 --- a/.maintain/node-template-release/Cargo.toml +++ b/.maintain/node-template-release/Cargo.toml @@ -3,6 +3,7 @@ name = "node-template-release" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] toml = "0.4" diff --git a/.maintain/node-template-release/src/main.rs b/.maintain/node-template-release/src/main.rs index db42b155e65e729356694bf31d2838ae01313f28..a1d85bf33fe33b663ef92ad95cf6a812e12b3297 100644 --- a/.maintain/node-template-release/src/main.rs +++ b/.maintain/node-template-release/src/main.rs @@ -1,7 +1,7 @@ use structopt::StructOpt; use std::{ - path::{PathBuf, Path}, collections::HashMap, fs::{File, self}, io::{Read, Write}, + path::{PathBuf, Path}, collections::HashMap, fs::{File, OpenOptions, self}, io::{Read, Write}, process::Command }; @@ -88,7 +88,7 @@ fn replace_path_dependencies_with_git(cargo_toml_path: &Path, commit_id: &str, c // remove `Cargo.toml` cargo_toml_path.pop(); - for &table in &["dependencies", "build-dependencies"] { + for &table in &["dependencies", "build-dependencies", "dev-dependencies"] { let mut dependencies: toml::value::Table = match cargo_toml .remove(table) .and_then(|v| v.try_into().ok()) { @@ -212,11 +212,21 @@ fn main() { let node_template_path = build_dir.path().join(node_template_folder); copy_node_template(&options.node_template, build_dir.path()); - let cargo_tomls = find_cargo_tomls(build_dir.path().to_owned()); + let mut cargo_tomls = find_cargo_tomls(build_dir.path().to_owned()); let commit_id = get_git_commit_id(&options.node_template); let top_level_cargo_toml_path = node_template_path.join("Cargo.toml"); + // Check if top level Cargo.toml exists. If not, create one in the destination + if !cargo_tomls.contains(&top_level_cargo_toml_path) { + // create the top_level_cargo_toml + OpenOptions::new().create(true).write(true).open(top_level_cargo_toml_path.clone()) + .expect("Create root level `Cargo.toml` failed."); + + // push into our data structure + cargo_tomls.push(PathBuf::from(top_level_cargo_toml_path.clone())); + } + cargo_tomls.iter().for_each(|t| { let mut cargo_toml = parse_cargo_toml(&t); replace_path_dependencies_with_git(&t, &commit_id, &mut cargo_toml); diff --git a/.maintain/rename-crates-for-2.0.sh b/.maintain/rename-crates-for-2.0.sh old mode 100644 new mode 100755 index b024cfd4418c6672539e96c260212aba9a2985a7..5d873d26cdfca69db389ae569b6be534d142af1d --- a/.maintain/rename-crates-for-2.0.sh +++ b/.maintain/rename-crates-for-2.0.sh @@ -44,6 +44,8 @@ TO_RENAME=( "sp-sesssion sp-session" "sp-tracing-pool sp-transaction-pool" "sc-basic-authority sc-basic-authorship" + "sc-api sc-client-api" + "sc-database sc-client-db" # PRIMITIVES "substrate-application-crypto sp-application-crypto" @@ -87,7 +89,7 @@ TO_RENAME=( # # CLIENT "substrate-client sc-client" - "substrate-client-api sc-api" + "substrate-client-api sc-client-api" "substrate-authority-discovery sc-authority-discovery" "substrate-basic-authorship sc-basic-authorship" "substrate-block-builder sc-block-builder" @@ -99,7 +101,7 @@ TO_RENAME=( "substrate-consensus-pow sc-consensus-pow" "substrate-consensus-slots sc-consensus-slots" "substrate-consensus-uncles sc-consensus-uncles" - "substrate-client-db sc-database" + "substrate-client-db sc-client-db" "substrate-executor sc-executor" "substrate-runtime-test sc-runtime-test" "substrate-finality-grandpa sc-finality-grandpa" diff --git a/Cargo.lock b/Cargo.lock index 2e5d60b616f59c79d4e282a0f621e70d100bb937..8249ccd698403dd82afa61c3f03cecb675567c22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,532 +4,599 @@ name = "Inflector" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "regex", ] [[package]] name = "adler32" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] name = "aes-ctr" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "ctr", + "stream-cipher", ] [[package]] name = "aes-soft" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "byteorder 1.3.4", + "opaque-debug", ] [[package]] name = "aesni" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "opaque-debug", + "stream-cipher", ] [[package]] name = "ahash" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" dependencies = [ - "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random", ] [[package]] name = "aho-corasick" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "anyhow" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" [[package]] name = "app_dirs" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" dependencies = [ - "ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ole32-sys", + "shell32-sys", + "winapi 0.2.8", + "xdg", ] [[package]] name = "arc-swap" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" [[package]] name = "arrayref" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", ] [[package]] name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "asn1_der" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fce6b6a0ffdafebd82c87e79e3f40e8d2c523e5fea5566ff6b90509bf98d638" dependencies = [ - "asn1_der_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "asn1_der_derive", ] [[package]] name = "asn1_der_derive" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d0864d84b8e07b145449be9a8537db86bf9de5ce03b913214694643b4743502" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2", + "syn", ] [[package]] -name = "assert_matches" -version = "1.3.0" +name = "assert_cmd" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6283bac8dd7226470d491bc4737816fea4ca1fba7a2847f2e9097fd6bfb4624c" +dependencies = [ + "doc-comment", + "escargot", + "predicates", + "predicates-core", + "predicates-tree", +] [[package]] -name = "async-macros" -version = "2.0.0" +name = "assert_matches" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" [[package]] name = "async-std" -version = "1.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "async-macros 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-task 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "broadcaster 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +dependencies = [ + "async-task", + "broadcaster", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "futures-core", + "futures-io", + "futures-timer 2.0.2", + "kv-log-macro", + "log 0.4.8", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "once_cell 1.3.1", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] name = "async-task" -version = "1.0.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.8", ] [[package]] name = "async-tls" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce6977f57fa68da77ffe5542950d47e9c23d65f5bc7cb0a9f8700996913eec7" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "rustls", + "webpki", + "webpki-roots 0.17.0", ] [[package]] name = "atty" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi 0.3.8", ] [[package]] name = "autocfg" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.40" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f80256bc78f67e7df7e36d77366f636ed976895d91fe2ab9efa3973e8fe8c4f" dependencies = [ - "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", ] [[package]] name = "backtrace-sys" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", ] [[package]] name = "base58" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" [[package]] name = "base64" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "safemem", ] [[package]] name = "base64" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", ] [[package]] name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "bincode" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "serde", ] [[package]] name = "bindgen" version = "0.49.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c07087f3d5731bf3fb375a81841b99597e25dc11bd3bc72d16d43adf6624a6e" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cexpr", + "cfg-if", + "clang-sys", + "clap", + "env_logger 0.6.2", + "fxhash", + "lazy_static", + "log 0.4.8", + "peeking_take_while", + "proc-macro2 0.4.30", + "quote 0.6.13", + "regex", + "shlex", + "which 2.0.1", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitmask" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" [[package]] name = "bitvec" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" [[package]] name = "blake2" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", + "crypto-mac", + "digest", + "opaque-debug", ] [[package]] name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12", + "constant_time_eq", ] [[package]] name = "blake2b_simd" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "arrayvec 0.5.1", + "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder 1.3.4", + "generic-array", ] [[package]] name = "block-cipher-trait" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", ] [[package]] name = "broadcaster" -version = "0.2.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c972e21e0d055a36cf73e4daae870941fe7a8abcd5ac3396aab9e4c126bd87" dependencies = [ - "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-sink", + "futures-util", + "parking_lot 0.10.0", + "slab", ] [[package]] name = "browser-utils" version = "0.8.0" dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-web 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-chain-spec 2.0.0", - "sc-network 0.8.0", - "sc-service 0.8.0", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", + "clear_on_drop", + "console_error_panic_hook", + "console_log", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "js-sys", + "kvdb-web", + "libp2p", + "log 0.4.8", + "rand 0.6.5", + "rand 0.7.3", + "sc-chain-spec", + "sc-informant", + "sc-network", + "sc-service", + "wasm-bindgen", + "wasm-bindgen-futures", ] -[[package]] -name = "bs58" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "bs58" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b170cd256a3f9fa6b9edae3e44a7dfdfc77e8124dbc3e2612d75f9c3e2396dae" [[package]] name = "bstr" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "memchr", + "regex-automata", + "serde", ] [[package]] name = "build-helper" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" dependencies = [ - "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.6.0", ] [[package]] name = "bumpalo" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" [[package]] name = "byte-slice-cast" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "either", + "iovec", ] [[package]] name = "bytes" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" [[package]] name = "c2-chacha" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", ] [[package]] name = "c_linked_list" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" [[package]] name = "cargo_metadata" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "cast" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "cc" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" dependencies = [ - "jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver", ] [[package]] name = "cexpr" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chain-spec-builder" version = "2.0.0" dependencies = [ - "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "node-cli 2.0.0", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-keystore 2.0.0", - "sp-core 2.0.0", - "structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.12.1", + "node-cli", + "rand 0.7.3", + "sc-keystore", + "sp-core", + "structopt", ] [[package]] name = "chrono" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" dependencies = [ "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -540,3913 +607,4315 @@ dependencies = [ name = "clang-sys" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0", + "libc", + "libloading", ] [[package]] name = "clap" version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] name = "clear_on_drop" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "cmake" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "console_error_panic_hook" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "wasm-bindgen", ] [[package]] name = "console_log" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7871d2947441b0fdd8e2bd1ce2a2f75304f896582c0d572162d48290683c48" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", + "web-sys", ] [[package]] name = "const-random" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" dependencies = [ - "const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random-macro", + "proc-macro-hack", ] [[package]] name = "const-random-macro" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "proc-macro-hack", ] [[package]] name = "constant_time_eq" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys", + "libc", ] [[package]] name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "cranelift-bforest" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0f53d59dc9ab1c8ab68c991d8406b52b7a0aab0b15b05a3a6895579c4e5dd9" dependencies = [ - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0381a794836fb994c47006465d46d46be072483b667f36013d993b9895117fee" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-bforest 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen-meta 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen-shared 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli 0.20.0", + "log 0.4.8", + "serde", + "smallvec 1.2.0", + "target-lexicon", + "thiserror", ] [[package]] name = "cranelift-codegen-meta" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208c3c8d82bfef32a534c5020c6cfc3bc92f41388f1246b7bb98cf543331abaa" dependencies = [ - "cranelift-codegen-shared 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-shared", + "cranelift-entity", ] [[package]] name = "cranelift-codegen-shared" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea048c456a517e56fd6df8f0e3947922897e6e6f61fbc5eb557a36c7b8ff6394" [[package]] name = "cranelift-entity" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8c7ed50812194c9e9de1fa39c77b39fc9ab48173d5e7ee88b25b6a8953e9b8" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "cranelift-frontend" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ceb931d9f919731df1b1ecdc716b5c66384b413a7f95909d1f45441ab9bef5" dependencies = [ - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen", + "log 0.4.8", + "smallvec 1.2.0", + "target-lexicon", ] [[package]] name = "cranelift-native" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564ee82268bc25b914fcf331edfc2452f2d9ca34f976b187b4ca668beba250c8" dependencies = [ - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen", + "raw-cpuid", + "target-lexicon", ] [[package]] name = "cranelift-wasm" -version = "0.50.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de63e2271b374be5b07f359184e2126a08fb24d24a740cbc178b7e0107ddafa5" dependencies = [ - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-frontend 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "log 0.4.8", + "serde", + "thiserror", + "wasmparser 0.48.2", ] [[package]] name = "crc32fast" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "criterion" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot 0.3.1", + "csv", + "itertools", + "lazy_static", + "libc", + "num-traits", + "rand_core 0.3.1", + "rand_os", + "rand_xoshiro", + "rayon", + "rayon-core", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", ] [[package]] name = "criterion" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "cast", + "clap", + "criterion-plot 0.4.1", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", ] [[package]] name = "criterion-plot" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "cast", + "itertools", ] [[package]] name = "criterion-plot" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" dependencies = [ - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cast", + "itertools", ] [[package]] name = "crossbeam-channel" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" dependencies = [ - "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard 1.0.0", ] [[package]] name = "crossbeam-queue" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "cfg-if", + "lazy_static", ] [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", + "subtle 1.0.0", ] [[package]] name = "csv" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" dependencies = [ - "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] name = "csv-core" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ct-logs" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" dependencies = [ - "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sct", ] [[package]] name = "ctor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2", + "syn", ] [[package]] name = "ctr" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ctrlc" -version = "3.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "stream-cipher", ] [[package]] name = "cuckoofilter" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f" dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 0.5.3", + "rand 0.3.23", ] [[package]] name = "curve25519-dalek" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "clear_on_drop", + "digest", + "rand_core 0.3.1", + "subtle 2.2.2", ] [[package]] name = "curve25519-dalek" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "digest", + "rand_core 0.5.1", + "subtle 2.2.2", + "zeroize 1.1.0", ] [[package]] name = "data-encoding" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" [[package]] name = "derive_more" version = "0.99.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "difference" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "directories" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "redox_users", + "winapi 0.3.8", ] [[package]] name = "dns-parser" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "quick-error", ] [[package]] name = "doc-comment" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" [[package]] name = "ed25519-dalek" version = "1.0.0-pre.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop", + "curve25519-dalek 2.0.0", + "rand 0.7.3", + "sha2", ] [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] name = "enumflags2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33121c8782ba948ba332dab29311b026a8716dc65a1599e5b88f392d38496af8" dependencies = [ - "enumflags2_derive 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "enumflags2_derive", ] [[package]] name = "enumflags2_derive" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf634c5213044b8d54a46dd282cf5dd1f86bb5cb53e92c409cb4680a7fb9894" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "env_logger" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log 0.4.8", + "regex", + "termcolor", ] [[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log 0.4.8", + "regex", + "termcolor", ] [[package]] name = "environmental" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" [[package]] name = "erased-serde" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7d80305c9bd8cd78e3c753eb9fb110f83621e5211f1a3afffcc812b104daf9" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "errno" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" dependencies = [ - "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "errno-dragonfly", + "libc", + "winapi 0.3.8", ] [[package]] name = "errno-dragonfly" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", +] + +[[package]] +name = "escargot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74cf96bec282dcdb07099f7e31d9fed323bca9435a09aba7b6d99b7617bca96d" +dependencies = [ + "lazy_static", + "log 0.4.8", + "serde", + "serde_json", ] [[package]] name = "evm" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "272f65e18a2b6449b682bfcdf6c3ccc63db0b93898d89c0fb237548bbfc764a5" dependencies = [ - "evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "evm-gasometer 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-core", + "evm-gasometer", + "evm-runtime", + "primitive-types", + "rlp", + "serde", + "sha3", ] [[package]] name = "evm-core" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66534d42e13d50f9101bed87cb568fd5aa929c600c3c13f8dadbbf39f5635945" dependencies = [ - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", ] [[package]] name = "evm-gasometer" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39bc5b592803ca644781fe2290b7305ea5182f7c9516d615ddfb2308c2cab639" dependencies = [ - "evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-core", + "evm-runtime", + "primitive-types", ] [[package]] name = "evm-runtime" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389e4b447fb26971a9c76c8aa49c0ab435f8e46e8fc46e1bc4ebf01f3c2b428f" dependencies = [ - "evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-core", + "primitive-types", + "sha3", ] [[package]] name = "exit-future" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", ] [[package]] name = "faerie" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b9ed6159e4a6212c61d9c6a86bee01876b192a64accecf58d5b5ae3b667b52" dependencies = [ - "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "goblin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "string-interner 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "goblin", + "indexmap", + "log 0.4.8", + "scroll", + "string-interner", + "target-lexicon", + "thiserror", ] [[package]] name = "failure" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" dependencies = [ - "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "failure_derive", ] [[package]] name = "failure_derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "synstructure", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fdlimit" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9084c55bb76efb1496328976db88320fe7d9ee86e649e83c4ecce3ba7a9a35d1" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "file-per-thread-logger" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2", + "log 0.4.8", ] [[package]] name = "finality-grandpa" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbb25bef9fcad97fb31e817da280b1c9174435b8769c770ee190a330dd181ea" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "futures-timer 2.0.2", + "log 0.4.8", + "num-traits", + "parity-scale-codec", + "parking_lot 0.9.0", + "rand 0.6.5", ] [[package]] name = "fixed-hash" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "libc", + "rand 0.7.3", + "rustc-hex", + "static_assertions", ] [[package]] name = "fixedbitset" -version = "0.1.9" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "flate2" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "crc32fast", + "libc", + "libz-sys", + "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "fork-tree" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", ] [[package]] name = "frame-executive" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0", - "pallet-indices 2.0.0", - "pallet-transaction-payment 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "pallet-indices", + "pallet-transaction-payment", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "frame-metadata" -version = "10.0.0" +version = "11.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "serde", + "sp-core", + "sp-std", ] [[package]] name = "frame-support" version = "2.0.0" dependencies = [ - "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-metadata 10.0.0", - "frame-support-procedural 2.0.0", - "frame-system 2.0.0", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 2.0.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitmask", + "frame-metadata", + "frame-support-procedural", + "frame-system", + "impl-trait-for-tuples", + "log 0.4.8", + "once_cell 0.2.4", + "parity-scale-codec", + "paste", + "pretty_assertions", + "serde", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "tracing", ] [[package]] name = "frame-support-procedural" version = "2.0.0" dependencies = [ - "frame-support-procedural-tools 2.0.0", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support-procedural-tools", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "frame-support-procedural-tools" version = "2.0.0" dependencies = [ - "frame-support-procedural-tools-derive 2.0.0", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "frame-support-procedural-tools-derive" version = "2.0.0" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "frame-support-test" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "trybuild 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support", + "parity-scale-codec", + "pretty_assertions", + "serde", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "trybuild", ] [[package]] name = "frame-system" version = "2.0.0" dependencies = [ - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", + "criterion 0.2.11", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-core", + "sp-externalities", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "substrate-test-runtime-client", ] [[package]] name = "frame-system-rpc-runtime-api" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", + "parity-scale-codec", + "sp-api", ] [[package]] name = "fs-swap" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "libloading", + "winapi 0.3.8", ] [[package]] name = "fs2" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.8", ] +[[package]] +name = "fs_extra" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" + [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "futures" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" dependencies = [ - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] name = "futures-channel" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", ] [[package]] name = "futures-channel-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" dependencies = [ - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview", ] [[package]] name = "futures-core" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" [[package]] name = "futures-core-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" [[package]] name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "num_cpus", +] + +[[package]] +name = "futures-diagnose" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdcef58a173af8148b182684c9f2d5250875adbcaff7b5794073894f9d8634a9" +dependencies = [ + "futures 0.1.29", + "futures 0.3.4", + "lazy_static", + "log 0.4.8", + "parking_lot 0.9.0", + "pin-project", + "serde", + "serde_json", ] [[package]] name = "futures-executor" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-task", + "futures-util", + "num_cpus", ] [[package]] name = "futures-io" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" [[package]] name = "futures-macro" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-sink-preview" -version = "0.3.0-alpha.19" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" [[package]] name = "futures-task" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" [[package]] name = "futures-timer" -version = "0.4.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" [[package]] name = "futures-timer" -version = "2.0.2" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de1a2b2a2a33d9e60e17980b60ee061eeaae96a5abe9121db0fdb9af167a1c5" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] [[package]] name = "futures-util" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", + "tokio-io", ] [[package]] name = "futures-util-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" dependencies = [ - "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview", + "futures-core-preview", + "pin-utils", + "slab", ] [[package]] name = "futures_codec" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a73299e4718f5452e45980fc1d6957a070abe308d3700b63b8673f47e1c2b3" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures 0.3.4", + "memchr", + "pin-project", ] [[package]] name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", ] [[package]] name = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "get_if_addrs" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7" dependencies = [ - "c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "c_linked_list", + "get_if_addrs-sys", + "libc", + "winapi 0.2.8", ] [[package]] name = "get_if_addrs-sys" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", ] [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", + "wasm-bindgen", ] [[package]] name = "gimli" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162d18ae5f2e3b90a993d202f1ba17a5633c2484426f8bcae201f86194bacd00" +dependencies = [ + "arrayvec 0.4.12", + "byteorder 1.3.4", + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "indexmap", ] [[package]] name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "bstr", + "fnv", + "log 0.4.8", + "regex", +] + +[[package]] +name = "gloo-timers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2d17dbd803c2fc86cb1b613adf63192046a7176f383a8302594654752c4c4a" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "goblin" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081214398d39e4bd7f2c1975f0488ed04614ffdd976c6fc7a0708278552c0da" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", + "plain", + "scroll", ] [[package]] name = "h2" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "bytes 0.4.12", + "fnv", + "futures 0.1.29", + "http 0.1.21", + "indexmap", + "log 0.4.8", + "slab", + "string", + "tokio-io", ] [[package]] name = "h2" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.0", + "indexmap", + "log 0.4.8", + "slab", + "tokio 0.2.11", + "tokio-util", ] [[package]] name = "hash-db" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" [[package]] name = "hash256-std-hasher" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "hashbrown" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "scopeguard 0.3.3", ] [[package]] name = "hashbrown" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" [[package]] name = "hashbrown" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ - "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "autocfg 0.1.7", ] [[package]] name = "heck" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ - "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "hex" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cdda6bf525062a0c9e8f14ee2b37935c86b8efb6c8b69b3c83dfb518a914af" [[package]] name = "hex-literal" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" dependencies = [ - "hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal-impl", + "proc-macro-hack", ] [[package]] name = "hex-literal-impl" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", ] [[package]] name = "hmac" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "digest", ] [[package]] name = "hmac-drbg" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" dependencies = [ - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest", + "generic-array", + "hmac", ] [[package]] name = "http" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "fnv", + "itoa", ] [[package]] name = "http" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "fnv", + "itoa", ] [[package]] name = "http-body" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "http 0.1.21", + "tokio-buf", ] [[package]] name = "http-body" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "http 0.2.0", ] [[package]] name = "httparse" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error", ] [[package]] name = "hyper" version = "0.10.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.3", + "httparse", + "language-tags", + "log 0.3.9", + "mime", + "num_cpus", + "time", + "traitobject", + "typeable", + "unicase 1.4.2", + "url 1.7.2", ] [[package]] name = "hyper" version = "0.12.35" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "futures-cpupool", + "h2 0.1.26", + "http 0.1.21", + "http-body 0.1.0", + "httparse", + "iovec", + "itoa", + "log 0.4.8", + "net2", + "rustc_version", + "time", + "tokio 0.1.22", + "tokio-buf", + "tokio-executor 0.1.10", + "tokio-io", + "tokio-reactor", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "want 0.2.0", ] [[package]] name = "hyper" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa1c527bbc634be72aa7ba31e4e4def9bbb020f5416916279b7c705cd838893e" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.2.1", + "http 0.2.0", + "http-body 0.3.1", + "httparse", + "itoa", + "log 0.4.8", + "pin-project", + "time", + "tokio 0.2.11", + "tower-service", + "want 0.3.0", ] [[package]] name = "hyper-rustls" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719d85c7df4a7f309a77d145340a063ea929dcb2e025bae46a80345cffec2952" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "ct-logs", + "futures 0.1.29", + "hyper 0.12.35", + "rustls", + "tokio-io", + "tokio-rustls", + "webpki", + "webpki-roots 0.17.0", ] [[package]] name = "hyper-tls" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "hyper 0.12.35", + "native-tls", + "tokio-io", ] [[package]] name = "idna" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "impl-codec" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", ] [[package]] name = "impl-rlp" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" dependencies = [ - "rlp 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp", ] [[package]] name = "impl-serde" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", +] + +[[package]] +name = "impl-serde" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bbe9ea9b182f0fb1cabbd61f4ff9b7b7b9197955e95a7e4c27de5055eb29ff8" +dependencies = [ + "serde", ] [[package]] name = "impl-trait-for-tuples" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "indexmap" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", ] [[package]] name = "integer-sqrt" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" [[package]] name = "interleaved-ordered" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "ipnet" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a859057dc563d1388c1e816f98a1892629075fc046ed06e845b883bb8b2916fb" [[package]] name = "itertools" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "jobserver" -version = "0.1.17" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "js-sys" -version = "0.3.34" +version = "0.3.35" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" dependencies = [ - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen", ] [[package]] name = "jsonrpc-client-transports" version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a9ae166c4d1f702d297cd76d4b55758ace80272ffc6dbb139fdc1bf810de40b" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "websocket 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure", + "futures 0.1.29", + "hyper 0.12.35", + "hyper-tls", + "jsonrpc-core", + "jsonrpc-pubsub", + "log 0.4.8", + "serde", + "serde_json", + "tokio 0.1.22", + "url 1.7.2", + "websocket", ] [[package]] name = "jsonrpc-core" version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe3b688648f1ef5d5072229e2d672ecb92cbff7d1c79bcf3fd5898f3f3df0970" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "log 0.4.8", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "jsonrpc-core-client" version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080dc110be17701097df238fad3c816d4a478a1899dfbcf8ec8957dd40ec7304" dependencies = [ - "jsonrpc-client-transports 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-transports", ] [[package]] name = "jsonrpc-derive" version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8609af8f63b626e8e211f52441fcdb6ec54f1a446606b10d5c89ae9bf8a20058" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "jsonrpc-http-server" -version = "14.0.5" +version = "14.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816d63997ea45d3634608edbef83ddb35e661f7c0b27b5b72f237e321f0e9807" dependencies = [ - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35", + "jsonrpc-core", + "jsonrpc-server-utils", + "log 0.4.8", + "net2", + "parking_lot 0.10.0", + "unicase 2.6.0", ] [[package]] name = "jsonrpc-pubsub" -version = "14.0.5" +version = "14.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b31c9b90731276fdd24d896f31bb10aecf2e5151733364ae81123186643d939" dependencies = [ - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "log 0.4.8", + "parking_lot 0.10.0", + "serde", ] [[package]] name = "jsonrpc-server-utils" version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b7635e618a0edbbe0d2a2bbbc69874277c49383fcf6c3c0414491cfb517d22" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "globset", + "jsonrpc-core", + "lazy_static", + "log 0.4.8", + "tokio 0.1.22", + "tokio-codec", + "unicase 2.6.0", ] [[package]] name = "jsonrpc-ws-server" -version = "14.0.5" +version = "14.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94e5773b2ae66e0e02c80775ce6bbba6f15d5bb47c14ec36a36fcf94f8df851" dependencies = [ - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log 0.4.8", + "parking_lot 0.10.0", + "slab", + "ws", ] [[package]] name = "keccak" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "keccak-hasher" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3468207deea1359a0e921591ae9b4c928733d94eb9d6a2eeda994cfd59f42cf8" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hash256-std-hasher", + "tiny-keccak 1.5.0", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "kv-log-macro" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", ] [[package]] name = "kvdb" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03080afe6f42cd996da9f568d6add5d7fb5ee2ea7fb7802d2d2cbd836958fd87" dependencies = [ - "parity-bytes 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes", + "parity-util-mem", + "smallvec 1.2.0", ] [[package]] name = "kvdb-memorydb" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9355274e5a9e0a7e8ef43916950eae3949024de2a8dffe4d5a6c13974a37c8e" dependencies = [ - "kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "parity-util-mem", + "parking_lot 0.10.0", ] [[package]] name = "kvdb-rocksdb" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af36fd66ccd99f3f771ae39b75aaba28b952372b6debfb971134bf1f03466ab2" dependencies = [ - "fs-swap 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rocksdb 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fs-swap", + "interleaved-ordered", + "kvdb", + "log 0.4.8", + "num_cpus", + "owning_ref", + "parity-util-mem", + "parking_lot 0.10.0", + "regex", + "rocksdb", + "smallvec 1.2.0", ] [[package]] name = "kvdb-web" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a985c47b4c46429e96033ebf6eaed784a81ceccb4e5df13d63f3b9078a4df81" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "js-sys", + "kvdb", + "kvdb-memorydb", + "log 0.4.8", + "parity-util-mem", + "send_wrapper 0.3.0", + "wasm-bindgen", + "web-sys", ] [[package]] name = "language-tags" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" + +[[package]] +name = "leb128" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" [[package]] name = "libloading" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "winapi 0.3.8", ] [[package]] name = "libp2p" -version = "0.14.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core-derive 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-deflate 0.6.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-dns 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-floodsub 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-kad 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mdns 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mplex 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-noise 0.12.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-plaintext 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-secio 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-tcp 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-uds 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-wasm-ext 0.7.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-websocket 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-yamux 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84847789ab24b3fc5971a68656ac85886df640986d9ce3264c0327694eae471" +dependencies = [ + "bytes 0.5.4", + "futures 0.3.4", + "lazy_static", + "libp2p-core", + "libp2p-core-derive", + "libp2p-deflate", + "libp2p-dns", + "libp2p-floodsub", + "libp2p-gossipsub", + "libp2p-identify", + "libp2p-kad", + "libp2p-mdns", + "libp2p-mplex", + "libp2p-noise", + "libp2p-ping", + "libp2p-plaintext", + "libp2p-secio", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-uds", + "libp2p-wasm-ext", + "libp2p-websocket", + "libp2p-yamux", + "parity-multiaddr", + "parity-multihash", + "parking_lot 0.10.0", + "pin-project", + "smallvec 1.2.0", + "wasm-timer", ] [[package]] name = "libp2p-core" -version = "0.14.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "asn1_der 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "multistream-select 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbafb2706b8082233f66dc13e196f9cf9b4c229f2cd7c96b2b16617ad6ee330b" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "fnv", + "futures 0.3.4", + "futures-timer 2.0.2", + "lazy_static", + "libsecp256k1", + "log 0.4.8", + "multistream-select", + "parity-multiaddr", + "parity-multihash", + "parking_lot 0.10.0", + "pin-project", + "prost", + "prost-build", + "rand 0.7.3", + "ring", + "rw-stream-sink", + "sha2", + "smallvec 1.2.0", + "thiserror", + "unsigned-varint", + "untrusted", + "void", + "zeroize 1.1.0", ] [[package]] name = "libp2p-core-derive" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c320266be0a7760e23484d635acdb83844b2d74d3612d93b41c393c9bcf004e" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2", + "syn", ] [[package]] name = "libp2p-deflate" -version = "0.6.0-alpha.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be32697b42d040b325c3737f827ea04ede569ec956b7807700dd8d89d8210f9" dependencies = [ - "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2", + "futures 0.3.4", + "libp2p-core", ] [[package]] name = "libp2p-dns" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c979b882f25d85726b15637d5bbc722dfa1be576605c54e99b8cf56906be3" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "libp2p-core", + "log 0.4.8", ] [[package]] name = "libp2p-floodsub" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bdf6fba9272ad47dde94bade89540fdb16e24ae9ff7fb714c1c80a035165f28" dependencies = [ - "bs58 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bs58", + "cuckoofilter", + "fnv", + "futures 0.3.4", + "libp2p-core", + "libp2p-swarm", + "prost", + "prost-build", + "rand 0.7.3", + "smallvec 1.2.0", ] [[package]] -name = "libp2p-identify" -version = "0.14.0-alpha.1" +name = "libp2p-gossipsub" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6ecd058bf769d27ebec530544b081e08b0a1088e3186da8cc58d59915784d0" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0", + "bs58", + "byteorder 1.3.4", + "bytes 0.5.4", + "fnv", + "futures 0.3.4", + "futures_codec", + "libp2p-core", + "libp2p-swarm", + "log 0.4.8", + "lru 0.4.3", + "prost", + "prost-build", + "rand 0.7.3", + "sha2", + "smallvec 1.2.0", + "unsigned-varint", + "wasm-timer", ] [[package]] -name = "libp2p-kad" -version = "0.14.0-alpha.1" +name = "libp2p-identify" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1a6261b804111c2dbf53f8ca03f66edc5ad1c29b78a61cf0cc5354052e28e9" dependencies = [ - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "libp2p-core", + "libp2p-swarm", + "log 0.4.8", + "prost", + "prost-build", + "smallvec 1.2.0", + "wasm-timer", +] + +[[package]] +name = "libp2p-kad" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6ea6fece0d99599afb1b2082ca8937944cdd6b0946a88d54cb3ae7a38d1253" +dependencies = [ + "arrayvec 0.5.1", + "bytes 0.5.4", + "either", + "fnv", + "futures 0.3.4", + "futures_codec", + "libp2p-core", + "libp2p-swarm", + "log 0.4.8", + "parity-multihash", + "prost", + "prost-build", + "rand 0.7.3", + "sha2", + "smallvec 1.2.0", + "uint", + "unsigned-varint", + "void", + "wasm-timer", ] [[package]] name = "libp2p-mdns" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074312353355df310affa105ec71b16fd7e52f5c6ae61d3dcbb3e79e8fdc9e5f" dependencies = [ - "async-std 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", + "data-encoding", + "dns-parser", + "either", + "futures 0.3.4", + "lazy_static", + "libp2p-core", + "libp2p-swarm", + "log 0.4.8", + "net2", + "rand 0.7.3", + "smallvec 1.2.0", + "void", + "wasm-timer", ] [[package]] name = "libp2p-mplex" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d0b44dfdef80cc2be4b42d127de1c793905eca2459415a5c57d6b4fbd8ec30" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "fnv", + "futures 0.3.4", + "futures_codec", + "libp2p-core", + "log 0.4.8", + "parking_lot 0.10.0", + "unsigned-varint", ] [[package]] name = "libp2p-noise" -version = "0.12.0-alpha.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845e8208d814cd41c26c90a6a2f2b720c31b588209cecc49a44c881a09f417f" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "snow 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.2.3", + "futures 0.3.4", + "lazy_static", + "libp2p-core", + "log 0.4.8", + "prost", + "prost-build", + "rand 0.7.3", + "ring", + "snow", + "x25519-dalek", + "zeroize 1.1.0", ] [[package]] name = "libp2p-ping" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ecced2949ae93b6ff29565303ecd1bef15c4e4efb689033ee744922561a36b" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "libp2p-core", + "libp2p-swarm", + "log 0.4.8", + "rand 0.7.3", + "void", + "wasm-timer", ] [[package]] name = "libp2p-plaintext" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195fda6b6a948a242fd30570e0e3418ae8e0a20055ea75d45458e1079a8efb05" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures 0.3.4", + "futures_codec", + "libp2p-core", + "log 0.4.8", + "prost", + "prost-build", + "rw-stream-sink", + "unsigned-varint", + "void", ] [[package]] name = "libp2p-secio" -version = "0.14.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quicksink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceef68ca377b264f84d64c88739a8fa118b5db1e8f18297351dff75314504a5f" +dependencies = [ + "aes-ctr", + "ctr", + "futures 0.3.4", + "hmac", + "js-sys", + "lazy_static", + "libp2p-core", + "log 0.4.8", + "parity-send-wrapper", + "pin-project", + "prost", + "prost-build", + "quicksink", + "rand 0.7.3", + "ring", + "rw-stream-sink", + "sha2", + "static_assertions", + "twofish", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] name = "libp2p-swarm" -version = "0.4.0-alpha.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ea00be81bc3985e36abad263ce2ad1b6aeb862aa743563eb70ad42880c05ae" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "libp2p-core", + "log 0.4.8", + "smallvec 1.2.0", + "void", + "wasm-timer", ] [[package]] name = "libp2p-tcp" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e65ef381570df31cb047dfbc11483ab0fe7e6abbdcf2bdc2c60b5d11133d241" dependencies = [ - "async-std 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "get_if_addrs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnet 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", + "futures 0.3.4", + "futures-timer 2.0.2", + "get_if_addrs", + "ipnet", + "libp2p-core", + "log 0.4.8", ] [[package]] name = "libp2p-uds" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4f4f7989b35f33d4b9738aab2f031310eb20fec513cab44d12b1bc985a8074" dependencies = [ - "async-std 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", + "futures 0.3.4", + "libp2p-core", + "log 0.4.8", ] [[package]] name = "libp2p-wasm-ext" -version = "0.7.0-alpha.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b4d457adb91a5e2212343218a554394cd8ced64a79fb8e36e7aed2a16d49495" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "js-sys", + "libp2p-core", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] name = "libp2p-websocket" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bba425f2af1fdb7dece88b9ae05ca9430dfb0b72b2c078e73ded6f1556084509" dependencies = [ - "async-tls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quicksink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "soketto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-tls", + "bytes 0.5.4", + "either", + "futures 0.3.4", + "libp2p-core", + "log 0.4.8", + "quicksink", + "rustls", + "rw-stream-sink", + "soketto", + "url 2.1.1", + "webpki", + "webpki-roots 0.18.0", ] [[package]] name = "libp2p-yamux" -version = "0.14.0-alpha.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca25b3aac78a3c93c2a567622abd3cfc16f96f26ae1bf6134f0056203d62d86" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "yamux 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "libp2p-core", + "log 0.4.8", + "parking_lot 0.10.0", + "thiserror", + "yamux", ] [[package]] name = "librocksdb-sys" version = "6.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0785e816e1e11e7599388a492c61ef80ddc2afc91e313e61662cce537809be" dependencies = [ - "bindgen 0.49.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", + "cc", + "glob 0.3.0", + "libc", ] [[package]] name = "libsecp256k1" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "crunchy", + "digest", + "hmac-drbg", + "rand 0.7.3", + "sha2", + "subtle 2.2.2", + "typenum", ] [[package]] name = "libz-sys" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "pkg-config", + "vcpkg", ] [[package]] name = "linked-hash-map" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" [[package]] name = "linked_hash_set" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7c91c4c7bbeb4f2f7c4e5be11e6a05bd6830bc37249c47ce1ad86ad453ff9c" dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map", ] [[package]] name = "lock_api" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" dependencies = [ - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3", ] [[package]] name = "lock_api" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0", ] [[package]] name = "log" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", ] [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "lru" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8f669d42c72d18514dfca8115689c5f6370a17d980cb5bd777a67f404594c8" dependencies = [ - "hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.5.0", ] [[package]] name = "lru" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609345ddee5badacf857d4f547e0e5a2e987db77085c24cd887f73573a04237" dependencies = [ - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.3", ] [[package]] name = "mach" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "malloc_size_of_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "memoffset" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "memory-db" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198831fe8722331a395bc199a5d08efbc197497ef354cb4c77b969c02ffc0fc4" dependencies = [ - "ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "hash-db", + "hashbrown 0.6.3", + "parity-util-mem", ] [[package]] name = "memory_units" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" [[package]] name = "merlin" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "keccak", + "rand_core 0.4.2", + "zeroize 1.1.0", ] [[package]] name = "mime" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" dependencies = [ - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9", ] [[package]] name = "miniz_oxide" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" dependencies = [ - "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32", ] [[package]] name = "mio" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log 0.4.8", + "miow", + "net2", + "slab", + "winapi 0.2.8", ] [[package]] name = "mio-extras" version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ - "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell", + "log 0.4.8", + "mio", + "slab", ] [[package]] name = "mio-uds" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" dependencies = [ - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec", + "libc", + "mio", ] [[package]] name = "miow" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] name = "more-asserts" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" [[package]] name = "multimap" -version = "0.4.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97fbd5d00e0e37bfb10f433af8f5aaf631e739368dc9fc28286ca81ca4948dc" [[package]] name = "multistream-select" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f938ffe420493e77c8b6cbcc3f282283f68fc889c5dcbc8e51668d5f3a01ad94" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures 0.1.29", + "log 0.4.8", + "smallvec 1.2.0", + "tokio-io", + "unsigned-varint", ] [[package]] name = "names" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef320dab323286b50fb5cdda23f61c796a72a89998ab565ca32525c5c556f2da" dependencies = [ - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.23", ] [[package]] name = "native-tls" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "log 0.4.8", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "winapi 0.3.8", ] [[package]] name = "nix" -version = "0.14.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cc", + "cfg-if", + "libc", + "void", ] [[package]] name = "node-cli" version = "2.0.0" dependencies = [ - "browser-utils 0.8.0", - "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "frame-system 2.0.0", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "node-executor 2.0.0", - "node-primitives 2.0.0", - "node-rpc 2.0.0", - "node-runtime 2.0.0", - "node-transaction-factory 0.8.0", - "pallet-authority-discovery 2.0.0", - "pallet-balances 2.0.0", - "pallet-contracts 2.0.0", - "pallet-im-online 2.0.0", - "pallet-indices 2.0.0", - "pallet-timestamp 2.0.0", - "pallet-transaction-payment 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-authority-discovery 0.8.0", - "sc-basic-authorship 0.8.0", - "sc-chain-spec 2.0.0", - "sc-cli 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-client-db 0.8.0", - "sc-consensus-babe 0.8.0", - "sc-finality-grandpa 0.8.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-offchain 2.0.0", - "sc-rpc 2.0.0", - "sc-service 0.8.0", - "sc-service-test 2.0.0", - "sc-telemetry 2.0.0", - "sc-transaction-pool 2.0.0", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-authority-discovery 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-finality-grandpa 2.0.0", - "sp-finality-tracker 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-timestamp 2.0.0", - "sp-transaction-pool 2.0.0", - "structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-build-script-utils 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_cmd", + "browser-utils", + "frame-support", + "frame-system", + "futures 0.3.4", + "hex-literal", + "jsonrpc-core", + "log 0.4.8", + "nix", + "node-executor", + "node-primitives", + "node-rpc", + "node-runtime", + "node-transaction-factory", + "pallet-authority-discovery", + "pallet-balances", + "pallet-contracts", + "pallet-im-online", + "pallet-indices", + "pallet-timestamp", + "pallet-transaction-payment", + "parity-scale-codec", + "rand 0.7.3", + "sc-authority-discovery", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client", + "sc-client-api", + "sc-client-db", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-finality-grandpa", + "sc-keystore", + "sc-network", + "sc-offchain", + "sc-rpc", + "sc-service", + "sc-service-test", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "serde", + "sp-authority-discovery", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-finality-tracker", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "sp-transaction-pool", + "structopt", + "substrate-build-script-utils", + "tempfile", + "tracing", + "vergen", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] name = "node-executor" version = "2.0.0" dependencies = [ - "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "frame-system 2.0.0", - "node-primitives 2.0.0", - "node-runtime 2.0.0", - "node-testing 2.0.0", - "pallet-balances 2.0.0", - "pallet-contracts 2.0.0", - "pallet-grandpa 2.0.0", - "pallet-im-online 2.0.0", - "pallet-indices 2.0.0", - "pallet-session 2.0.0", - "pallet-timestamp 2.0.0", - "pallet-transaction-payment 2.0.0", - "pallet-treasury 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-executor 0.8.0", - "sp-application-crypto 2.0.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-trie 2.0.0", - "substrate-test-client 2.0.0", - "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.3.1", + "frame-support", + "frame-system", + "node-primitives", + "node-runtime", + "node-testing", + "pallet-balances", + "pallet-contracts", + "pallet-grandpa", + "pallet-im-online", + "pallet-indices", + "pallet-session", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "parity-scale-codec", + "sc-executor", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-trie", + "substrate-test-client", + "trie-root", + "wabt", ] [[package]] name = "node-primitives" version = "2.0.0" dependencies = [ - "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-serializer 2.0.0", + "pretty_assertions", + "sp-core", + "sp-runtime", + "sp-serializer", ] [[package]] name = "node-rpc" version = "2.0.0" dependencies = [ - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "node-primitives 2.0.0", - "node-runtime 2.0.0", - "pallet-contracts-rpc 0.8.0", - "pallet-transaction-payment-rpc 2.0.0", - "sc-client 0.8.0", - "sp-api 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-frame-rpc-system 2.0.0", + "jsonrpc-core", + "node-primitives", + "node-runtime", + "pallet-contracts-rpc", + "pallet-transaction-payment-rpc", + "sc-client", + "sp-api", + "sp-runtime", + "sp-transaction-pool", + "substrate-frame-rpc-system", ] [[package]] name = "node-rpc-client" version = "2.0.0" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "node-primitives 2.0.0", - "sc-rpc 2.0.0", + "env_logger 0.7.1", + "futures 0.1.29", + "hyper 0.12.35", + "jsonrpc-core-client", + "log 0.4.8", + "node-primitives", + "sc-rpc", ] [[package]] name = "node-runtime" version = "2.0.0" dependencies = [ - "frame-executive 2.0.0", - "frame-support 2.0.0", - "frame-system 2.0.0", - "frame-system-rpc-runtime-api 2.0.0", - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "node-primitives 2.0.0", - "pallet-authority-discovery 2.0.0", - "pallet-authorship 2.0.0", - "pallet-babe 2.0.0", - "pallet-balances 2.0.0", - "pallet-collective 2.0.0", - "pallet-contracts 2.0.0", - "pallet-contracts-rpc-runtime-api 0.8.0", - "pallet-democracy 2.0.0", - "pallet-elections-phragmen 2.0.0", - "pallet-finality-tracker 2.0.0", - "pallet-grandpa 2.0.0", - "pallet-identity 2.0.0", - "pallet-im-online 2.0.0", - "pallet-indices 2.0.0", - "pallet-membership 2.0.0", - "pallet-offences 2.0.0", - "pallet-randomness-collective-flip 2.0.0", - "pallet-recovery 2.0.0", - "pallet-session 2.0.0", - "pallet-society 2.0.0", - "pallet-staking 2.0.0", - "pallet-staking-reward-curve 2.0.0", - "pallet-sudo 2.0.0", - "pallet-timestamp 2.0.0", - "pallet-transaction-payment 2.0.0", - "pallet-transaction-payment-rpc-runtime-api 2.0.0", - "pallet-treasury 2.0.0", - "pallet-utility 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-authority-discovery 2.0.0", - "sp-block-builder 2.0.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-offchain 2.0.0", - "sp-runtime 2.0.0", - "sp-session 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", - "sp-transaction-pool 2.0.0", - "sp-version 2.0.0", - "substrate-wasm-builder-runner 1.0.4", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "integer-sqrt", + "node-primitives", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-collective", + "pallet-contracts", + "pallet-contracts-primitives", + "pallet-contracts-rpc-runtime-api", + "pallet-democracy", + "pallet-elections-phragmen", + "pallet-finality-tracker", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-offences", + "pallet-randomness-collective-flip", + "pallet-recovery", + "pallet-session", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "parity-scale-codec", + "rustc-hex", + "serde", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder-runner", ] [[package]] name = "node-template" version = "2.0.0" dependencies = [ - "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "node-template-runtime 2.0.0", - "sc-basic-authorship 0.8.0", - "sc-cli 0.8.0", - "sc-client 0.8.0", - "sc-consensus-aura 0.8.0", - "sc-executor 0.8.0", - "sc-finality-grandpa 0.8.0", - "sc-network 0.8.0", - "sc-service 0.8.0", - "sc-transaction-pool 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-aura 0.8.0", - "sp-core 2.0.0", - "sp-finality-grandpa 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-build-script-utils 2.0.0", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "log 0.4.8", + "node-template-runtime", + "sc-basic-authorship", + "sc-cli", + "sc-client", + "sc-consensus-aura", + "sc-executor", + "sc-finality-grandpa", + "sc-network", + "sc-service", + "sc-transaction-pool", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-finality-grandpa", + "sp-inherents", + "sp-runtime", + "sp-transaction-pool", + "structopt", + "substrate-build-script-utils", + "vergen", ] [[package]] name = "node-template-runtime" version = "2.0.0" dependencies = [ - "frame-executive 2.0.0", - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-aura 2.0.0", - "pallet-balances 2.0.0", - "pallet-grandpa 2.0.0", - "pallet-indices 2.0.0", - "pallet-randomness-collective-flip 2.0.0", - "pallet-sudo 2.0.0", - "pallet-timestamp 2.0.0", - "pallet-transaction-payment 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-block-builder 2.0.0", - "sp-consensus-aura 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-offchain 2.0.0", - "sp-runtime 2.0.0", - "sp-session 2.0.0", - "sp-std 2.0.0", - "sp-transaction-pool 2.0.0", - "sp-version 2.0.0", - "substrate-wasm-builder-runner 1.0.4", + "frame-executive", + "frame-support", + "frame-system", + "pallet-aura", + "pallet-balances", + "pallet-grandpa", + "pallet-indices", + "pallet-randomness-collective-flip", + "pallet-sudo", + "pallet-template", + "pallet-timestamp", + "pallet-transaction-payment", + "parity-scale-codec", + "serde", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder-runner", ] [[package]] name = "node-testing" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "node-executor 2.0.0", - "node-primitives 2.0.0", - "node-runtime 2.0.0", - "pallet-balances 2.0.0", - "pallet-contracts 2.0.0", - "pallet-grandpa 2.0.0", - "pallet-indices 2.0.0", - "pallet-session 2.0.0", - "pallet-society 2.0.0", - "pallet-staking 2.0.0", - "pallet-timestamp 2.0.0", - "pallet-transaction-payment 2.0.0", - "pallet-treasury 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-executor 0.8.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "substrate-test-client 2.0.0", - "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.3.1", + "frame-support", + "frame-system", + "fs_extra", + "hex-literal", + "log 0.4.8", + "node-executor", + "node-primitives", + "node-runtime", + "pallet-balances", + "pallet-contracts", + "pallet-grandpa", + "pallet-indices", + "pallet-session", + "pallet-society", + "pallet-staking", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "parity-scale-codec", + "sc-cli", + "sc-client", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sc-service", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-finality-tracker", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "substrate-test-client", + "tempdir", + "wabt", ] [[package]] name = "node-transaction-factory" version = "0.8.0" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-cli 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-service 0.8.0", - "sp-api 2.0.0", - "sp-block-builder 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", + "log 0.4.8", + "parity-scale-codec", + "sc-cli", + "sc-client", + "sc-client-api", + "sc-service", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", ] [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "nohash-hasher" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721a2bf1c26159ebf17e0a980bc4ce61f4b2fec5ec3b42d42fddd7a84a9e538f" [[package]] name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", + "version_check 0.1.5", ] [[package]] name = "num-bigint" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-integer", + "num-traits", ] [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-traits", ] [[package]] name = "num-rational" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "num-bigint", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", ] [[package]] name = "num_cpus" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" dependencies = [ - "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "ole32-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "once_cell" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1", ] [[package]] name = "once_cell" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d584f08c2d717d5c23a6414fc2822b71c651560713e54fa7eace675f758a355e" [[package]] name = "once_cell" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + +[[package]] +name = "oorandom" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.26" +version = "0.10.28" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", ] [[package]] name = "openssl-probe" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.53" +version = "0.9.54" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0", + "cc", + "libc", + "pkg-config", + "vcpkg", ] [[package]] name = "output_vt100" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "owning_ref" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait", ] [[package]] name = "pallet-assets" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-aura" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-session 2.0.0", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-consensus-aura 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "sp-timestamp 2.0.0", + "frame-support", + "frame-system", + "lazy_static", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "parking_lot 0.10.0", + "serde", + "sp-application-crypto", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] name = "pallet-authority-discovery" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-session 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-authority-discovery 2.0.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-authority-discovery", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] name = "pallet-authorship" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-authorship 2.0.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-authorship", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-babe" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-session 2.0.0", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", - "sp-timestamp 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime 2.0.0", + "frame-support", + "frame-system", + "hex-literal", + "lazy_static", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "parking_lot 0.10.0", + "serde", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-timestamp", + "sp-version", + "substrate-test-runtime", ] [[package]] name = "pallet-balances" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-transaction-payment 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-collective" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-contracts" version = "2.0.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0", - "pallet-randomness-collective-flip 2.0.0", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-sandbox 0.8.0", - "sp-std 2.0.0", - "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "pallet-contracts-primitives", + "pallet-randomness-collective-flip", + "pallet-timestamp", + "parity-scale-codec", + "parity-wasm 0.41.0", + "pwasm-utils", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-sandbox", + "sp-std", + "wabt", + "wasmi-validation", +] + +[[package]] +name = "pallet-contracts-primitives" +version = "2.0.0" +dependencies = [ + "parity-scale-codec", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-contracts-rpc" version = "0.8.0" dependencies = [ - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-contracts-rpc-runtime-api 0.8.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-rpc 2.0.0", - "sp-runtime 2.0.0", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "pallet-contracts-primitives", + "pallet-contracts-rpc-runtime-api", + "parity-scale-codec", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", ] [[package]] name = "pallet-contracts-rpc-runtime-api" version = "0.8.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "pallet-contracts-primitives", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-democracy" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-storage", ] [[package]] name = "pallet-elections" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-elections-phragmen" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-phragmen 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "substrate-test-utils 2.0.0", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-phragmen", + "sp-runtime", + "sp-std", + "substrate-test-utils", ] [[package]] name = "pallet-evm" version = "2.0.0" dependencies = [ - "evm 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "evm", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-timestamp", + "parity-scale-codec", + "primitive-types", + "rlp", + "serde", + "sha3", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-example" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-finality-tracker" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-finality-tracker 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-core", + "sp-finality-tracker", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-generic-asset" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-grandpa" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-finality-tracker 2.0.0", - "pallet-session 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-finality-grandpa 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-finality-tracker", + "pallet-session", + "parity-scale-codec", + "serde", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] name = "pallet-identity" version = "2.0.0" dependencies = [ - "enumflags2 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "enumflags2", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-im-online" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-authorship 2.0.0", - "pallet-session 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] name = "pallet-indices" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-membership" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-nicks" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-offences" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] name = "pallet-randomness-collective-flip" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "parity-scale-codec", + "safe-mix", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-recovery" version = "2.0.0" dependencies = [ - "enumflags2 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "enumflags2", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-scored-pool" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-session" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", - "sp-trie 2.0.0", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "lazy_static", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-trie", ] [[package]] name = "pallet-society" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "rand_chacha 0.2.1", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-staking" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-authorship 2.0.0", - "pallet-balances 2.0.0", - "pallet-session 2.0.0", - "pallet-staking-reward-curve 2.0.0", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-phragmen 2.0.0", - "sp-runtime 2.0.0", - "sp-staking 2.0.0", - "sp-std 2.0.0", - "substrate-test-utils 2.0.0", + "frame-support", + "frame-system", + "pallet-authorship", + "pallet-balances", + "pallet-session", + "pallet-staking-reward-curve", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-keyring", + "sp-phragmen", + "sp-runtime", + "sp-staking", + "sp-std", + "substrate-test-utils", ] [[package]] name = "pallet-staking-reward-curve" version = "2.0.0" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "sp-runtime", + "syn", ] [[package]] name = "pallet-sudo" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-template" +version = "2.0.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "safe-mix", + "sp-core", + "sp-io", + "sp-runtime", ] [[package]] name = "pallet-timestamp" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "sp-timestamp 2.0.0", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-core", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] name = "pallet-transaction-payment" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "pallet-transaction-payment-rpc-runtime-api 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-transaction-payment-rpc" version = "2.0.0" dependencies = [ - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-transaction-payment-rpc-runtime-api 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-rpc 2.0.0", - "sp-runtime 2.0.0", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", ] [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "parity-scale-codec", + "serde", + "serde_json", + "sp-api", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-treasury" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] name = "pallet-utility" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "pallet-balances 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "parity-bytes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-multiaddr" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "pallet-vesting" +version = "2.0.0" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "enumflags2", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-storage", ] [[package]] -name = "parity-multiaddr" -version = "0.7.0" +name = "parity-bytes" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "0c276d76c5333b8c2579e02d49a06733a55b8282d2d9b13e8d53b6406bd7e30a" [[package]] -name = "parity-multihash" -version = "0.1.3" +name = "parity-multiaddr" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80878c27f90dd162d3143333d672e80b194d6b080f05c83440e3dfda42e409f2" dependencies = [ - "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "bs58", + "byteorder 1.3.4", + "data-encoding", + "parity-multihash", + "percent-encoding 2.1.0", + "serde", + "static_assertions", + "unsigned-varint", + "url 2.1.1", ] [[package]] name = "parity-multihash" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f42bbd3a021c5061b77154bd3334d5a57e1a03eb162de0b962681cc25800d" dependencies = [ - "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2", + "bytes 0.5.4", + "rand 0.7.3", + "sha-1", + "sha2", + "sha3", + "unsigned-varint", ] [[package]] name = "parity-scale-codec" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" dependencies = [ - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1", + "bitvec", + "byte-slice-cast", + "parity-scale-codec-derive", + "serde", ] [[package]] name = "parity-scale-codec-derive" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "parity-send-wrapper" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" [[package]] name = "parity-util-mem" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-util-mem" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1476e40bf8f5c6776e9600983435821ca86eb9819d74a6207cca69d091406a" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot 0.10.0", + "primitive-types", + "smallvec 1.2.0", + "winapi 0.3.8", ] [[package]] name = "parity-util-mem-derive" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "syn", + "synstructure", ] [[package]] name = "parity-wasm" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ad52817c4d343339b3bc2e26861bd21478eda0b7509acf83505727000512ac" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", ] [[package]] name = "parity-wasm" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" [[package]] name = "parking_lot" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.1.5", + "parking_lot_core 0.4.0", ] [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.3", + "parking_lot_core 0.6.2", + "rustc_version", ] [[package]] name = "parking_lot" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" dependencies = [ - "lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.3", + "parking_lot_core 0.7.0", ] [[package]] name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand 0.6.5", + "rustc_version", + "smallvec 0.6.13", + "winapi 0.3.8", ] [[package]] name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "rustc_version", + "smallvec 0.6.13", + "winapi 0.3.8", ] [[package]] name = "parking_lot_core" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec 1.2.0", + "winapi 0.3.8", ] [[package]] name = "paste" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" dependencies = [ - "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl", + "proc-macro-hack", ] [[package]] name = "paste-impl" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "pbkdf2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "crypto-mac", ] [[package]] name = "pdqselect" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "petgraph" -version = "0.4.13" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92" dependencies = [ - "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "fixedbitset", + "indexmap", ] [[package]] name = "pin-project" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" dependencies = [ - "pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" [[package]] name = "pin-utils" version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" [[package]] name = "pkg-config" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "plotters" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +dependencies = [ + "js-sys", + "num-traits", + "wasm-bindgen", + "web-sys", +] [[package]] name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + +[[package]] +name = "predicates" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9bfe52247e5cc9b2f943682a85a5549fb9662245caf094504e69a2f03fe64d4" +dependencies = [ + "difference", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" + +[[package]] +name = "predicates-tree" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +dependencies = [ + "predicates-core", + "treeline", +] [[package]] name = "pretty_assertions" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0", + "ctor", + "difference", + "output_vt100", ] [[package]] name = "primitive-types" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" dependencies = [ - "fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde 0.3.0", + "uint", ] [[package]] name = "proc-macro-crate" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" dependencies = [ - "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro-error" -version = "0.4.4" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875077759af22fa20b610ad4471d8155b321c89c3f2785526c9839b099be4e0a" dependencies = [ - "proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr", + "proc-macro2 1.0.8", + "quote 1.0.2", + "rustversion", + "syn", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.3" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5717d9fa2664351a01ed73ba5ef6df09c01a521cb42cb65a061432a826f3c7a" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "rustversion", + "syn", + "syn-mid", ] [[package]] name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "proc-macro-nested" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" [[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0", ] [[package]] @@ -4487,623 +4956,642 @@ dependencies = [ name = "prost" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "prost-derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0", ] [[package]] name = "prost" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "prost-derive 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "prost-derive", ] [[package]] name = "prost-build" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "multimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "prost-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "prost-derive" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "heck", + "itertools", + "log 0.4.8", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which 3.1.0", ] [[package]] name = "prost-derive" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" dependencies = [ - "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "itertools", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "prost-types" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "prost", ] -[[package]] -name = "protobuf" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "pwasm-utils" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "log 0.4.8", + "parity-wasm 0.41.0", ] [[package]] name = "quick-error" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quickcheck" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.1", + "log 0.4.8", + "rand 0.7.3", + "rand_core 0.5.1", ] [[package]] name = "quicksink" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8461ef7445f61fd72d8dcd0629ce724b9131b3c2eb36e83a5d3d4161c127530" dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", + "pin-project-lite", ] [[package]] name = "quote" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", ] [[package]] name = "rand" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand 0.4.6", ] [[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.8", ] [[package]] name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi 0.3.8", ] [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha 0.2.1", + "rand_core 0.5.1", + "rand_hc 0.2.0", ] [[package]] name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.3.1", ] [[package]] name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha", + "rand_core 0.5.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand_core 0.4.2", + "winapi 0.3.8", ] [[package]] name = "rand_os" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "wasm-bindgen", + "winapi 0.3.8", ] [[package]] name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7", + "rand_core 0.4.2", ] [[package]] name = "rand_xorshift" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_xoshiro" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xoshiro" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "rand_core 0.3.1", ] [[package]] name = "raw-cpuid" version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cc", + "rustc_version", ] [[package]] name = "rayon" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" dependencies = [ - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque", + "either", + "rayon-core", ] [[package]] name = "rayon-core" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" dependencies = [ - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque", + "crossbeam-queue", + "crossbeam-utils", + "lazy_static", + "num_cpus", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "redox_users" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "redox_syscall", + "rust-argon2", ] [[package]] name = "regex" -version = "1.3.1" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-automata" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", ] [[package]] name = "regex-syntax" -version = "0.6.12" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" [[package]] name = "region" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "448e868c6e4cfddfa49b6a72c95906c04e8547465e9536575b95c70a4044f856" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "libc", + "mach", + "winapi 0.3.8", ] [[package]] name = "remove_dir_all" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "ring" -version = "0.16.9" +version = "0.16.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741ba1704ae21999c00942f9f5944f801e977f54302af346b596287599ad1862" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "lazy_static", + "libc", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.8", ] [[package]] name = "rlp" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a44d5ae8afcb238af8b75640907edc6c931efcfab2c854e81ed35fa080f84cd" dependencies = [ - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex", ] [[package]] name = "rocksdb" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12069b106981c6103d3eab7dd1c86751482d0779a520b7c14954c8b586c1e643" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "librocksdb-sys 6.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "librocksdb-sys", ] [[package]] name = "rpassword" -version = "4.0.3" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.8", ] [[package]] name = "rust-argon2" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", ] [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustc-hex" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0", ] [[package]] name = "rustls" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1", + "log 0.4.8", + "ring", + "sct", + "webpki", ] [[package]] name = "rustversion" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "rw-stream-sink" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "pin-project", + "static_assertions", ] [[package]] name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" [[package]] name = "safe-mix" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d3d055a2582e6b00ed7a31c1524040aa391092bf636328350813f3a0605215c" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "safemem" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "same-file" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "sc-authority-discovery" version = "0.8.0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "prost-build 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-peerset 2.0.0", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-authority-discovery 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "substrate-test-runtime-client 2.0.0", + "bytes 0.4.12", + "derive_more", + "env_logger 0.7.1", + "futures 0.3.4", + "futures-timer 3.0.1", + "libp2p", + "log 0.4.8", + "parity-scale-codec", + "prost", + "prost-build", + "quickcheck", + "rand 0.7.3", + "sc-client-api", + "sc-keystore", + "sc-network", + "sc-peerset", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-runtime", + "substrate-test-runtime-client", ] [[package]] name = "sc-basic-authorship" version = "0.8.0" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-block-builder 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-telemetry 2.0.0", - "sc-transaction-pool 2.0.0", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-block-builder", + "sc-client", + "sc-client-api", + "sc-telemetry", + "sc-transaction-pool", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", + "tokio-executor 0.2.0-alpha.6", ] [[package]] name = "sc-block-builder" version = "0.8.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sp-api 2.0.0", - "sp-block-builder 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", ] [[package]] name = "sc-chain-spec" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-chain-spec-derive 2.0.0", - "sc-network 0.8.0", - "sc-telemetry 2.0.0", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-runtime 2.0.0", + "impl-trait-for-tuples", + "sc-chain-spec-derive", + "sc-network", + "sc-telemetry", + "serde", + "serde_json", + "sp-core", + "sp-runtime", ] [[package]] name = "sc-chain-spec-derive" version = "2.0.0" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] @@ -5146,596 +5634,665 @@ dependencies = [ name = "sc-client" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-block-builder 0.8.0", - "sc-client-api 2.0.0", - "sc-executor 0.8.0", - "sc-telemetry 2.0.0", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-inherents 2.0.0", - "sp-keyring 2.0.0", - "sp-panic-handler 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-trie 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more", + "env_logger 0.7.1", + "fnv", + "futures 0.3.4", + "hash-db", + "hex-literal", + "kvdb", + "kvdb-memorydb", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-block-builder", + "sc-client-api", + "sc-executor", + "sc-telemetry", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-keyring", + "sp-panic-handler", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "substrate-test-runtime-client", + "tempfile", + "tracing", ] [[package]] name = "sc-client-api" version = "2.0.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-executor 0.8.0", - "sc-telemetry 2.0.0", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-inherents 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-test-primitives 2.0.0", - "sp-transaction-pool 2.0.0", - "sp-trie 2.0.0", - "sp-version 2.0.0", + "derive_more", + "fnv", + "futures 0.3.4", + "hash-db", + "hex-literal", + "kvdb", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-executor", + "sc-telemetry", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-keyring", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-test-primitives", + "sp-transaction-pool", + "sp-trie", + "sp-version", ] [[package]] name = "sc-client-db" version = "0.8.0" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-executor 0.8.0", - "sc-state-db 0.8.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-trie 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.1", + "hash-db", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map", + "log 0.4.8", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "quickcheck", + "rand 0.7.3", + "sc-client", + "sc-client-api", + "sc-executor", + "sc-state-db", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-keyring", + "sp-runtime", + "sp-state-machine", + "sp-trie", + "substrate-test-runtime-client", + "tempfile", ] [[package]] name = "sc-consensus-aura" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-consensus-slots 0.8.0", - "sc-executor 0.8.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-network-test 0.8.0", - "sc-service 0.8.0", - "sc-telemetry 2.0.0", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-block-builder 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-aura 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-timestamp 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more", + "env_logger 0.7.1", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-client", + "sc-client-api", + "sc-consensus-slots", + "sc-executor", + "sc-keystore", + "sc-network", + "sc-network-test", + "sc-service", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "sp-version", + "substrate-test-runtime-client", + "tempfile", + "tokio 0.1.22", ] [[package]] name = "sc-consensus-babe" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fork-tree 2.0.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-block-builder 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-consensus-slots 0.8.0", - "sc-consensus-uncles 0.8.0", - "sc-executor 0.8.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-network-test 0.8.0", - "sc-service 0.8.0", - "sc-telemetry 2.0.0", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-block-builder 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-timestamp 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more", + "env_logger 0.7.1", + "fork-tree", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "log 0.4.8", + "merlin", + "num-bigint", + "num-rational", + "num-traits", + "parity-scale-codec", + "parking_lot 0.10.0", + "pdqselect", + "rand 0.7.3", + "sc-block-builder", + "sc-client", + "sc-client-api", + "sc-consensus-epochs", + "sc-consensus-slots", + "sc-consensus-uncles", + "sc-executor", + "sc-keystore", + "sc-network", + "sc-network-test", + "sc-service", + "sc-telemetry", + "schnorrkel", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "sp-version", + "substrate-test-runtime-client", + "tempfile", + "tokio 0.1.22", +] + +[[package]] +name = "sc-consensus-epochs" +version = "0.8.0" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-client-api", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-consensus-manual-seal" +version = "0.8.0" +dependencies = [ + "derive_more", + "env_logger 0.7.1", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "log 0.4.8", + "parking_lot 0.10.0", + "sc-basic-authorship", + "sc-client", + "sc-client-api", + "sc-transaction-pool", + "serde", + "sp-blockchain", + "sp-consensus", + "sp-inherents", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", + "substrate-test-runtime-transaction-pool", + "tempfile", + "tokio 0.2.11", ] [[package]] name = "sc-consensus-pow" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sp-api 2.0.0", - "sp-block-builder 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-pow 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-timestamp 2.0.0", + "derive_more", + "futures 0.3.4", + "log 0.4.8", + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-pow", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-timestamp", ] [[package]] name = "sc-consensus-slots" version = "0.8.0" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sc-telemetry 2.0.0", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "substrate-test-runtime-client 2.0.0", + "futures 0.3.4", + "futures-timer 3.0.1", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-client-api", + "sc-telemetry", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "substrate-test-runtime-client", ] [[package]] name = "sc-consensus-uncles" version = "0.8.0" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sp-authorship 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", + "log 0.4.8", + "sc-client-api", + "sp-authorship", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", ] [[package]] name = "sc-executor" version = "0.8.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-executor-common 0.8.0", - "sc-executor-wasmi 0.8.0", - "sc-executor-wasmtime 0.8.0", - "sc-runtime-test 2.0.0", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-io 2.0.0", - "sp-panic-handler 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-serializer 2.0.0", - "sp-state-machine 0.8.0", - "sp-trie 2.0.0", - "sp-version 2.0.0", - "sp-wasm-interface 2.0.0", - "substrate-test-runtime 2.0.0", - "test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "derive_more", + "hex-literal", + "lazy_static", + "libsecp256k1", + "log 0.4.8", + "parity-scale-codec", + "parity-wasm 0.41.0", + "parking_lot 0.10.0", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sc-runtime-test", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-serializer", + "sp-state-machine", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "substrate-test-runtime", + "test-case", + "wabt", + "wasmi", ] [[package]] name = "sc-executor-common" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-serializer 2.0.0", - "sp-wasm-interface 2.0.0", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more", + "log 0.4.8", + "parity-scale-codec", + "sp-allocator", + "sp-core", + "sp-runtime-interface", + "sp-serializer", + "sp-wasm-interface", + "wasmi", ] [[package]] name = "sc-executor-wasmi" version = "0.8.0" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-executor-common 0.8.0", - "sp-core 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-wasm-interface 2.0.0", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", + "parity-scale-codec", + "parity-wasm 0.41.0", + "sc-executor-common", + "sp-allocator", + "sp-core", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", ] [[package]] name = "sc-executor-wasmtime" version = "0.8.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-frontend 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-native 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-executor-common 0.8.0", - "sp-core 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-wasm-interface 2.0.0", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-jit 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-runtime 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "log 0.4.8", + "parity-scale-codec", + "parity-wasm 0.41.0", + "sc-executor-common", + "sp-allocator", + "sp-core", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", + "wasmtime", ] [[package]] name = "sc-finality-grandpa" version = "0.8.0" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "finality-grandpa 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fork-tree 2.0.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-network-gossip 0.8.0", - "sc-network-test 0.8.0", - "sc-telemetry 2.0.0", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-arithmetic 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-finality-grandpa 2.0.0", - "sp-finality-tracker 2.0.0", - "sp-inherents 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "substrate-test-runtime-client 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "env_logger 0.7.1", + "finality-grandpa", + "fork-tree", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "pin-project", + "rand 0.7.3", + "sc-client", + "sc-client-api", + "sc-keystore", + "sc-network", + "sc-network-gossip", + "sc-network-test", + "sc-telemetry", + "serde_json", + "sp-api", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-finality-tracker", + "sp-inherents", + "sp-keyring", + "sp-runtime", + "sp-state-machine", + "substrate-test-runtime-client", + "tempfile", + "tokio 0.1.22", +] + +[[package]] +name = "sc-informant" +version = "0.8.0" +dependencies = [ + "ansi_term 0.12.1", + "futures 0.3.4", + "log 0.4.8", + "parity-util-mem", + "sc-client-api", + "sc-network", + "sc-service", + "sp-blockchain", + "sp-runtime", + "wasm-timer", ] [[package]] name = "sc-keystore" version = "2.0.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-core 2.0.0", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more", + "hex", + "parking_lot 0.10.0", + "rand 0.7.3", + "serde_json", + "sp-application-crypto", + "sp-core", + "subtle 2.2.2", + "tempfile", ] [[package]] name = "sc-network" version = "0.8.0" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fork-tree 2.0.0", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "linked_hash_set 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-block-builder 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-peerset 2.0.0", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "slog_derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-test-primitives 2.0.0", - "substrate-test-client 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "async-std", + "bitflags", + "bytes 0.5.4", + "derive_more", + "either", + "env_logger 0.7.1", + "erased-serde", + "fnv", + "fork-tree", + "futures 0.3.4", + "futures-timer 3.0.1", + "futures_codec", + "libp2p", + "linked-hash-map", + "linked_hash_set", + "log 0.4.8", + "lru 0.4.3", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.10.0", + "prost", + "prost-build", + "quickcheck", + "rand 0.7.3", + "rustc-hex", + "sc-block-builder", + "sc-client", + "sc-client-api", + "sc-peerset", + "serde", + "serde_json", + "slog", + "slog_derive", + "smallvec 0.6.13", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-keyring", + "sp-runtime", + "sp-test-primitives", + "substrate-test-client", + "substrate-test-runtime", + "substrate-test-runtime-client", + "tempfile", + "thiserror", + "unsigned-varint", + "void", + "wasm-timer", + "yamux", + "zeroize 1.1.0", ] [[package]] name = "sc-network-gossip" version = "0.8.0" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-network 0.8.0", - "sp-runtime 2.0.0", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "libp2p", + "log 0.4.8", + "lru 0.1.17", + "parking_lot 0.10.0", + "sc-network", + "sp-runtime", + "wasm-timer", ] [[package]] name = "sc-network-test" version = "0.8.0" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-block-builder 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-network 0.8.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "substrate-test-runtime 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.1", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "libp2p", + "log 0.4.8", + "parking_lot 0.10.0", + "rand 0.7.3", + "sc-block-builder", + "sc-client", + "sc-client-api", + "sc-network", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-runtime", + "substrate-test-runtime", + "substrate-test-runtime-client", + "tempfile", + "tokio 0.1.22", ] [[package]] name = "sc-offchain" version = "2.0.0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sc-client-db 0.8.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-transaction-pool 2.0.0", - "sp-api 2.0.0", - "sp-core 2.0.0", - "sp-offchain 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-test-runtime-client 2.0.0", - "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "env_logger 0.7.1", + "fnv", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.1", + "hyper 0.12.35", + "hyper-rustls", + "log 0.4.8", + "num_cpus", + "parity-scale-codec", + "parking_lot 0.10.0", + "rand 0.7.3", + "sc-client-api", + "sc-client-db", + "sc-keystore", + "sc-network", + "sc-transaction-pool", + "sp-api", + "sp-core", + "sp-offchain", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", + "threadpool", + "tokio 0.1.22", ] [[package]] name = "sc-peerset" version = "2.0.0" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "libp2p", + "log 0.4.8", + "rand 0.7.3", + "serde_json", + "wasm-timer", ] [[package]] name = "sc-rpc" version = "2.0.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-executor 0.8.0", - "sc-keystore 2.0.0", - "sc-network 0.8.0", - "sc-rpc-api 0.8.0", - "sc-transaction-pool 2.0.0", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-rpc 2.0.0", - "sp-runtime 2.0.0", - "sp-session 2.0.0", - "sp-state-machine 0.8.0", - "sp-transaction-pool 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "futures 0.1.29", + "futures 0.3.4", + "hash-db", + "jsonrpc-core", + "jsonrpc-pubsub", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "rustc-hex", + "sc-client", + "sc-client-api", + "sc-executor", + "sc-keystore", + "sc-network", + "sc-rpc-api", + "sc-transaction-pool", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-io", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-transaction-pool", + "sp-version", + "substrate-test-runtime-client", + "tokio 0.1.22", ] [[package]] name = "sc-rpc-api" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-rpc 2.0.0", - "sp-transaction-pool 2.0.0", - "sp-version 2.0.0", + "derive_more", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-pubsub", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "serde", + "serde_json", + "sp-core", + "sp-rpc", + "sp-transaction-pool", + "sp-version", ] [[package]] name = "sc-rpc-server" version = "2.0.0" dependencies = [ - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ws-server 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0", + "jsonrpc-core", + "jsonrpc-http-server", + "jsonrpc-pubsub", + "jsonrpc-ws-server", + "log 0.4.8", + "serde", + "serde_json", + "sp-runtime", ] [[package]] name = "sc-runtime-test" version = "2.0.0" dependencies = [ - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-sandbox 0.8.0", - "sp-std 2.0.0", - "substrate-wasm-builder-runner 1.0.4", + "sp-allocator", + "sp-core", + "sp-io", + "sp-runtime", + "sp-sandbox", + "sp-std", + "substrate-wasm-builder-runner", ] [[package]] @@ -5789,55 +6346,56 @@ dependencies = [ "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "sc-service-test" -version = "2.0.0" -dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-network 0.8.0", - "sc-service 0.8.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", +[[package]] +name = "sc-service-test" +version = "2.0.0" +dependencies = [ + "env_logger 0.7.1", + "fdlimit", + "futures 0.1.29", + "futures 0.3.4", + "log 0.4.8", + "sc-client", + "sc-network", + "sc-service", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-transaction-pool", + "tempfile", + "tokio 0.1.22", ] [[package]] name = "sc-state-db" version = "0.8.0" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", + "env_logger 0.7.1", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "sp-core", ] [[package]] name = "sc-telemetry" version = "2.0.0" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-scope 4.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures 0.3.4", + "futures-timer 3.0.1", + "libp2p", + "log 0.4.8", + "parking_lot 0.10.0", + "pin-project", + "rand 0.7.3", + "serde", + "slog", + "slog-json", + "slog-scope", + "take_mut", + "void", + "wasm-timer", ] [[package]] @@ -5859,798 +6417,871 @@ dependencies = [ name = "sc-transaction-graph" version = "2.0.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-test-runtime 2.0.0", + "assert_matches", + "criterion 0.3.1", + "derive_more", + "futures 0.3.4", + "log 0.4.8", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "serde", + "sp-core", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime", + "wasm-timer", ] [[package]] name = "sc-transaction-pool" version = "2.0.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sc-transaction-graph 2.0.0", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-test-runtime-client 2.0.0", + "derive_more", + "futures 0.3.4", + "futures-diagnose", + "log 0.4.8", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "sc-client-api", + "sc-transaction-graph", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", + "substrate-test-runtime-transaction-pool", + "wasm-timer", ] [[package]] name = "schannel" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "winapi 0.3.8", ] [[package]] name = "schnorrkel" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" dependencies = [ - "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.2.3", + "failure", + "merlin", + "rand 0.6.5", + "rand_core 0.4.2", + "rand_os", + "sha2", + "subtle 2.2.2", + "zeroize 0.9.3", ] [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" [[package]] name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" [[package]] name = "scroll" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" dependencies = [ - "scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll_derive", ] [[package]] name = "scroll_derive" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "sct" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" dependencies = [ - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "security-framework" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" dependencies = [ - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys", ] [[package]] name = "semver" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", + "serde", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "send_wrapper" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" [[package]] name = "send_wrapper" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686ef91cf020ad8d4aca9a7047641fd6add626b7b89e14546c2b6a76781cf822" + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" dependencies = [ - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "serde_json" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90" dependencies = [ - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha-1" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] name = "sha1" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] name = "sha3" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "byte-tools", + "digest", + "keccak", + "opaque-debug", ] [[package]] name = "shell32-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "shlex" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "slog" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" dependencies = [ - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "erased-serde", ] [[package]] name = "slog-json" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" dependencies = [ - "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", + "erased-serde", + "serde", + "serde_json", + "slog", ] [[package]] name = "slog-scope" version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c44c89dd8b0ae4537d1ae318353eaf7840b4869c536e31c41e963d1ea523ee6" dependencies = [ - "arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arc-swap", + "lazy_static", + "slog", ] [[package]] name = "slog_derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a945ec7f7ce853e89ffa36be1e27dce9a43e82ff9093bf3461c30d5da74ed11b" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "smallvec" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit", ] [[package]] name = "smallvec" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" [[package]] name = "snow" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb767eee7d257ba202f0b9b08673bc13b22281632ef45267b19f13100accd2f" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "rand_core 0.5.1", + "ring", + "rustc_version", + "subtle 2.2.2", ] [[package]] name = "soketto" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c9dab3f95c9ebdf3a88268c19af668f637a3c5039c2c56ff2d40b1b2d64a25b" dependencies = [ - "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0", + "bytes 0.5.4", + "flate2", + "futures 0.3.4", + "http 0.2.0", + "httparse", + "log 0.4.8", + "rand 0.7.3", + "sha1", + "smallvec 1.2.0", + "static_assertions", + "thiserror", ] [[package]] name = "sourcefile" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" + +[[package]] +name = "sp-allocator" +version = "2.0.0" +dependencies = [ + "derive_more", + "log 0.4.8", + "sp-core", + "sp-std", + "sp-wasm-interface", +] [[package]] name = "sp-api" version = "2.0.0" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api-proc-macro 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-test-primitives 2.0.0", - "sp-version 2.0.0", + "hash-db", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-test-primitives", + "sp-version", ] [[package]] name = "sp-api-proc-macro" version = "2.0.0" dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "sp-api-test" version = "2.0.0" dependencies = [ - "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", - "trybuild 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.3.1", + "parity-scale-codec", + "rustversion", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-runtime", + "sp-state-machine", + "sp-version", + "substrate-test-runtime-client", + "trybuild", ] [[package]] name = "sp-application-crypto" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-std", ] [[package]] name = "sp-application-crypto-test" version = "2.0.0" dependencies = [ - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "substrate-test-runtime-client 2.0.0", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-runtime", + "substrate-test-runtime-client", ] [[package]] name = "sp-arithmetic" version = "2.0.0" dependencies = [ - "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-std 2.0.0", + "criterion 0.3.1", + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "primitive-types", + "rand 0.7.3", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-authority-discovery" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-authorship" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-block-builder" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-blockchain" version = "2.0.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-block-builder 2.0.0", - "sp-consensus 0.8.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", + "derive_more", + "log 0.4.8", + "lru 0.4.3", + "parity-scale-codec", + "parking_lot 0.10.0", + "sp-block-builder", + "sp-consensus", + "sp-runtime", + "sp-state-machine", ] [[package]] name = "sp-consensus" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-test-primitives 2.0.0", - "sp-version 2.0.0", + "derive_more", + "futures 0.3.4", + "futures-diagnose", + "futures-timer 3.0.1", + "libp2p", + "log 0.4.8", + "parity-scale-codec", + "parking_lot 0.10.0", + "serde", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-test-primitives", + "sp-version", ] [[package]] name = "sp-consensus-aura" version = "0.8.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "sp-timestamp 2.0.0", + "parity-scale-codec", + "sp-api", + "sp-application-crypto", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] name = "sp-consensus-babe" version = "0.8.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-consensus 0.8.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "sp-timestamp 2.0.0", + "parity-scale-codec", + "schnorrkel", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] name = "sp-consensus-pow" version = "0.8.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-core" version = "2.0.0" dependencies = [ - "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-externalities 0.8.0", - "sp-runtime-interface 2.0.0", - "sp-serializer 2.0.0", - "sp-std 2.0.0", - "sp-storage 2.0.0", - "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base58", + "blake2-rfc", + "byteorder 1.3.4", + "criterion 0.2.11", + "ed25519-dalek", + "hash-db", + "hash256-std-hasher", + "hex", + "hex-literal", + "impl-serde 0.2.3", + "lazy_static", + "libsecp256k1", + "log 0.4.8", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "pretty_assertions", + "primitive-types", + "rand 0.7.3", + "regex", + "rustc-hex", + "schnorrkel", + "serde", + "serde_json", + "sha2", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-serializer", + "sp-std", + "sp-storage", + "substrate-bip39", + "tiny-bip39", + "tiny-keccak 2.0.1", + "twox-hash", + "wasmi", + "zeroize 1.1.0", ] [[package]] name = "sp-debug-derive" version = "2.0.0" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "sp-externalities" version = "0.8.0" dependencies = [ - "environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 2.0.0", - "sp-storage 2.0.0", + "environmental", + "sp-std", + "sp-storage", ] [[package]] name = "sp-finality-grandpa" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "serde", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-finality-tracker" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-inherents 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "sp-inherents", + "sp-std", ] [[package]] name = "sp-inherents" version = "2.0.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-std 2.0.0", + "derive_more", + "parity-scale-codec", + "parking_lot 0.10.0", + "sp-core", + "sp-std", ] [[package]] name = "sp-io" version = "2.0.0" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-runtime-interface 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-trie 2.0.0", + "hash-db", + "libsecp256k1", + "log 0.4.8", + "parity-scale-codec", + "sp-core", + "sp-externalities", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-wasm-interface", ] [[package]] name = "sp-keyring" version = "2.0.0" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "sp-core", + "sp-runtime", + "strum", ] [[package]] name = "sp-offchain" version = "2.0.0" dependencies = [ - "sp-api 2.0.0", - "sp-runtime 2.0.0", + "sp-api", + "sp-runtime", ] [[package]] name = "sp-panic-handler" version = "2.0.0" dependencies = [ - "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "log 0.4.8", ] [[package]] name = "sp-phragmen" version = "2.0.0" dependencies = [ - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", - "substrate-test-utils 2.0.0", + "rand 0.7.3", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "substrate-test-utils", ] [[package]] name = "sp-rpc" version = "2.0.0" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", + "serde", + "serde_json", + "sp-core", ] [[package]] name = "sp-runtime" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-arithmetic 2.0.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-std 2.0.0", + "impl-trait-for-tuples", + "log 0.4.8", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "serde", + "serde_json", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-std", ] [[package]] name = "sp-runtime-interface" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-io 2.0.0", - "sp-runtime-interface-proc-macro 2.0.0", - "sp-runtime-interface-test-wasm 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-wasm-interface 2.0.0", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trybuild 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", + "primitive-types", + "rustversion", + "sp-core", + "sp-externalities", + "sp-io", + "sp-runtime-interface-proc-macro", + "sp-runtime-interface-test-wasm", + "sp-state-machine", + "sp-std", + "sp-wasm-interface", + "static_assertions", + "trybuild", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "2.0.0" dependencies = [ - "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "Inflector", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "sp-runtime-interface-test" version = "2.0.0" dependencies = [ - "sc-executor 0.8.0", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-runtime-interface-test-wasm 2.0.0", - "sp-state-machine 0.8.0", + "sc-executor", + "sp-core", + "sp-io", + "sp-runtime-interface", + "sp-runtime-interface-test-wasm", + "sp-state-machine", ] [[package]] name = "sp-runtime-interface-test-wasm" version = "2.0.0" dependencies = [ - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-std 2.0.0", - "substrate-wasm-builder-runner 1.0.4", + "sp-core", + "sp-io", + "sp-runtime-interface", + "sp-std", + "substrate-wasm-builder-runner", ] [[package]] name = "sp-sandbox" version = "0.8.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-io 2.0.0", - "sp-std 2.0.0", - "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-std", + "sp-wasm-interface", + "wabt", + "wasmi", ] [[package]] name = "sp-serializer" version = "2.0.0" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_json", ] [[package]] name = "sp-session" version = "2.0.0" dependencies = [ - "sp-api 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-staking" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "parity-scale-codec", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-state-machine" version = "0.8.0" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-externalities 0.8.0", - "sp-panic-handler 2.0.0", - "sp-trie 2.0.0", - "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hex-literal", + "log 0.4.8", + "num-traits", + "parity-scale-codec", + "parking_lot 0.10.0", + "rand 0.7.3", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-trie", + "trie-db", + "trie-root", ] [[package]] @@ -6661,205 +7292,224 @@ version = "2.0.0" name = "sp-storage" version = "2.0.0" dependencies = [ - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-std 2.0.0", + "impl-serde 0.2.3", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-test-primitives" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-application-crypto 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", + "parity-scale-codec", + "parity-util-mem", + "serde", + "sp-application-crypto", + "sp-core", + "sp-runtime", ] [[package]] name = "sp-timestamp" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-inherents 2.0.0", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", + "wasm-timer", ] [[package]] name = "sp-transaction-pool" version = "2.0.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-runtime 2.0.0", + "derive_more", + "futures 0.3.4", + "log 0.4.8", + "parity-scale-codec", + "serde", + "sp-api", + "sp-runtime", ] [[package]] name = "sp-trie" version = "2.0.0" dependencies = [ - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-core 2.0.0", - "sp-std 2.0.0", - "trie-bench 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-standardmap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.2.11", + "hash-db", + "hex-literal", + "memory-db", + "parity-scale-codec", + "sp-core", + "sp-std", + "trie-bench", + "trie-db", + "trie-root", + "trie-standardmap", ] [[package]] name = "sp-version" version = "2.0.0" dependencies = [ - "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 2.0.0", - "sp-std 2.0.0", + "impl-serde 0.2.3", + "parity-scale-codec", + "serde", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-wasm-interface" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-std", + "wasmi", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stream-cipher" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "string" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", ] [[package]] name = "string-interner" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" dependencies = [ - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "lazy_static", + "structopt-derive", ] [[package]] name = "structopt-derive" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro-error", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "strum" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" dependencies = [ - "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros", ] [[package]] name = "strum_macros" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "subkey" version = "2.0.0" dependencies = [ - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-system 2.0.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "node-primitives 2.0.0", - "node-runtime 2.0.0", - "pallet-balances 2.0.0", - "pallet-transaction-payment 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-rpc 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "derive_more", + "frame-system", + "futures 0.1.29", + "hex", + "hex-literal", + "hyper 0.12.35", + "itertools", + "jsonrpc-core-client", + "libp2p", + "node-primitives", + "node-runtime", + "pallet-balances", + "pallet-transaction-payment", + "parity-scale-codec", + "rand 0.7.3", + "rpassword", + "rustc-hex", + "sc-rpc", + "serde_json", + "sp-core", + "sp-runtime", + "substrate-bip39", + "tiny-bip39", ] [[package]] name = "substrate-bip39" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" dependencies = [ - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac", + "pbkdf2", + "schnorrkel", + "sha2", ] [[package]] @@ -6870,115 +7520,130 @@ version = "2.0.0" name = "substrate-frame-rpc-support" version = "2.0.0" dependencies = [ - "frame-support 2.0.0", - "frame-system 2.0.0", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-client-transports 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-rpc-api 0.8.0", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-storage 2.0.0", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support", + "frame-system", + "futures 0.3.4", + "jsonrpc-client-transports", + "jsonrpc-core", + "parity-scale-codec", + "sc-rpc-api", + "serde", + "sp-storage", + "tokio 0.1.22", ] [[package]] -name = "substrate-frame-rpc-system" -version = "2.0.0" -dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-system-rpc-runtime-api 2.0.0", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-transaction-pool 2.0.0", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-test-runtime-client 2.0.0", +name = "substrate-frame-rpc-system" +version = "2.0.0" +dependencies = [ + "env_logger 0.7.1", + "frame-system-rpc-runtime-api", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "log 0.4.8", + "parity-scale-codec", + "sc-client", + "sc-transaction-pool", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", ] [[package]] name = "substrate-test-client" version = "2.0.0" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-client-db 0.8.0", - "sc-executor 0.8.0", - "sp-blockchain 2.0.0", - "sp-consensus 0.8.0", - "sp-core 2.0.0", - "sp-keyring 2.0.0", - "sp-runtime 2.0.0", - "sp-state-machine 0.8.0", + "futures 0.3.4", + "hash-db", + "parity-scale-codec", + "sc-client", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-keyring", + "sp-runtime", + "sp-state-machine", ] [[package]] name = "substrate-test-runtime" version = "2.0.0" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "frame-executive 2.0.0", - "frame-support 2.0.0", - "frame-system 2.0.0", - "frame-system-rpc-runtime-api 2.0.0", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pallet-babe 2.0.0", - "pallet-timestamp 2.0.0", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client 0.8.0", - "sc-executor 0.8.0", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-api 2.0.0", - "sp-application-crypto 2.0.0", - "sp-block-builder 2.0.0", - "sp-consensus-aura 0.8.0", - "sp-consensus-babe 0.8.0", - "sp-core 2.0.0", - "sp-inherents 2.0.0", - "sp-io 2.0.0", - "sp-keyring 2.0.0", - "sp-offchain 2.0.0", - "sp-runtime 2.0.0", - "sp-runtime-interface 2.0.0", - "sp-session 2.0.0", - "sp-state-machine 0.8.0", - "sp-std 2.0.0", - "sp-transaction-pool 2.0.0", - "sp-trie 2.0.0", - "sp-version 2.0.0", - "substrate-test-runtime-client 2.0.0", - "substrate-wasm-builder-runner 1.0.4", - "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "log 0.4.8", + "memory-db", + "pallet-babe", + "pallet-timestamp", + "parity-scale-codec", + "parity-util-mem", + "sc-client", + "sc-executor", + "serde", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-offchain", + "sp-runtime", + "sp-runtime-interface", + "sp-session", + "sp-state-machine", + "sp-std", + "sp-transaction-pool", + "sp-trie", + "sp-version", + "substrate-test-runtime-client", + "substrate-wasm-builder-runner", + "trie-db", ] [[package]] name = "substrate-test-runtime-client" version = "2.0.0" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-block-builder 0.8.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sp-api 2.0.0", - "sp-blockchain 2.0.0", - "sp-core 2.0.0", - "sp-runtime 2.0.0", - "substrate-test-client 2.0.0", - "substrate-test-runtime 2.0.0", + "futures 0.3.4", + "parity-scale-codec", + "sc-block-builder", + "sc-client", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "substrate-test-client", + "substrate-test-runtime", +] + +[[package]] +name = "substrate-test-runtime-transaction-pool" +version = "2.0.0" +dependencies = [ + "derive_more", + "futures 0.3.4", + "parity-scale-codec", + "parking_lot 0.10.0", + "sc-transaction-graph", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", ] [[package]] @@ -6989,1263 +7654,1436 @@ version = "2.0.0" name = "substrate-wasm-builder" version = "1.0.9" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "build-helper 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cargo_metadata 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-gc-api 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "build-helper", + "cargo_metadata", + "fs2", + "tempfile", + "toml", + "walkdir", + "wasm-gc-api", ] [[package]] name = "substrate-wasm-builder-runner" -version = "1.0.4" +version = "1.0.5" [[package]] name = "subtle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "unicode-xid 0.2.0", ] [[package]] name = "syn-mid" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "unicode-xid 0.2.0", ] [[package]] name = "sysinfo" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4b2468c629cffba39c0a4425849ab3cdb03d9dfacba69684609aea04d08ff9" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "doc-comment", + "libc", + "rayon", + "winapi 0.3.8", ] [[package]] name = "take_mut" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "target-lexicon" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" [[package]] name = "target_info" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6", + "remove_dir_all", ] [[package]] name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "rand 0.7.3", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.8", ] [[package]] name = "termcolor" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ - "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "test-case" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a605baa797821796a751f4a959e1206079b24a4b7e1ed302b7d785d81a9276c9" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "version_check 0.9.1", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "thiserror" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad" dependencies = [ - "thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "thread_local" -version = "0.3.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "threadpool" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" dependencies = [ - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus", ] [[package]] name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall", + "winapi 0.3.8", ] [[package]] name = "tiny-bip39" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure", + "hashbrown 0.1.8", + "hmac", + "once_cell 0.1.8", + "pbkdf2", + "rand 0.6.5", + "sha2", ] [[package]] name = "tiny-keccak" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "tiny-keccak" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy", ] [[package]] name = "tinytemplate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_json", ] [[package]] name = "tokio" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "mio", + "num_cpus", + "tokio-codec", + "tokio-current-thread", + "tokio-executor 0.1.10", + "tokio-fs", + "tokio-io", + "tokio-reactor", + "tokio-sync 0.1.8", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "tokio-udp", + "tokio-uds", ] [[package]] name = "tokio" -version = "0.2.4" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdd17989496f49cdc57978c96f0c9fe5e4a58a8bddc6813c449a4624f6a030b" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "fnv", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi 0.3.8", ] [[package]] name = "tokio-buf" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "either", + "futures 0.1.29", ] [[package]] name = "tokio-codec" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "tokio-io", ] [[package]] name = "tokio-current-thread" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "tokio-executor 0.1.10", ] [[package]] name = "tokio-executor" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils", + "futures 0.1.29", ] [[package]] name = "tokio-executor" version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee9ceecf69145923834ea73f32ba40c790fd877b74a7817dd0b089f1eb9c7c8" dependencies = [ - "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview", + "lazy_static", + "tokio-sync 0.2.0-alpha.6", ] [[package]] name = "tokio-fs" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "tokio-io", + "tokio-threadpool", ] [[package]] name = "tokio-io" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "log 0.4.8", +] + +[[package]] +name = "tokio-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4b1e7ed7d5d4c2af3d999904b0eebe76544897cdbfb2b9684bed2174ab20f7c" +dependencies = [ + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", ] [[package]] name = "tokio-reactor" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils", + "futures 0.1.29", + "lazy_static", + "log 0.4.8", + "mio", + "num_cpus", + "parking_lot 0.9.0", + "slab", + "tokio-executor 0.1.10", + "tokio-io", + "tokio-sync 0.1.8", ] [[package]] name = "tokio-rustls" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7cf08f990090abd6c6a73cab46fed62f85e8aef8b99e4b918a9f4a637f0676" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "iovec", + "rustls", + "tokio-io", + "webpki", ] [[package]] name = "tokio-sync" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv", + "futures 0.1.29", ] [[package]] name = "tokio-sync" version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1aaeb685540f7407ea0e27f1c9757d258c7c6bf4e3eb19da6fc59b747239d2" dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv", + "futures-core-preview", + "futures-util-preview", ] [[package]] name = "tokio-tcp" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "iovec", + "mio", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-threadpool" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque", + "crossbeam-queue", + "crossbeam-utils", + "futures 0.1.29", + "lazy_static", + "log 0.4.8", + "num_cpus", + "slab", + "tokio-executor 0.1.10", ] [[package]] name = "tokio-timer" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils", + "futures 0.1.29", + "slab", + "tokio-executor 0.1.10", ] [[package]] name = "tokio-tls" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "native-tls", + "tokio-io", ] [[package]] name = "tokio-udp" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "log 0.4.8", + "mio", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-uds" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5076db410d6fdc6523df7595447629099a1fdc47b3d9f896220780fa48faf798" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "iovec", + "libc", + "log 0.4.8", + "mio", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-util" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures-core", + "futures-sink", + "log 0.4.8", + "pin-project-lite", + "tokio 0.2.11", ] [[package]] name = "toml" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "tower-service" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e213bd24252abeb86a0b7060e02df677d367ce6cb772cef17e9214b8390a8d3" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-attributes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "tracing-attributes", + "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cfd395def5a60236e187e1ff905cb55668a59f29928dec05e6e1b1fd2ac1f3" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a46f11e372b8bd4b4398ea54353412fdd7fd42a8370c7e543e218cf7661978" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + +[[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" [[package]] name = "trie-bench" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dcd9bac85703d8f974ee1e6dfe668784b105d3385c174ad729adb7427ad5d81" dependencies = [ - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-standardmap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.2.11", + "hash-db", + "keccak-hasher", + "memory-db", + "parity-scale-codec", + "trie-db", + "trie-root", + "trie-standardmap", ] [[package]] name = "trie-db" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9222c50cc325855621271157c973da27a0dcd26fa06f8edf81020bd2333df0" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hashbrown 0.6.3", + "log 0.4.8", + "rustc-hex", + "smallvec 1.2.0", ] [[package]] name = "trie-root" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", ] [[package]] name = "trie-standardmap" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3161ba520ab28cd8e6b68e1126f1009f6e335339d1a73b978139011703264c8" dependencies = [ - "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "keccak-hasher", ] [[package]] name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "trybuild" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5b3f750c701725331ac78e389b5d143b7d25f6b6ffffd0d419759a9063ac5f" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0", + "lazy_static", + "serde", + "serde_json", + "termcolor", + "toml", ] [[package]] name = "twofish" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "byteorder 1.3.4", + "opaque-debug", ] [[package]] name = "twox-hash" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" dependencies = [ - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3", ] [[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" [[package]] name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" [[package]] name = "uint" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "crunchy", + "rustc-hex", + "static_assertions", ] [[package]] name = "unicase" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5", ] [[package]] name = "unicase" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1", ] [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", ] [[package]] name = "unicode-normalization" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.2.0", ] [[package]] name = "unicode-segmentation" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unsigned-varint" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "unsigned-varint" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c689459fbaeb50e56c6749275f084decfd02194ac5852e6617d95d0d3cf02eaf" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures_codec", + "tokio-util", ] [[package]] name = "untrusted" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" [[package]] name = "url" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", ] [[package]] name = "url" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0", + "matches", + "percent-encoding 2.1.0", ] [[package]] name = "vcpkg" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] name = "vergen" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "chrono", + "failure", ] [[package]] name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "wabt" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693" dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_derive", + "serde_json", + "wabt-sys", ] [[package]] name = "wabt-sys" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "cmake", + "glob 0.2.11", ] [[package]] name = "walkdir" -version = "2.2.9" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ - "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi 0.3.8", + "winapi-util", ] [[package]] name = "want" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29", + "log 0.4.8", + "try-lock", ] [[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", + "try-lock", ] [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.57" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.57" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" dependencies = [ - "bumpalo 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log 0.4.8", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bbdd49e3e28b40dec6a9ba8d17798245ce32b019513a845369c641b275135d9" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.57" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2", + "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.57" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.57" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" [[package]] name = "wasm-bindgen-webidl" -version = "0.2.57" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" dependencies = [ - "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "heck", + "log 0.4.8", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "wasm-bindgen-backend", + "weedle", ] [[package]] name = "wasm-gc-api" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c32691b6c7e6c14e7f8fd55361a9088b507aa49620fcd06c09b3a1082186b9" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8", + "parity-wasm 0.32.0", + "rustc-demangle", ] [[package]] name = "wasm-timer" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" dependencies = [ - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "js-sys", + "parking_lot 0.9.0", + "pin-utils", + "send_wrapper 0.2.0", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] name = "wasmi" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" dependencies = [ - "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "errno", + "libc", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm 0.41.0", + "wasmi-validation", ] [[package]] name = "wasmi-validation" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" dependencies = [ - "parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.41.0", ] [[package]] name = "wasmparser" -version = "0.39.3" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "073da89bf1c84db000dd68ce660c1b4a08e3a2d28fd1e3394ab9e7abdde4a0f8" + +[[package]] +name = "wasmparser" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e41b27a1677fe28c115de49efca55dabb14f7fece2c32947ffb9b1064fe5bd4" + +[[package]] +name = "wasmtime" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5614d964c3e7d07a13b59aca66103c52656bd80430f0d86dc7eeb3af4f03d4a2" +dependencies = [ + "anyhow", + "backtrace", + "cfg-if", + "lazy_static", + "libc", + "region", + "rustc-demangle", + "target-lexicon", + "wasmparser 0.51.1", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "wat", + "winapi 0.3.8", +] [[package]] name = "wasmtime-debug" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb5900275b4ef0b621ce725b9d5660b12825d7f7d79b392b97baf089ffab8c0" dependencies = [ - "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "faerie 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "faerie", + "gimli 0.19.0", + "more-asserts", + "target-lexicon", + "thiserror", + "wasmparser 0.51.1", + "wasmtime-environ", ] [[package]] name = "wasmtime-environ" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "zstd 0.5.1+zstd.1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", +checksum = "f04661851e133fb11691c4a0f92a705766b4bbf7afc06811f949e295cc8414fc" +dependencies = [ + "anyhow", + "base64 0.11.0", + "bincode", + "cranelift-codegen", + "cranelift-entity", + "cranelift-wasm", + "directories", + "errno", + "file-per-thread-logger", + "indexmap", + "libc", + "log 0.4.8", + "more-asserts", + "rayon", + "serde", + "sha2", + "thiserror", + "toml", + "wasmparser 0.51.1", + "winapi 0.3.8", + "zstd", ] [[package]] name = "wasmtime-jit" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d451353764ce55c9bb6a8b260063cfc209b7adadd277a9a872ab4563a69e357c" dependencies = [ - "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-frontend 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-debug 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-runtime 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "more-asserts", + "region", + "target-lexicon", + "thiserror", + "wasmparser 0.51.1", + "wasmtime-debug", + "wasmtime-environ", + "wasmtime-runtime", + "winapi 0.3.8", ] [[package]] name = "wasmtime-runtime" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dbd4fc114b828cae3e405fed413df4b3814d87a92ea029640cec9ba41f0c162" dependencies = [ - "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmtime-environ 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "cc", + "cfg-if", + "indexmap", + "libc", + "memoffset", + "more-asserts", + "region", + "thiserror", + "wasmtime-environ", + "winapi 0.3.8", +] + +[[package]] +name = "wast" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a729d076deb29c8509fa71f2d427729f9394f9496844ed8fcab152f35d163d" +dependencies = [ + "leb128", +] + +[[package]] +name = "wat" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5795e34a4b39893653dec97e644fac85c31398e0ce1abecc48967aac83d9e8ce" +dependencies = [ + "wast", ] [[package]] name = "web-sys" -version = "0.3.34" +version = "0.3.35" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" dependencies = [ - "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "js-sys", + "sourcefile", + "wasm-bindgen", + "wasm-bindgen-webidl", ] [[package]] name = "webpki" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef" dependencies = [ - "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "webpki-roots" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a262ae37dd9d60f60dd473d1158f9fbebf110ba7b6a5051c8160460f6043718b" dependencies = [ - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki", ] [[package]] name = "webpki-roots" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" dependencies = [ - "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki", ] [[package]] name = "websocket" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413b37840b9e27b340ce91b319ede10731de8c72f5bc4cb0206ec1ca4ce581d0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "websocket-base 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.29", + "hyper 0.10.16", + "native-tls", + "rand 0.6.5", + "tokio-codec", + "tokio-io", + "tokio-reactor", + "tokio-tcp", + "tokio-tls", + "unicase 1.4.2", + "url 1.7.2", + "websocket-base", ] [[package]] name = "websocket-base" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3810f0d00c4dccb54c30a4eee815e703232819dec7b007db115791c42aa374" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1", + "bitflags", + "byteorder 1.3.4", + "bytes 0.4.12", + "futures 0.1.29", + "native-tls", + "rand 0.6.5", + "sha1", + "tokio-codec", + "tokio-io", + "tokio-tcp", + "tokio-tls", ] [[package]] name = "weedle" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" dependencies = [ - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] name = "which" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" dependencies = [ - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "failure", + "libc", +] + +[[package]] +name = "which" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5475d47078209a02e60614f7ba5e645ef3ed60f771920ac1906d7c1cc65024c8" +dependencies = [ + "libc", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wincolor" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "ws" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4", + "bytes 0.4.12", + "httparse", + "log 0.4.8", + "mio", + "mio-extras", + "rand 0.7.3", + "sha-1", + "slab", + "url 2.1.1", ] [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "x25519-dalek" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop", + "curve25519-dalek 1.2.3", + "rand_core 0.3.1", ] [[package]] name = "xdg" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "yamux" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902f4cee32c401c211b6b69f4a3f6f4cf3515644db5bd822cf685a7dbd6201f9" dependencies = [ - "bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "nohash-hasher 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4", + "futures 0.3.4", + "log 0.4.8", + "nohash-hasher", + "parking_lot 0.10.0", + "rand 0.7.3", + "thiserror", ] [[package]] name = "zeroize" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" [[package]] name = "zeroize" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" dependencies = [ - "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn", + "synstructure", ] [[package]] name = "zstd" version = "0.5.1+zstd.1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5d978b793ae64375b80baf652919b148f6a496ac8802922d9999f5a553194f" dependencies = [ - "zstd-safe 2.0.3+zstd.1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd-safe", ] [[package]] name = "zstd-safe" version = "2.0.3+zstd.1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee25eac9753cfedd48133fa1736cbd23b774e253d89badbeac7d12b23848d3f" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "zstd-sys 1.4.15+zstd.1.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "zstd-sys", ] [[package]] name = "zstd-sys" version = "1.4.15+zstd.1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89719b034dc22d240d5b407fb0a3fe6d29952c181cff9a9f95c0bd40b4f8f7d8" dependencies = [ "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 9f4e9c3760c7d9dc8dad1d5a29949fa3b62d7e3a..0f1566cce54c4334c2420ca76991a433e752fc69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [workspace] members = [ - "bin/node-template", + "bin/node-template/node", "bin/node-template/runtime", + "bin/node-template/pallets/template", "bin/node/cli", "bin/node/executor", "bin/node/primitives", @@ -22,9 +23,11 @@ members = [ "client/cli", "client/consensus/aura", "client/consensus/babe", + "client/consensus/manual-seal", "client/consensus/pow", - "client/consensus/slots", "client/consensus/uncles", + "client/consensus/slots", + "client/consensus/epochs", "client/db", "client/executor", "client/executor/common", @@ -32,6 +35,7 @@ members = [ "client/executor/wasmtime", "client/executor/runtime-test", "client/finality-grandpa", + "client/informant", "client/tracing", "client/keystore", "client/network", @@ -97,6 +101,8 @@ members = [ "frame/transaction-payment/rpc/runtime-api", "frame/treasury", "frame/utility", + "frame/vesting", + "primitives/allocator", "primitives/application-crypto", "primitives/application-crypto/test", "primitives/authority-discovery", @@ -144,6 +150,7 @@ members = [ "test-utils/client", "test-utils/runtime", "test-utils/runtime/client", + "test-utils/runtime/transaction-pool", "utils/browser", "utils/build-script-utils", "utils/fork-tree", diff --git a/bin/node-template/Cargo.toml b/bin/node-template/Cargo.toml deleted file mode 100644 index cf3d7509f45d30a75ddfc8bb01e624e4938302e5..0000000000000000000000000000000000000000 --- a/bin/node-template/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "node-template" -version = "2.0.0" -authors = ["Anonymous"] -build = "build.rs" -edition = "2018" - -[[bin]] -name = "node-template" -path = "src/main.rs" - -[dependencies] -futures = "0.3.1" -ctrlc = { version = "3.1.3", features = ["termination"] } -log = "0.4.8" -tokio = { version = "0.2", features = ["rt-threaded"] } -sc-cli = { version = "0.8.0", path = "../../client/cli" } -sp-core = { version = "2.0.0", path = "../../primitives/core" } -sc-executor = { version = "0.8", path = "../../client/executor" } -sc-service = { version = "0.8", path = "../../client/service" } -sp-inherents = { version = "2.0.0", path = "../../primitives/inherents" } -sc-transaction-pool = { version = "2.0.0", path = "../../client/transaction-pool" } -sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } -sc-network = { version = "0.8", path = "../../client/network" } -sc-consensus-aura = { version = "0.8", path = "../../client/consensus/aura" } -sp-consensus-aura = { version = "0.8", path = "../../primitives/consensus/aura" } -sp-consensus = { version = "0.8", path = "../../primitives/consensus/common" } -grandpa = { version = "0.8", package = "sc-finality-grandpa", path = "../../client/finality-grandpa" } -grandpa-primitives = { version = "2.0.0", package = "sp-finality-grandpa", path = "../../primitives/finality-grandpa" } -sc-client = { version = "0.8", path = "../../client/" } -node-template-runtime = { version = "2.0.0", path = "runtime" } -sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } -sc-basic-authorship = { path = "../../client/basic-authorship" } - -[build-dependencies] -vergen = "3.0.4" -build-script-utils = { version = "2.0.0", package = "substrate-build-script-utils", path = "../../utils/build-script-utils" } diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9ad4a0e8a55ad5ad82a07252f1d62fc12e8ce101 --- /dev/null +++ b/bin/node-template/node/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "node-template" +version = "2.0.0" +authors = ["Anonymous"] +edition = "2018" +license = "Unlicense" +build = "build.rs" + +[[bin]] +name = "node-template" + +[dependencies] +futures = "0.3.1" +log = "0.4.8" +structopt = "0.3.8" + +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sc-executor = { version = "0.8", path = "../../../client/executor" } +sc-service = { version = "0.8", path = "../../../client/service" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sc-transaction-pool = { version = "2.0.0", path = "../../../client/transaction-pool" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +sc-network = { version = "0.8", path = "../../../client/network" } +sc-consensus-aura = { version = "0.8", path = "../../../client/consensus/aura" } +sp-consensus-aura = { version = "0.8", path = "../../../primitives/consensus/aura" } +sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" } +grandpa = { version = "0.8", package = "sc-finality-grandpa", path = "../../../client/finality-grandpa" } +grandpa-primitives = { version = "2.0.0", package = "sp-finality-grandpa", path = "../../../primitives/finality-grandpa" } +sc-client = { version = "0.8", path = "../../../client/" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sc-basic-authorship = { path = "../../../client/basic-authorship" } + +node-template-runtime = { version = "2.0.0", path = "../runtime" } + +[build-dependencies] +vergen = "3.0.4" +build-script-utils = { version = "2.0.0", package = "substrate-build-script-utils", path = "../../../utils/build-script-utils" } diff --git a/bin/node-template/build.rs b/bin/node-template/node/build.rs similarity index 100% rename from bin/node-template/build.rs rename to bin/node-template/node/build.rs diff --git a/bin/node-template/src/chain_spec.rs b/bin/node-template/node/src/chain_spec.rs similarity index 94% rename from bin/node-template/src/chain_spec.rs rename to bin/node-template/node/src/chain_spec.rs index 6afb67547bd6c70c241b76951e58843b872eb3aa..9bdfea3b7820d5ed80e5912d65de7e91a3b14f9a 100644 --- a/bin/node-template/src/chain_spec.rs +++ b/bin/node-template/node/src/chain_spec.rs @@ -1,7 +1,7 @@ use sp_core::{Pair, Public, sr25519}; use node_template_runtime::{ AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, - SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, Signature + IndicesConfig, SudoConfig, SystemConfig, WASM_BINARY, Signature }; use sp_consensus_aura::sr25519::{AuthorityId as AuraId}; use grandpa_primitives::{AuthorityId as GrandpaId}; @@ -128,11 +128,10 @@ fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>, changes_trie_config: Default::default(), }), indices: Some(IndicesConfig { - ids: endowed_accounts.clone(), + indices: vec![], }), balances: Some(BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), - vesting: vec![], }), sudo: Some(SudoConfig { key: root_key, @@ -145,3 +144,10 @@ fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>, }), } } + +pub fn load_spec(id: &str) -> Result, String> { + Ok(match Alternative::from(id) { + Some(spec) => Some(spec.load()?), + None => None, + }) +} diff --git a/bin/node-template/node/src/cli.rs b/bin/node-template/node/src/cli.rs new file mode 100644 index 0000000000000000000000000000000000000000..0091ef7d75912f736c1cbffde84d29e17186038c --- /dev/null +++ b/bin/node-template/node/src/cli.rs @@ -0,0 +1,11 @@ +use sc_cli::{RunCmd, Subcommand}; +use structopt::StructOpt; + +#[derive(Debug, StructOpt)] +pub struct Cli { + #[structopt(subcommand)] + pub subcommand: Option, + + #[structopt(flatten)] + pub run: RunCmd, +} diff --git a/bin/node-template/node/src/command.rs b/bin/node-template/node/src/command.rs new file mode 100644 index 0000000000000000000000000000000000000000..e7e386703deee0a7894dfc5bf931c6c41fa89438 --- /dev/null +++ b/bin/node-template/node/src/command.rs @@ -0,0 +1,46 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair}; +use sc_cli::{VersionInfo, error}; +use crate::service; +use crate::chain_spec; +use crate::cli::Cli; + +/// Parse and run command line arguments +pub fn run(version: VersionInfo) -> error::Result<()> { + let opt = sc_cli::from_args::(&version); + + let config = sc_service::Configuration::new(&version); + + match opt.subcommand { + Some(subcommand) => sc_cli::run_subcommand( + config, + subcommand, + chain_spec::load_spec, + |config: _| Ok(new_full_start!(config).0), + &version, + ), + None => sc_cli::run( + config, + opt.run, + service::new_light, + service::new_full, + chain_spec::load_spec, + &version, + ) + } +} diff --git a/bin/node-template/src/main.rs b/bin/node-template/node/src/main.rs similarity index 72% rename from bin/node-template/src/main.rs rename to bin/node-template/node/src/main.rs index ea64bc1413642b88a0a25539d2dcdc645014266c..9d0a57d77a851d551e9a8e087365a8d8cb470977 100644 --- a/bin/node-template/src/main.rs +++ b/bin/node-template/node/src/main.rs @@ -5,10 +5,11 @@ mod chain_spec; #[macro_use] mod service; mod cli; +mod command; -pub use sc_cli::{VersionInfo, IntoExit, error}; +pub use sc_cli::{VersionInfo, error}; -fn main() -> Result<(), cli::error::Error> { +fn main() -> Result<(), error::Error> { let version = VersionInfo { name: "Substrate Node", commit: env!("VERGEN_SHA_SHORT"), @@ -17,7 +18,8 @@ fn main() -> Result<(), cli::error::Error> { author: "Anonymous", description: "Template Node", support_url: "support.anonymous.an", + copyright_start_year: 2017, }; - cli::run(std::env::args(), cli::Exit, version) + command::run(version) } diff --git a/bin/node-template/src/service.rs b/bin/node-template/node/src/service.rs similarity index 87% rename from bin/node-template/src/service.rs rename to bin/node-template/node/src/service.rs index ed2299e30f73ecf31d8e0180bc71074d37eae388..cf5cb361fcc055de1aa895001bf4ba8b12bae1dd 100644 --- a/bin/node-template/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -11,7 +11,6 @@ use sc_executor::native_executor_instance; pub use sc_executor::NativeExecutor; use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair}; use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; -use futures::{FutureExt, compat::Future01CompatExt}; // Our native executor instance. native_executor_instance!( @@ -42,10 +41,8 @@ macro_rules! new_full_start { })? .with_transaction_pool(|config, client, _fetcher| { let pool_api = sc_transaction_pool::FullChainApi::new(client.clone()); - let pool = sc_transaction_pool::BasicPool::new(config, pool_api); - let maintainer = sc_transaction_pool::FullBasicPoolMaintainer::new(pool.pool().clone(), client); - let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer); - Ok(maintainable_pool) + let pool = sc_transaction_pool::BasicPool::new(config, std::sync::Arc::new(pool_api)); + Ok(pool) })? .with_import_queue(|_config, client, mut select_chain, transaction_pool| { let select_chain = select_chain.take() @@ -80,7 +77,7 @@ macro_rules! new_full_start { } /// Builds a new service for a full client. -pub fn new_full(config: Configuration) +pub fn new_full(config: Configuration) -> Result { let is_authority = config.roles.is_authority(); @@ -133,7 +130,7 @@ pub fn new_full(config: Configuration(config: Configuration { // start the lightweight GRANDPA observer - service.spawn_task(grandpa::run_grandpa_observer( + service.spawn_task("grandpa-observer", grandpa::run_grandpa_observer( grandpa_config, grandpa_link, service.network(), service.on_exit(), - service.spawn_task_handle(), - )?.compat().map(drop)); + )?); }, (true, false) => { // start the full GRANDPA voter @@ -175,12 +171,11 @@ pub fn new_full(config: Configuration { grandpa::setup_disabled_grandpa( @@ -195,7 +190,7 @@ pub fn new_full(config: Configuration(config: Configuration) +pub fn new_light(config: Configuration) -> Result { let inherent_data_providers = InherentDataProviders::new(); @@ -207,11 +202,12 @@ pub fn new_light(config: Configuration> + Into<::Event>; +} + +// This pallet's storage items. +decl_storage! { + trait Store for Module as TemplateModule { + // Just a dummy storage item. + // Here we are declaring a StorageValue, `Something` as a Option + // `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored + Something get(fn something): Option; + } +} + +// The pallet's events +decl_event!( + pub enum Event where AccountId = ::AccountId { + /// Just a dummy event. + /// Event `Something` is declared with a parameter of the type `u32` and `AccountId` + /// To emit this event, we call the deposit funtion, from our runtime funtions + SomethingStored(u32, AccountId), + } +); + +// The pallet's errors +decl_error! { + pub enum Error for Module { + /// Value was None + NoneValue, + /// Value reached maximum and cannot be incremented further + StorageOverflow, + } +} + +// The pallet's dispatchable functions. +decl_module! { + /// The module declaration. + pub struct Module for enum Call where origin: T::Origin { + // Initializing errors + // this includes information about your errors in the node's metadata. + // it is needed only if you are using errors in your pallet + type Error = Error; + + // Initializing events + // this is needed only if you are using events in your pallet + fn deposit_event() = default; + + /// Just a dummy entry point. + /// function that can be called by the external world as an extrinsics call + /// takes a parameter of the type `AccountId`, stores it, and emits an event + pub fn do_something(origin, something: u32) -> dispatch::DispatchResult { + // Check it was signed and get the signer. See also: ensure_root and ensure_none + let who = ensure_signed(origin)?; + + // Code to execute when something calls this. + // For example: the following line stores the passed in u32 in the storage + Something::put(something); + + // Here we are raising the Something event + Self::deposit_event(RawEvent::SomethingStored(something, who)); + Ok(()) + } + + /// Another dummy entry point. + /// takes no parameters, attempts to increment storage value, and possibly throws an error + pub fn cause_error(origin) -> dispatch::DispatchResult { + // Check it was signed and get the signer. See also: ensure_root and ensure_none + let _who = ensure_signed(origin)?; + + match Something::get() { + None => Err(Error::::NoneValue)?, + Some(old) => { + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + Something::put(new); + Ok(()) + }, + } + } + } +} diff --git a/bin/node-template/pallets/template/src/mock.rs b/bin/node-template/pallets/template/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..b3c1098db6743189ec8fcad258459f2e013126e8 --- /dev/null +++ b/bin/node-template/pallets/template/src/mock.rs @@ -0,0 +1,55 @@ +// Creating mock runtime here + +use crate::{Module, Trait}; +use sp_core::H256; +use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill, +}; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +// For testing the module, we construct most of a mock runtime. This means +// first constructing a configuration type (`Test`) which `impl`s each of the +// configuration traits of modules we want to use. +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); +} +impl system::Trait for Test { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); +} +impl Trait for Test { + type Event = (); +} +pub type TemplateModule = Module; + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::default().build_storage::().unwrap().into() +} diff --git a/bin/node-template/pallets/template/src/tests.rs b/bin/node-template/pallets/template/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..44a423c948fbf88cb193612fbab307d4df20b634 --- /dev/null +++ b/bin/node-template/pallets/template/src/tests.rs @@ -0,0 +1,26 @@ +// Tests to be written here + +use crate::{Error, mock::*}; +use frame_support::{assert_ok, assert_noop}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Just a dummy test for the dummy funtion `do_something` + // calling the `do_something` function with a value 42 + assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); + // asserting that the stored value is equal to what we stored + assert_eq!(TemplateModule::something(), Some(42)); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the correct error is thrown on None value + assert_noop!( + TemplateModule::cause_error(Origin::signed(1)), + Error::::NoneValue + ); + }); +} diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index e048d8baf7bfebd97a03c86da99804328af05370..ddecb0e4cff4c11da2ddf6e7973c1c57e082d1e1 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -3,8 +3,11 @@ name = "node-template-runtime" version = "2.0.0" authors = ["Anonymous"] edition = "2018" +license = "Unlicense" [dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } + aura = { version = "2.0.0", default-features = false, package = "pallet-aura", path = "../../../frame/aura" } balances = { version = "2.0.0", default-features = false, package = "pallet-balances", path = "../../../frame/balances" } frame-support = { version = "2.0.0", default-features = false, path = "../../../frame/support" } @@ -15,8 +18,6 @@ sudo = { version = "2.0.0", default-features = false, package = "pallet-sudo", p system = { version = "2.0.0", default-features = false, package = "frame-system", path = "../../../frame/system" } timestamp = { version = "2.0.0", default-features = false, package = "pallet-timestamp", path = "../../../frame/timestamp" } transaction-payment = { version = "2.0.0", default-features = false, package = "pallet-transaction-payment", path = "../../../frame/transaction-payment" } - -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } frame-executive = { version = "2.0.0", default-features = false, path = "../../../frame/executive" } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-api = { version = "2.0.0", default-features = false, path = "../../../primitives/api" } @@ -32,6 +33,8 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../../primiti sp-transaction-pool = { version = "2.0.0", default-features = false, path = "../../../primitives/transaction-pool" } sp-version = { version = "2.0.0", default-features = false, path = "../../../primitives/version" } +template = { version = "2.0.0", default-features = false, path = "../pallets/template", package = "pallet-template" } + [build-dependencies] wasm-builder-runner = { version = "1.0.4", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } @@ -63,4 +66,5 @@ std = [ "system/std", "timestamp/std", "transaction-payment/std", + "template/std", ] diff --git a/bin/node-template/runtime/build.rs b/bin/node-template/runtime/build.rs index 8bdf7584bb81aeae96e02d18121b437e4e1c0f89..39f7f56feb0b1730d7f2c9a7daa898fe54bfd052 100644 --- a/bin/node-template/runtime/build.rs +++ b/bin/node-template/runtime/build.rs @@ -1,11 +1,10 @@ -use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; +use wasm_builder_runner::WasmBuilder; fn main() { - build_current_project_with_rustflags( - "wasm_binary.rs", - WasmBuilderSource::Crates("1.0.9"), - // This instructs LLD to export __heap_base as a global variable, which is used by the - // external memory allocator. - "-Clink-arg=--export=__heap_base", - ); + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_from_crates("1.0.9") + .export_heap_base() + .import_memory() + .build() } diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 4416cbf13ee004feb7a03df8a781748e4e470b40..04394917f14fd1e032ed2aa241a17c9f6a3c5e93 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -12,7 +12,7 @@ use sp_std::prelude::*; use sp_core::OpaqueMetadata; use sp_runtime::{ ApplyExtrinsicResult, transaction_validity::TransactionValidity, generic, create_runtime_str, - impl_opaque_keys, MultiSignature + impl_opaque_keys, MultiSignature, }; use sp_runtime::traits::{ BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, IdentifyAccount @@ -37,6 +37,9 @@ pub use frame_support::{ weights::Weight, }; +/// Importing a template pallet +pub use template; + /// An index to a block. pub type BlockNumber = u32; @@ -63,9 +66,6 @@ pub type Hash = sp_core::H256; /// Digest item type. pub type DigestItem = generic::DigestItem; -/// Used for the module template in `./template.rs` -mod template; - /// 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 @@ -161,6 +161,12 @@ impl system::Trait for Runtime { /// /// This type is being generated by `construct_runtime!`. type ModuleToIndex = ModuleToIndex; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnReapAccount = Balances; + /// The data to be stored in an account. + type AccountData = balances::AccountData; } impl aura::Trait for Runtime { @@ -171,16 +177,21 @@ impl grandpa::Trait for Runtime { type Event = Event; } +parameter_types! { + /// How much an index costs. + pub const IndexDeposit: u128 = 100; +} + impl indices::Trait for Runtime { /// The type for recording indexing into the account enumeration. If this ever overflows, there /// will be problems! type AccountIndex = AccountIndex; - /// Use the standard means of resolving an index hint from an id. - type ResolveHint = indices::SimpleResolveHint; - /// Determine whether an account is dead. - type IsDeadAccount = Balances; /// The ubiquitous event type. type Event = Event; + /// The currency type. + type Currency = Balances; + /// How much an index costs. + type Deposit = IndexDeposit; } parameter_types! { @@ -196,26 +207,16 @@ impl timestamp::Trait for Runtime { parameter_types! { pub const ExistentialDeposit: u128 = 500; - pub const TransferFee: u128 = 0; - pub const CreationFee: u128 = 0; } impl balances::Trait for Runtime { /// The type for recording an account's balance. type Balance = Balance; - /// What to do if an account's free balance gets zeroed. - type OnFreeBalanceZero = (); - /// What to do if an account is fully reaped from the system. - type OnReapAccount = System; - /// What to do if a new account is created. - type OnNewAccount = Indices; /// The ubiquitous event type. type Event = Event; type DustRemoval = (); - type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -248,12 +249,12 @@ construct_runtime!( NodeBlock = opaque::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Storage, Config, Event}, + System: system::{Module, Call, Config, Storage, Event}, Timestamp: timestamp::{Module, Call, Storage, Inherent}, Aura: aura::{Module, Config, Inherent(Timestamp)}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, - Indices: indices, - Balances: balances, + Indices: indices::{Module, Call, Storage, Event, Config}, + Balances: balances::{Module, Call, Storage, Config, Event}, TransactionPayment: transaction_payment::{Module, Storage}, Sudo: sudo, // Used for the module template in `./template.rs` @@ -360,6 +361,12 @@ impl_runtime_apis! { fn generate_session_keys(seed: Option>) -> Vec { opaque::SessionKeys::generate(seed) } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + opaque::SessionKeys::decode_into_raw_public_keys(&encoded) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/bin/node-template/runtime/src/template.rs b/bin/node-template/runtime/src/template.rs deleted file mode 100644 index a64a4c3216fe6b408181fc20c22255764beff8fd..0000000000000000000000000000000000000000 --- a/bin/node-template/runtime/src/template.rs +++ /dev/null @@ -1,132 +0,0 @@ -/// A runtime module template with necessary imports - -/// Feel free to remove or edit this file as needed. -/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs -/// If you remove this file, you can remove those references - - -/// For more guidance on Substrate modules, see the example module -/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs - -use frame_support::{decl_module, decl_storage, decl_event, dispatch}; -use system::ensure_signed; - -/// The module's configuration trait. -pub trait Trait: system::Trait { - // TODO: Add other types and constants required configure this module. - - /// The overarching event type. - type Event: From> + Into<::Event>; -} - -// This module's storage items. -decl_storage! { - trait Store for Module as TemplateModule { - // Just a dummy storage item. - // Here we are declaring a StorageValue, `Something` as a Option - // `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored - Something get(fn something): Option; - } -} - -// The module's dispatchable functions. -decl_module! { - /// The module declaration. - pub struct Module for enum Call where origin: T::Origin { - // Initializing events - // this is needed only if you are using events in your module - fn deposit_event() = default; - - // Just a dummy entry point. - // function that can be called by the external world as an extrinsics call - // takes a parameter of the type `AccountId`, stores it and emits an event - pub fn do_something(origin, something: u32) -> dispatch::DispatchResult { - // TODO: You only need this if you want to check it was signed. - let who = ensure_signed(origin)?; - - // TODO: Code to execute when something calls this. - // For example: the following line stores the passed in u32 in the storage - Something::put(something); - - // here we are raising the Something event - Self::deposit_event(RawEvent::SomethingStored(something, who)); - Ok(()) - } - } -} - -decl_event!( - pub enum Event where AccountId = ::AccountId { - // Just a dummy event. - // Event `Something` is declared with a parameter of the type `u32` and `AccountId` - // To emit this event, we call the deposit funtion, from our runtime funtions - SomethingStored(u32, AccountId), - } -); - -/// tests for this module -#[cfg(test)] -mod tests { - use super::*; - - use sp_core::H256; - use frame_support::{impl_outer_origin, assert_ok, parameter_types, weights::Weight}; - use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill, - }; - - impl_outer_origin! { - pub enum Origin for Test {} - } - - // For testing the module, we construct most of a mock runtime. This means - // first constructing a configuration type (`Test`) which `impl`s each of the - // configuration traits of modules we want to use. - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); - } - impl system::Trait for Test { - type Origin = Origin; - type Call = (); - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = (); - type BlockHashCount = BlockHashCount; - type MaximumBlockWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type Version = (); - type ModuleToIndex = (); - } - impl Trait for Test { - type Event = (); - } - type TemplateModule = Module; - - // This function basically just builds a genesis storage key/value store according to - // our desired mockup. - fn new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::default().build_storage::().unwrap().into() - } - - #[test] - fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Just a dummy test for the dummy funtion `do_something` - // calling the `do_something` function with a value 42 - assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); - // asserting that the stored value is equal to what we stored - assert_eq!(TemplateModule::something(), Some(42)); - }); - } -} diff --git a/bin/node-template/src/cli.rs b/bin/node-template/src/cli.rs deleted file mode 100644 index fcfd330816cd1b0e46bbef0876f449d1c9661c7a..0000000000000000000000000000000000000000 --- a/bin/node-template/src/cli.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::service; -use futures::{future::{select, Map, Either}, FutureExt, channel::oneshot}; -use std::cell::RefCell; -use tokio::runtime::Runtime; -pub use sc_cli::{VersionInfo, IntoExit, error}; -use sc_cli::{display_role, informant, parse_and_prepare, ParseAndPrepare, NoCustom}; -use sc_service::{AbstractService, Roles as ServiceRoles, Configuration}; -use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair}; -use crate::chain_spec; -use log::info; - -/// Parse command line arguments into service configuration. -pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> where - I: IntoIterator, - T: Into + Clone, - E: IntoExit, -{ - type Config = Configuration<(), T>; - match parse_and_prepare::(&version, "substrate-node", args) { - ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit, - |exit, _cli_args, _custom_args, mut config: Config<_>| { - info!("{}", version.name); - info!(" version {}", config.full_version()); - info!(" by {}, 2017, 2018", version.author); - info!("Chain specification: {}", config.chain_spec.name()); - info!("Node name: {}", config.name); - info!("Roles: {}", display_role(&config)); - let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; - config.tasks_executor = { - let runtime_handle = runtime.handle().clone(); - Some(Box::new(move |fut| { runtime_handle.spawn(fut); })) - }; - match config.roles { - ServiceRoles::LIGHT => run_until_exit( - runtime, - service::new_light(config)?, - exit - ), - _ => run_until_exit( - runtime, - service::new_full(config)?, - exit - ), - } - }), - ParseAndPrepare::BuildSpec(cmd) => cmd.run::(load_spec), - ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>| - Ok(new_full_start!(config).0), load_spec, exit), - ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>| - Ok(new_full_start!(config).0), load_spec, exit), - ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder(|config: Config<_>| - Ok(new_full_start!(config).0), load_spec, exit), - ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec), - ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_>| - Ok(new_full_start!(config).0), load_spec), - ParseAndPrepare::CustomCommand(_) => Ok(()) - }?; - - Ok(()) -} - -fn load_spec(id: &str) -> Result, String> { - Ok(match chain_spec::Alternative::from(id) { - Some(spec) => Some(spec.load()?), - None => None, - }) -} - -fn run_until_exit( - mut runtime: Runtime, - service: T, - e: E, -) -> error::Result<()> -where - T: AbstractService, - E: IntoExit, -{ - let (exit_send, exit) = oneshot::channel(); - - let informant = informant::build(&service); - - let handle = runtime.spawn(select(exit, informant)); - - // we eagerly drop the service so that the internal exit future is fired, - // but we need to keep holding a reference to the global telemetry guard - let _telemetry = service.telemetry(); - - let exit = e.into_exit(); - let service_res = runtime.block_on(select(service, exit)); - - let _ = exit_send.send(()); - - runtime.block_on(handle); - - match service_res { - Either::Left((res, _)) => res.map_err(error::Error::Service), - Either::Right((_, _)) => Ok(()) - } -} - -// handles ctrl-c -pub struct Exit; -impl IntoExit for Exit { - type Exit = Map, fn(Result<(), oneshot::Canceled>) -> ()>; - fn into_exit(self) -> Self::Exit { - // can't use signal directly here because CtrlC takes only `Fn`. - let (exit_send, exit) = oneshot::channel(); - - let exit_send_cell = RefCell::new(Some(exit_send)); - ctrlc::set_handler(move || { - let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take(); - if let Some(exit_send) = exit_send { - exit_send.send(()).expect("Error sending exit notification"); - } - }).expect("Error setting Ctrl-C handler"); - - exit.map(drop) - } -} diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index c81cf38a9c2fff99808a8726fa580ecc72404b56..dad76ae4bfdad4b9f267f077bc168b8f1ed490ad 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Parity Technologies "] description = "Substrate node implementation in Rust." build = "build.rs" edition = "2018" +license = "GPL-3.0" default-run = "substrate" [badges] @@ -30,7 +31,8 @@ hex-literal = "0.2.1" jsonrpc-core = "14.0.3" log = "0.4.8" rand = "0.7.2" -structopt = "=0.3.7" +structopt = { version = "0.3.8", optional = true } +tracing = "0.1.10" # primitives sp-authority-discovery = { version = "2.0.0", path = "../../../primitives/authority-discovery" } @@ -59,6 +61,7 @@ sc-offchain = { version = "2.0.0", path = "../../../client/offchain" } sc-rpc = { version = "2.0.0", path = "../../../client/rpc" } sc-basic-authorship = { version = "0.8", path = "../../../client/basic-authorship" } sc-service = { version = "0.8", default-features = false, path = "../../../client/service" } +sc-tracing = { version = "2.0.0", path = "../../../client/tracing" } sc-telemetry = { version = "2.0.0", path = "../../../client/telemetry" } sc-authority-discovery = { version = "0.8", path = "../../../client/authority-discovery" } @@ -80,9 +83,7 @@ node-primitives = { version = "2.0.0", path = "../primitives" } node-executor = { version = "2.0.0", path = "../executor" } # CLI-specific dependencies -tokio = { version = "0.2", features = ["rt-threaded"], optional = true } sc-cli = { version = "0.8.0", optional = true, path = "../../../client/cli" } -ctrlc = { version = "3.1.3", features = ["termination"], optional = true } node-transaction-factory = { version = "0.8.0", optional = true, path = "../transaction-factory" } # WASM-specific dependencies @@ -93,15 +94,27 @@ browser-utils = { path = "../../../utils/browser", optional = true } [dev-dependencies] sc-keystore = { version = "2.0.0", path = "../../../client/keystore" } sc-consensus-babe = { version = "0.8", features = ["test-helpers"], path = "../../../client/consensus/babe" } +sc-consensus-epochs = { version = "0.8", path = "../../../client/consensus/epochs" } sc-service-test = { version = "2.0.0", path = "../../../client/service/test" } futures = "0.3.1" tempfile = "3.1.0" +assert_cmd = "0.12" +nix = "0.17" [build-dependencies] -sc-cli = { version = "0.8.0", package = "sc-cli", path = "../../../client/cli" } build-script-utils = { version = "2.0.0", package = "substrate-build-script-utils", path = "../../../utils/build-script-utils" } -structopt = "=0.3.7" -vergen = "3.0.4" +structopt = { version = "0.3.8", optional = true } +node-transaction-factory = { version = "0.8.0", optional = true, path = "../transaction-factory" } + +[build-dependencies.sc-cli] +version = "0.8.0" +package = "sc-cli" +path = "../../../client/cli" +optional = true + +[build-dependencies.vergen] +version = "3.0.4" +optional = true [features] default = ["cli", "wasmtime"] @@ -113,10 +126,10 @@ browser = [ cli = [ "sc-cli", "node-transaction-factory", - "tokio", - "ctrlc", "sc-service/rocksdb", "node-executor/wasmi-errno", + "vergen", + "structopt", ] wasmtime = [ "cli", diff --git a/bin/node/cli/bin/main.rs b/bin/node/cli/bin/main.rs index c766f3945c99f29d4d4150caa4b0ffddf743ed84..e951c04710b92ff387b2f5fd5ba3c144b68af05c 100644 --- a/bin/node/cli/bin/main.rs +++ b/bin/node/cli/bin/main.rs @@ -18,31 +18,8 @@ #![warn(missing_docs)] -use futures::channel::oneshot; -use futures::{future, FutureExt}; use sc_cli::VersionInfo; -use std::cell::RefCell; - -// handles ctrl-c -struct Exit; -impl sc_cli::IntoExit for Exit { - type Exit = future::Map, fn(Result<(), oneshot::Canceled>) -> ()>; - fn into_exit(self) -> Self::Exit { - // can't use signal directly here because CtrlC takes only `Fn`. - let (exit_send, exit) = oneshot::channel(); - - let exit_send_cell = RefCell::new(Some(exit_send)); - ctrlc::set_handler(move || { - if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() { - exit_send.send(()).expect("Error sending exit notification"); - } - }).expect("Error setting Ctrl-C handler"); - - exit.map(|_| ()) - } -} - fn main() -> Result<(), sc_cli::error::Error> { let version = VersionInfo { name: "Substrate Node", @@ -52,7 +29,8 @@ fn main() -> Result<(), sc_cli::error::Error> { author: "Parity Technologies ", description: "Generic substrate node", support_url: "https://github.com/paritytech/substrate/issues/new", + copyright_start_year: 2017, }; - node_cli::run(std::env::args(), Exit, version) + node_cli::run(std::env::args(), version) } diff --git a/bin/node/cli/browser-demo/index.html b/bin/node/cli/browser-demo/index.html index 0b66b612f10f78c7db40b582bb2d8d2ed5eb73af..f40863c46e77d2d811848aa6f459d5fe8381b6fe 100644 --- a/bin/node/cli/browser-demo/index.html +++ b/bin/node/cli/browser-demo/index.html @@ -15,11 +15,12 @@ function log(msg) { async function start() { log('Loading WASM'); await init('./pkg/node_cli_bg.wasm'); - log('Successfully loaded WASM'); + log('Fetching chain spec'); + const chain_spec_response = await fetch("https://raw.githubusercontent.com/paritytech/substrate/master/bin/node/cli/res/flaming-fir.json"); + const chain_spec_text = await chain_spec_response.text(); // Build our client. - log('Starting client'); - let client = await start_client(ws()); + let client = await start_client(chain_spec_text, 'info', ws()); log('Client started'); client.rpcSubscribe('{"method":"chain_subscribeNewHead","params":[],"id":1,"jsonrpc":"2.0"}', diff --git a/bin/node/cli/build.rs b/bin/node/cli/build.rs index 9e18fc669934d8fbc75d6c3e937f71e3acf1224a..e824b59be64f3dedba3b738a6a3908b9e48b5091 100644 --- a/bin/node/cli/build.rs +++ b/bin/node/cli/build.rs @@ -14,39 +14,48 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::{fs, env, path::Path}; -use structopt::{StructOpt, clap::Shell}; -use sc_cli::{NoCustom, CoreParams}; -use vergen::{ConstantsFlags, generate_cargo_keys}; - fn main() { - build_shell_completion(); - generate_cargo_keys(ConstantsFlags::all()).expect("Failed to generate metadata files"); - - build_script_utils::rerun_if_git_head_changed(); + #[cfg(feature = "cli")] + cli::main(); } -/// Build shell completion scripts for all known shells -/// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123 -fn build_shell_completion() { - for shell in &[Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] { - build_completion(shell); +#[cfg(feature = "cli")] +mod cli { + include!("src/cli.rs"); + + use std::{fs, env, path::Path}; + use sc_cli::{structopt::clap::Shell}; + use vergen::{ConstantsFlags, generate_cargo_keys}; + + pub fn main() { + build_shell_completion(); + generate_cargo_keys(ConstantsFlags::all()).expect("Failed to generate metadata files"); + + build_script_utils::rerun_if_git_head_changed(); + } + + /// Build shell completion scripts for all known shells + /// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123 + fn build_shell_completion() { + for shell in &[Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] { + build_completion(shell); + } } -} -/// Build the shell auto-completion for a given Shell -fn build_completion(shell: &Shell) { - let outdir = match env::var_os("OUT_DIR") { - None => return, - Some(dir) => dir, - }; - let path = Path::new(&outdir) - .parent().unwrap() - .parent().unwrap() - .parent().unwrap() - .join("completion-scripts"); - - fs::create_dir(&path).ok(); - - CoreParams::::clap().gen_completions("substrate-node", *shell, &path); + /// Build the shell auto-completion for a given Shell + fn build_completion(shell: &Shell) { + let outdir = match env::var_os("OUT_DIR") { + None => return, + Some(dir) => dir, + }; + let path = Path::new(&outdir) + .parent().unwrap() + .parent().unwrap() + .parent().unwrap() + .join("completion-scripts"); + + fs::create_dir(&path).ok(); + + Cli::clap().gen_completions("substrate-node", *shell, &path); + } } diff --git a/bin/node/cli/src/browser.rs b/bin/node/cli/src/browser.rs index 9747a583c78087f44f55ea8389d79dcde6e6bdab..80ca963445d0c10b579f05e7ffe1a46b8a4ae41b 100644 --- a/bin/node/cli/src/browser.rs +++ b/bin/node/cli/src/browser.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::ChainSpec; +use crate::chain_spec::ChainSpec; use log::info; use wasm_bindgen::prelude::*; use sc_service::Configuration; @@ -22,29 +22,29 @@ use browser_utils::{ Transport, Client, browser_configuration, set_console_error_panic_hook, init_console_log, }; +use std::str::FromStr; /// Starts the client. #[wasm_bindgen] -pub async fn start_client(wasm_ext: Transport) -> Result { - start_inner(wasm_ext) +pub async fn start_client(chain_spec: String, log_level: String, wasm_ext: Transport) -> Result { + start_inner(chain_spec, log_level, wasm_ext) .await .map_err(|err| JsValue::from_str(&err.to_string())) } -async fn start_inner(wasm_ext: Transport) -> Result> { +async fn start_inner(chain_spec: String, log_level: String, wasm_ext: Transport) -> Result> { set_console_error_panic_hook(); - init_console_log(log::Level::Info)?; - - let chain_spec = ChainSpec::FlamingFir.load() + init_console_log(log::Level::from_str(&log_level)?)?; + let chain_spec = ChainSpec::from_json_bytes(chain_spec.as_bytes().to_vec()) .map_err(|e| format!("{:?}", e))?; - let config: Configuration<(), _, _> = browser_configuration(wasm_ext, chain_spec) + let config: Configuration<_, _> = browser_configuration(wasm_ext, chain_spec) .await?; info!("Substrate browser node"); info!(" version {}", config.full_version()); - info!(" by Parity Technologies, 2017-2019"); - info!("Chain specification: {}", config.chain_spec.name()); + info!(" by Parity Technologies, 2017-2020"); + info!("Chain specification: {}", config.expect_chain_spec().name()); info!("Node name: {}", config.name); info!("Roles: {:?}", config.roles); diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index b8ff948b01b24e3a021b7cabc686b4079c724103..5cdfb5cab86f591343a18596aa5ee49c9f3d3ee7 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -21,8 +21,8 @@ use sp_core::{Pair, Public, crypto::UncheckedInto, sr25519}; use serde::{Serialize, Deserialize}; use node_runtime::{ AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, - GrandpaConfig, ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, - SocietyConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, + GrandpaConfig, ImOnlineConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, + IndicesConfig, SocietyConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, }; use node_runtime::Block; use node_runtime::constants::currency::*; @@ -237,12 +237,9 @@ pub fn testnet_genesis( .map(|k| (k, ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect(), - vesting: vec![], }), pallet_indices: Some(IndicesConfig { - ids: endowed_accounts.iter().cloned() - .chain(initial_authorities.iter().map(|x| x.0.clone())) - .collect::>(), + indices: vec![], }), pallet_session: Some(SessionConfig { keys: initial_authorities.iter().map(|x| { @@ -262,13 +259,17 @@ pub fn testnet_genesis( }), pallet_democracy: Some(DemocracyConfig::default()), pallet_collective_Instance1: Some(CouncilConfig { - members: endowed_accounts.iter().cloned() - .collect::>()[..(num_endowed_accounts + 1) / 2].to_vec(), + members: endowed_accounts.iter() + .take((num_endowed_accounts + 1) / 2) + .cloned() + .collect(), phantom: Default::default(), }), pallet_collective_Instance2: Some(TechnicalCommitteeConfig { - members: endowed_accounts.iter().cloned() - .collect::>()[..(num_endowed_accounts + 1) / 2].to_vec(), + members: endowed_accounts.iter() + .take((num_endowed_accounts + 1) / 2) + .cloned() + .collect(), phantom: Default::default(), }), pallet_contracts: Some(ContractsConfig { @@ -296,10 +297,14 @@ pub fn testnet_genesis( pallet_membership_Instance1: Some(Default::default()), pallet_treasury: Some(Default::default()), pallet_society: Some(SocietyConfig { - members: endowed_accounts[0..3].to_vec(), + members: endowed_accounts.iter() + .take((num_endowed_accounts + 1) / 2) + .cloned() + .collect(), pot: 0, max_members: 999, - }) + }), + pallet_vesting: Some(Default::default()), } } @@ -359,6 +364,7 @@ pub(crate) mod tests { use super::*; use crate::service::{new_full, new_light}; use sc_service_test; + use sp_runtime::BuildStorage; fn local_testnet_genesis_instant_single() -> GenesisConfig { testnet_genesis( @@ -408,4 +414,19 @@ pub(crate) mod tests { |config| new_light(config), ); } + + #[test] + fn test_create_development_chain_spec() { + development_config().build_storage().unwrap(); + } + + #[test] + fn test_create_local_testnet_chain_spec() { + local_testnet_config().build_storage().unwrap(); + } + + #[test] + fn test_staging_test_net_chain_spec() { + staging_testnet_config().build_storage().unwrap(); + } } diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index 5ade700513e537a997d61db43b6e82dcd8cc86e7..7844c2c0a521e04b4fcdacbd75c79d73e2dbaae8 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -14,21 +14,31 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -pub use sc_cli::VersionInfo; -use tokio::runtime::{Builder as RuntimeBuilder, Runtime}; -use sc_cli::{IntoExit, NoCustom, SharedParams, ImportParams, error}; -use sc_service::{AbstractService, Roles as ServiceRoles, Configuration}; -use log::info; +use sc_cli::{SharedParams, ImportParams, RunCmd}; use structopt::StructOpt; -use sc_cli::{display_role, parse_and_prepare, GetSharedParams, ParseAndPrepare}; -use crate::{service, ChainSpec, load_spec}; -use crate::factory_impl::FactoryState; -use node_transaction_factory::RuntimeAdapter; -use futures::{channel::oneshot, future::{select, Either}}; -/// Custom subcommands. +#[allow(missing_docs)] #[derive(Clone, Debug, StructOpt)] -pub enum CustomSubcommands { +#[structopt(settings = &[ + structopt::clap::AppSettings::GlobalVersion, + structopt::clap::AppSettings::ArgsNegateSubcommands, + structopt::clap::AppSettings::SubcommandsNegateReqs, +])] +pub struct Cli { + #[allow(missing_docs)] + #[structopt(subcommand)] + pub subcommand: Option, + #[allow(missing_docs)] + #[structopt(flatten)] + pub run: RunCmd, +} + +#[allow(missing_docs)] +#[derive(Clone, Debug, StructOpt)] +pub enum Subcommand { + #[allow(missing_docs)] + #[structopt(flatten)] + Base(sc_cli::Subcommand), /// The custom factory subcommmand for manufacturing transactions. #[structopt( name = "factory", @@ -38,46 +48,17 @@ pub enum CustomSubcommands { Factory(FactoryCmd), } -impl GetSharedParams for CustomSubcommands { - fn shared_params(&self) -> Option<&SharedParams> { - match self { - CustomSubcommands::Factory(cmd) => Some(&cmd.shared_params), - } - } -} - /// The `factory` command used to generate transactions. /// Please note: this command currently only works on an empty database! #[derive(Debug, StructOpt, Clone)] pub struct FactoryCmd { - /// How often to repeat. This option only has an effect in mode `MasterToNToM`. - #[structopt(long="rounds", default_value = "1")] - pub rounds: u64, - - /// MasterToN: Manufacture `num` transactions from the master account - /// to `num` randomly created accounts, one each. - /// - /// MasterTo1: Manufacture `num` transactions from the master account - /// to exactly one other randomly created account. - /// - /// MasterToNToM: Manufacture `num` transactions from the master account - /// to `num` randomly created accounts. - /// From each of these randomly created accounts manufacture - /// a transaction to another randomly created account. - /// Repeat this `rounds` times. If `rounds` = 1 the behavior - /// is the same as `MasterToN`.{n} - /// A -> B, A -> C, A -> D, ... x `num`{n} - /// B -> E, C -> F, D -> G, ...{n} - /// ... x `rounds` - /// - /// These three modes control manufacturing. - #[structopt(long="mode", default_value = "MasterToN")] - pub mode: node_transaction_factory::Mode, + /// Number of blocks to generate. + #[structopt(long="blocks", default_value = "1")] + pub blocks: u32, - /// Number of transactions to generate. In mode `MasterNToNToM` this is - /// the number of transactions per round. - #[structopt(long="num", default_value = "8")] - pub num: u64, + /// Number of transactions to push per block. + #[structopt(long="transactions", default_value = "8")] + pub transactions: u32, #[allow(missing_docs)] #[structopt(flatten)] @@ -87,123 +68,3 @@ pub struct FactoryCmd { #[structopt(flatten)] pub import_params: ImportParams, } - -/// Parse command line arguments into service configuration. -pub fn run(args: I, exit: E, version: sc_cli::VersionInfo) -> error::Result<()> where - I: IntoIterator, - T: Into + Clone, - E: IntoExit, -{ - type Config = Configuration<(), A, B>; - - match parse_and_prepare::(&version, "substrate-node", args) { - ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit, - |exit, _cli_args, _custom_args, mut config: Config<_, _>| { - info!("{}", version.name); - info!(" version {}", config.full_version()); - info!(" by Parity Technologies, 2017-2019"); - info!("Chain specification: {}", config.chain_spec.name()); - info!("Node name: {}", config.name); - info!("Roles: {}", display_role(&config)); - let runtime = RuntimeBuilder::new() - .thread_name("main-tokio-") - .threaded_scheduler() - .enable_all() - .build() - .map_err(|e| format!("{:?}", e))?; - config.tasks_executor = { - let runtime_handle = runtime.handle().clone(); - Some(Box::new(move |fut| { runtime_handle.spawn(fut); })) - }; - match config.roles { - ServiceRoles::LIGHT => run_until_exit( - runtime, - service::new_light(config)?, - exit - ), - _ => run_until_exit( - runtime, - service::new_full(config)?, - exit - ), - } - }), - ParseAndPrepare::BuildSpec(cmd) => cmd.run::(load_spec), - ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_, _>| - Ok(new_full_start!(config).0), load_spec, exit), - ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_, _>| - Ok(new_full_start!(config).0), load_spec, exit), - ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder(|config: Config<_, _>| - Ok(new_full_start!(config).0), load_spec, exit), - ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec), - ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_, _>| - Ok(new_full_start!(config).0), load_spec), - ParseAndPrepare::CustomCommand(CustomSubcommands::Factory(cli_args)) => { - let mut config: Config<_, _> = sc_cli::create_config_with_db_path( - load_spec, - &cli_args.shared_params, - &version, - None, - )?; - sc_cli::fill_import_params( - &mut config, - &cli_args.import_params, - ServiceRoles::FULL, - cli_args.shared_params.dev, - )?; - - match ChainSpec::from(config.chain_spec.id()) { - Some(ref c) if c == &ChainSpec::Development || c == &ChainSpec::LocalTestnet => {}, - _ => panic!("Factory is only supported for development and local testnet."), - } - - let factory_state = FactoryState::new( - cli_args.mode.clone(), - cli_args.num, - cli_args.rounds, - ); - - let service_builder = new_full_start!(config).0; - node_transaction_factory::factory::, _, _, _, _, _>( - factory_state, - service_builder.client(), - service_builder.select_chain() - .expect("The select_chain is always initialized by new_full_start!; QED") - ).map_err(|e| format!("Error in transaction factory: {}", e))?; - - Ok(()) - } - } -} - -fn run_until_exit( - mut runtime: Runtime, - service: T, - e: E, -) -> error::Result<()> -where - T: AbstractService, - E: IntoExit, -{ - let (exit_send, exit) = oneshot::channel(); - - let informant = sc_cli::informant::build(&service); - - let handle = runtime.spawn(select(exit, informant)); - - // we eagerly drop the service so that the internal exit future is fired, - // but we need to keep holding a reference to the global telemetry guard - let _telemetry = service.telemetry(); - - let exit = e.into_exit(); - let service_res = runtime.block_on(select(service, exit)); - - let _ = exit_send.send(()); - - runtime.block_on(handle); - - match service_res { - Either::Left((res, _)) => res.map_err(error::Error::Service), - Either::Right((_, _)) => Ok(()) - } -} diff --git a/bin/node/cli/src/command.rs b/bin/node/cli/src/command.rs new file mode 100644 index 0000000000000000000000000000000000000000..61d10517966ed2ef893acd607823cf83fb66e4c0 --- /dev/null +++ b/bin/node/cli/src/command.rs @@ -0,0 +1,92 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use sc_cli::{VersionInfo, error}; +use sc_service::{Roles as ServiceRoles}; +use node_transaction_factory::RuntimeAdapter; +use crate::{Cli, service, ChainSpec, load_spec, Subcommand, factory_impl::FactoryState}; + +/// Parse command line arguments into service configuration. +pub fn run(args: I, version: VersionInfo) -> error::Result<()> +where + I: Iterator, + T: Into + Clone, +{ + let args: Vec<_> = args.collect(); + let opt = sc_cli::from_iter::(args.clone(), &version); + + let mut config = sc_service::Configuration::new(&version); + + match opt.subcommand { + None => sc_cli::run( + config, + opt.run, + service::new_light, + service::new_full, + load_spec, + &version, + ), + Some(Subcommand::Factory(cli_args)) => { + sc_cli::init(&cli_args.shared_params, &version)?; + sc_cli::init_config(&mut config, &cli_args.shared_params, &version, load_spec)?; + sc_cli::fill_import_params( + &mut config, + &cli_args.import_params, + ServiceRoles::FULL, + cli_args.shared_params.dev, + )?; + + sc_cli::fill_config_keystore_in_memory(&mut config)?; + + match ChainSpec::from(config.expect_chain_spec().id()) { + Some(ref c) if c == &ChainSpec::Development || c == &ChainSpec::LocalTestnet => {}, + _ => panic!("Factory is only supported for development and local testnet."), + } + + // Setup tracing. + if let Some(tracing_targets) = cli_args.import_params.tracing_targets.as_ref() { + let subscriber = sc_tracing::ProfilingSubscriber::new( + cli_args.import_params.tracing_receiver.into(), tracing_targets + ); + if let Err(e) = tracing::subscriber::set_global_default(subscriber) { + panic!("Unable to set global default subscriber {}", e); + } + } + + let factory_state = FactoryState::new( + cli_args.blocks, + cli_args.transactions, + ); + + let service_builder = new_full_start!(config).0; + node_transaction_factory::factory::, _, _, _, _, _>( + factory_state, + service_builder.client(), + service_builder.select_chain() + .expect("The select_chain is always initialized by new_full_start!; QED") + ).map_err(|e| format!("Error in transaction factory: {}", e))?; + + Ok(()) + }, + Some(Subcommand::Base(subcommand)) => sc_cli::run_subcommand( + config, + subcommand, + load_spec, + |config: service::NodeConfiguration| Ok(new_full_start!(config).0), + &version, + ), + } +} diff --git a/bin/node/cli/src/factory_impl.rs b/bin/node/cli/src/factory_impl.rs index a1c5a5f4e050f5dbf8db06000f2d691225ab6eca..1d1eabe29cbfb12ca43663be00b0969e852a30d9 100644 --- a/bin/node/cli/src/factory_impl.rs +++ b/bin/node/cli/src/factory_impl.rs @@ -33,7 +33,6 @@ use sp_runtime::{ generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension, Verify, IdentifyAccount} }; use node_transaction_factory::RuntimeAdapter; -use node_transaction_factory::modes::Mode; use sp_inherents::InherentData; use sp_timestamp; use sp_finality_tracker; @@ -41,14 +40,10 @@ use sp_finality_tracker; type AccountPublic = ::Signer; pub struct FactoryState { - block_no: N, - - mode: Mode, - start_number: u32, - rounds: u32, - round: u32, - block_in_round: u32, - num: u32, + blocks: u32, + transactions: u32, + block_number: N, + index: u32, } type Number = <::Header as HeaderT>::Number; @@ -78,63 +73,35 @@ impl RuntimeAdapter for FactoryState { type Number = Number; fn new( - mode: Mode, - num: u64, - rounds: u64, + blocks: u32, + transactions: u32, ) -> FactoryState { FactoryState { - mode, - num: num as u32, - round: 0, - rounds: rounds as u32, - block_in_round: 0, - block_no: 0, - start_number: 0, + blocks, + transactions, + block_number: 0, + index: 0, } } - fn block_no(&self) -> Self::Number { - self.block_no - } - - fn block_in_round(&self) -> Self::Number { - self.block_in_round - } - - fn rounds(&self) -> Self::Number { - self.rounds - } - - fn num(&self) -> Self::Number { - self.num - } - - fn round(&self) -> Self::Number { - self.round - } - - fn start_number(&self) -> Self::Number { - self.start_number - } - - fn mode(&self) -> &Mode { - &self.mode + fn block_number(&self) -> u32 { + self.block_number } - fn set_block_no(&mut self, val: Self::Number) { - self.block_no = val; + fn blocks(&self) -> u32 { + self.blocks } - fn set_block_in_round(&mut self, val: Self::Number) { - self.block_in_round = val; + fn transactions(&self) -> u32 { + self.transactions } - fn set_round(&mut self, val: Self::Number) { - self.round = val; + fn set_block_number(&mut self, value: u32) { + self.block_number = value; } fn transfer_extrinsic( - &self, + &mut self, sender: &Self::AccountId, key: &Self::Secret, destination: &Self::AccountId, @@ -143,10 +110,12 @@ impl RuntimeAdapter for FactoryState { genesis_hash: &::Hash, prior_block_hash: &::Hash, ) -> ::Extrinsic { - let index = self.extract_index(&sender, prior_block_hash); - let phase = self.extract_phase(*prior_block_hash); + let phase = self.block_number() as Self::Phase; + let extra = Self::build_extra(self.index, phase); + self.index += 1; + sign::(CheckedExtrinsic { - signed: Some((sender.clone(), Self::build_extra(index, phase))), + signed: Some((sender.clone(), extra)), function: Call::Balances( BalancesCall::transfer( pallet_indices::address::Address::Id(destination.clone().into()), @@ -157,12 +126,12 @@ impl RuntimeAdapter for FactoryState { } fn inherent_extrinsics(&self) -> InherentData { - let timestamp = (self.block_no as u64 + 1) * MinimumPeriod::get(); + let timestamp = (self.block_number as u64 + 1) * MinimumPeriod::get(); let mut inherent = InherentData::new(); inherent.put_data(sp_timestamp::INHERENT_IDENTIFIER, ×tamp) .expect("Failed putting timestamp inherent"); - inherent.put_data(sp_finality_tracker::INHERENT_IDENTIFIER, &self.block_no) + inherent.put_data(sp_finality_tracker::INHERENT_IDENTIFIER, &self.block_number) .expect("Failed putting finalized number inherent"); inherent } @@ -180,49 +149,16 @@ impl RuntimeAdapter for FactoryState { } /// Generates a random `AccountId` from `seed`. - fn gen_random_account_id(seed: &Self::Number) -> Self::AccountId { - let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed)); + fn gen_random_account_id(seed: u32) -> Self::AccountId { + let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(seed)); AccountPublic::from(pair.public()).into_account() } /// Generates a random `Secret` from `seed`. - fn gen_random_account_secret(seed: &Self::Number) -> Self::Secret { - let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed)); + fn gen_random_account_secret(seed: u32) -> Self::Secret { + let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(seed)); pair } - - fn extract_index( - &self, - _account_id: &Self::AccountId, - _block_hash: &::Hash, - ) -> Self::Index { - // TODO get correct index for account via api. See #2587. - // This currently prevents the factory from being used - // without a preceding purge of the database. - if self.mode == Mode::MasterToN || self.mode == Mode::MasterTo1 { - self.block_no() as Self::Index - } else { - match self.round() { - 0 => - // if round is 0 all transactions will be done with master as a sender - self.block_no() as Self::Index, - _ => - // if round is e.g. 1 every sender account will be new and not yet have - // any transactions done - 0 - } - } - } - - fn extract_phase( - &self, - _block_hash: ::Hash - ) -> Self::Phase { - // TODO get correct phase via api. See #2587. - // This currently prevents the factory from being used - // without a preceding purge of the database. - self.block_no() as Self::Phase - } } fn gen_seed_bytes(seed: u32) -> [u8; 32] { diff --git a/bin/node/cli/src/lib.rs b/bin/node/cli/src/lib.rs index d3fbf9c1ea5cd6e166b3cd02b42f5cef304a9d9a..f5b915a2bedd611904ecee96cf8f24f01140d881 100644 --- a/bin/node/cli/src/lib.rs +++ b/bin/node/cli/src/lib.rs @@ -39,11 +39,15 @@ mod browser; mod cli; #[cfg(feature = "cli")] mod factory_impl; +#[cfg(feature = "cli")] +mod command; #[cfg(feature = "browser")] pub use browser::*; #[cfg(feature = "cli")] pub use cli::*; +#[cfg(feature = "cli")] +pub use command::*; /// The chain specification option. #[derive(Clone, Debug, PartialEq)] diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index b12dfcdc515f5e188093d3843305cb28c46c8a52..a88f2d1add5f356ffc771f17143407b72397aa40 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -63,10 +63,8 @@ macro_rules! new_full_start { })? .with_transaction_pool(|config, client, _fetcher| { let pool_api = sc_transaction_pool::FullChainApi::new(client.clone()); - let pool = sc_transaction_pool::BasicPool::new(config, pool_api); - let maintainer = sc_transaction_pool::FullBasicPoolMaintainer::new(pool.pool().clone(), client); - let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer); - Ok(maintainable_pool) + let pool = sc_transaction_pool::BasicPool::new(config, std::sync::Arc::new(pool_api)); + Ok(pool) })? .with_import_queue(|_config, client, mut select_chain, _transaction_pool| { let select_chain = select_chain.take() @@ -112,10 +110,7 @@ macro_rules! new_full_start { /// concrete types instead. macro_rules! new_full { ($config:expr, $with_startup_data: expr) => {{ - use futures::{ - prelude::*, - compat::Future01CompatExt - }; + use futures::prelude::*; use sc_network::Event; let ( @@ -177,7 +172,7 @@ macro_rules! new_full { }; let babe = sc_consensus_babe::start_babe(babe_config)?; - service.spawn_essential_task(babe); + service.spawn_essential_task("babe-proposer", babe); let network = service.network(); let dht_event_stream = network.event_stream().filter_map(|e| async move { match e { @@ -192,7 +187,7 @@ macro_rules! new_full { dht_event_stream, ); - service.spawn_task(authority_discovery); + service.spawn_task("authority-discovery", authority_discovery); } // if the node isn't actively participating in consensus then it doesn't @@ -216,13 +211,12 @@ macro_rules! new_full { match (is_authority, disable_grandpa) { (false, false) => { // start the lightweight GRANDPA observer - service.spawn_task(grandpa::run_grandpa_observer( + service.spawn_task("grandpa-observer", grandpa::run_grandpa_observer( config, grandpa_link, service.network(), service.on_exit(), - service.spawn_task_handle(), - )?.compat().map(drop)); + )?); }, (true, false) => { // start the full GRANDPA voter @@ -234,12 +228,12 @@ macro_rules! new_full { on_exit: service.on_exit(), telemetry_on_connect: Some(service.telemetry_on_connect_stream()), voting_rule: grandpa::VotingRulesBuilder::default().build(), - executor: service.spawn_task_handle(), }; // the GRANDPA voter task is considered infallible, i.e. // if it fails we take down the service with it. service.spawn_essential_task( - grandpa::run_grandpa_voter(grandpa_config)?.compat().map(drop) + "grandpa-voter", + grandpa::run_grandpa_voter(grandpa_config)? ); }, (_, true) => { @@ -272,22 +266,16 @@ type ConcreteClient = #[allow(dead_code)] type ConcreteBackend = Backend; #[allow(dead_code)] -type ConcreteTransactionPool = sp_transaction_pool::MaintainableTransactionPool< - sc_transaction_pool::BasicPool< - sc_transaction_pool::FullChainApi, - ConcreteBlock - >, - sc_transaction_pool::FullBasicPoolMaintainer< - ConcreteClient, - sc_transaction_pool::FullChainApi - > +type ConcreteTransactionPool = sc_transaction_pool::BasicPool< + sc_transaction_pool::FullChainApi, + ConcreteBlock >; /// A specialized configuration object for setting up the node.. -pub type NodeConfiguration = Configuration; +pub type NodeConfiguration = Configuration; /// Builds a new service for a full client. -pub fn new_full(config: NodeConfiguration) +pub fn new_full(config: NodeConfiguration) -> Result< Service< ConcreteBlock, @@ -309,7 +297,7 @@ pub fn new_full(config: NodeConfiguration) } /// Builds a new service for a light client. -pub fn new_light(config: NodeConfiguration) +pub fn new_light(config: NodeConfiguration) -> Result { type RpcExtension = jsonrpc_core::IoHandler; let inherent_data_providers = InherentDataProviders::new(); @@ -322,10 +310,10 @@ pub fn new_light(config: NodeConfiguration) let fetcher = fetcher .ok_or_else(|| "Trying to start light transaction pool without active fetcher")?; let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone()); - let pool = sc_transaction_pool::BasicPool::new(config, pool_api); - let maintainer = sc_transaction_pool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher); - let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer); - Ok(maintainable_pool) + let pool = sc_transaction_pool::BasicPool::with_revalidation_type( + config, Arc::new(pool_api), sc_transaction_pool::RevalidationType::Light, + ); + Ok(pool) })? .with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| { let fetch_checker = fetcher @@ -381,8 +369,11 @@ pub fn new_light(config: NodeConfiguration) #[cfg(test)] mod tests { - use std::sync::Arc; - use sc_consensus_babe::CompatibleDigestItem; + use std::{sync::Arc, collections::HashMap, borrow::Cow, any::Any}; + use sc_consensus_babe::{ + CompatibleDigestItem, BabeIntermediate, INTERMEDIATE_KEY + }; + use sc_consensus_epochs::descendent_query; use sp_consensus::{ Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy, BlockImport, RecordProof, @@ -394,7 +385,7 @@ mod tests { use sp_core::{crypto::Pair as CryptoPair, H256}; use sp_runtime::{ generic::{BlockId, Era, Digest, SignedPayload}, - traits::Block as BlockT, + traits::{Block as BlockT, Header as HeaderT}, traits::Verify, OpaqueExtrinsic, }; @@ -509,11 +500,21 @@ mod tests { let parent_id = BlockId::number(service.client().chain_info().best_number); let parent_header = service.client().header(&parent_id).unwrap().unwrap(); + let parent_hash = parent_header.hash(); + let parent_number = *parent_header.number(); let mut proposer_factory = sc_basic_authorship::ProposerFactory { client: service.client(), transaction_pool: service.transaction_pool(), }; + let epoch = babe_link.epoch_changes().lock().epoch_for_child_of( + descendent_query(&*service.client()), + &parent_hash, + parent_number, + slot_num, + |slot| babe_link.config().genesis_epoch(slot) + ).unwrap().unwrap(); + let mut digest = Digest::::default(); // even though there's only one authority some slots might be empty, @@ -565,7 +566,15 @@ mod tests { storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: { + let mut intermediates = HashMap::new(); + intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + intermediates + }, + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }; diff --git a/bin/node/transaction-factory/src/modes.rs b/bin/node/cli/tests/common.rs similarity index 52% rename from bin/node/transaction-factory/src/modes.rs rename to bin/node/cli/tests/common.rs index 5deab7635e114c16469fd3babbda2d1004b20641..96060bf85d9f1917917a6c498918b7b5c822c0a4 100644 --- a/bin/node/transaction-factory/src/modes.rs +++ b/bin/node/cli/tests/common.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,27 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! The transaction factory can operate in different modes. See -//! the `simple_mode` and `complex_mode` modules for details. +use std::{process::{Child, ExitStatus}, thread, time::Duration}; -use std::str::FromStr; - -/// Token distribution modes. -#[derive(Debug, Clone, PartialEq)] -pub enum Mode { - MasterToN, - MasterTo1, - MasterToNToM -} - -impl FromStr for Mode { - type Err = String; - fn from_str(mode: &str) -> Result { - match mode { - "MasterToN" => Ok(Mode::MasterToN), - "MasterTo1" => Ok(Mode::MasterTo1), - "MasterToNToM" => Ok(Mode::MasterToNToM), - _ => Err(format!("Invalid mode: {}", mode)), +/// Wait for the given `child` the given ammount of `secs`. +/// +/// Returns the `Some(exit status)` or `None` if the process did not finish in the given time. +pub fn wait_for(child: &mut Child, secs: usize) -> Option { + for _ in 0..secs { + match child.try_wait().unwrap() { + Some(status) => return Some(status), + None => thread::sleep(Duration::from_secs(1)), } } + eprintln!("Took to long to exit. Killing..."); + let _ = child.kill(); + child.wait().unwrap(); + + None } diff --git a/bin/node/cli/tests/purge_chain_works.rs b/bin/node/cli/tests/purge_chain_works.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6b71db9910487a4e55f2fdf117e48c833b120bb --- /dev/null +++ b/bin/node/cli/tests/purge_chain_works.rs @@ -0,0 +1,53 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use assert_cmd::cargo::cargo_bin; +use std::{convert::TryInto, process::Command, thread, time::Duration, fs, path::PathBuf}; + +mod common; + +#[test] +#[cfg(unix)] +fn purge_chain_works() { + use nix::sys::signal::{kill, Signal::SIGINT}; + use nix::unistd::Pid; + + let base_path = "purge_chain_test"; + + let _ = fs::remove_dir_all(base_path); + let mut cmd = Command::new(cargo_bin("substrate")) + .args(&["--dev", "-d", base_path]) + .spawn() + .unwrap(); + + // Let it produce some blocks. + thread::sleep(Duration::from_secs(30)); + assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running"); + + // Stop the process + kill(Pid::from_raw(cmd.id().try_into().unwrap()), SIGINT).unwrap(); + assert!(common::wait_for(&mut cmd, 30).map(|x| x.success()).unwrap_or_default()); + + let status = Command::new(cargo_bin("substrate")) + .args(&["purge-chain", "--dev", "-d", base_path, "-y"]) + .status() + .unwrap(); + assert!(status.success()); + + // Make sure that the `dev` chain folder exists, but the `db` is deleted. + assert!(PathBuf::from(base_path).join("chains/dev/").exists()); + assert!(!PathBuf::from(base_path).join("chains/dev/db").exists()); +} diff --git a/bin/node/cli/tests/running_the_node_and_interrupt.rs b/bin/node/cli/tests/running_the_node_and_interrupt.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ab719de9196d89afb60c9f8affe26ceb685ffc7 --- /dev/null +++ b/bin/node/cli/tests/running_the_node_and_interrupt.rs @@ -0,0 +1,48 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use assert_cmd::cargo::cargo_bin; +use std::{convert::TryInto, process::Command, thread, time::Duration, fs}; + +mod common; + +#[test] +#[cfg(unix)] +fn running_the_node_works_and_can_be_interrupted() { + use nix::sys::signal::{kill, Signal::{self, SIGINT, SIGTERM}}; + use nix::unistd::Pid; + + fn run_command_and_kill(signal: Signal) { + let _ = fs::remove_dir_all("interrupt_test"); + let mut cmd = Command::new(cargo_bin("substrate")) + .args(&["--dev", "-d", "interrupt_test"]) + .spawn() + .unwrap(); + + thread::sleep(Duration::from_secs(30)); + assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running"); + kill(Pid::from_raw(cmd.id().try_into().unwrap()), signal).unwrap(); + assert_eq!( + common::wait_for(&mut cmd, 30).map(|x| x.success()), + Some(true), + "the pocess must exit gracefully after signal {}", + signal, + ); + } + + run_command_and_kill(SIGINT); + run_command_and_kill(SIGTERM); +} diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 6e624e09d28dde021e163dfd71476ee695e0e176..1d894e39fa978599d973824c2513dd96dabb62fd 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] description = "Substrate node implementation in Rust." edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } @@ -14,7 +15,7 @@ sp-core = { version = "2.0.0", path = "../../../primitives/core" } sp-io = { version = "2.0.0", path = "../../../primitives/io" } sp-state-machine = { version = "0.8", path = "../../../primitives/state-machine" } sp-trie = { version = "2.0.0", path = "../../../primitives/trie" } -trie-root = "0.15.2" +trie-root = "0.16.0" [dev-dependencies] criterion = "0.3.0" diff --git a/bin/node/executor/src/lib.rs b/bin/node/executor/src/lib.rs index 812c018502f1b606cbd3db366420ae64f34ab6e4..72f40b7c1f0cd6c1984b39a914ba841452708d27 100644 --- a/bin/node/executor/src/lib.rs +++ b/bin/node/executor/src/lib.rs @@ -25,5 +25,6 @@ use sc_executor::native_executor_instance; native_executor_instance!( pub Executor, node_runtime::api::dispatch, - node_runtime::native_version + node_runtime::native_version, + sp_io::benchmarking::HostFunctions, ); diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 817ebbb173933a8fbcbbc55df53a4e71298fea71..100bdf3fe60ee8a90a557e4b174c6e76cc712b60 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -35,8 +35,7 @@ use frame_system::{self, EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, - System, TransactionPayment, Event, - TransferFee, TransactionBaseFee, TransactionByteFee, + System, TransactionPayment, Event, TransactionBaseFee, TransactionByteFee, constants::currency::*, }; use node_primitives::{Balance, Hash}; @@ -62,7 +61,7 @@ fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { ::WeightToFee::convert(weight); let base_fee = TransactionBaseFee::get(); - base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() + base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) } fn xt() -> UncheckedExtrinsic { @@ -164,15 +163,12 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { fn panic_execution_with_foreign_code_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - 69_u128.encode() + >::hashed_key_for(alice()) => { + (69u128, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { 69_u128.encode() }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, >::hashed_key_for(0) => { vec![0u8; 32] } @@ -203,15 +199,12 @@ fn panic_execution_with_foreign_code_gives_error() { fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - 69_u128.encode() + >::hashed_key_for(alice()) => { + (0u32, 69u128, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { 69_u128.encode() }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, >::hashed_key_for(0) => { vec![0u8; 32] } @@ -242,13 +235,12 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() { fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() + >::hashed_key_for(alice()) => { + (0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { (111 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -275,7 +267,8 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { assert!(r.is_ok()); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + let fees = transfer_fee(&xt(), fm); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -284,13 +277,12 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { fn successful_execution_with_foreign_code_gives_ok() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() + >::hashed_key_for(alice()) => { + (0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { (111 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -317,7 +309,8 @@ fn successful_execution_with_foreign_code_gives_ok() { assert!(r.is_ok()); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + let fees = transfer_fee(&xt(), fm); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -340,13 +333,14 @@ fn full_native_block_import_works() { ).0.unwrap(); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + let fees = transfer_fee(&xt(), fm); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); alice_last_known_balance = Balances::total_balance(&alice()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } )), topics: vec![], @@ -362,13 +356,12 @@ fn full_native_block_import_works() { alice().into(), bob().into(), 69 * DOLLARS, - 1 * CENTS, )), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -399,7 +392,7 @@ fn full_native_block_import_works() { let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } )), topics: vec![], @@ -416,14 +409,13 @@ fn full_native_block_import_works() { bob().into(), alice().into(), 5 * DOLLARS, - 1 * CENTS, ) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -440,14 +432,13 @@ fn full_native_block_import_works() { alice().into(), bob().into(), 15 * DOLLARS, - 1 * CENTS, ) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -710,13 +701,9 @@ fn native_big_block_import_fails_on_fallback() { fn panic_execution_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - 0_u128.encode() - }, >::hashed_key().to_vec() => { 0_u128.encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -745,13 +732,12 @@ fn panic_execution_gives_error() { fn successful_execution_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS).encode() + >::hashed_key_for(alice()) => { + (0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { (111 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -775,11 +761,12 @@ fn successful_execution_gives_ok() { ).0.unwrap().into_encoded(); ApplyExtrinsicResult::decode(&mut &r[..]) .unwrap() - .expect("Extrinsic could be applied") - .expect("Extrinsic did not fail"); + .expect("Extrinsic could not be applied") + .expect("Extrinsic failed"); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt(), fm)); + let fees = transfer_fee(&xt(), fm); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } diff --git a/bin/node/executor/tests/common.rs b/bin/node/executor/tests/common.rs index 52f8b6565409605998e07874d9a9a7052f63c8b8..090d2ee5d4ace1b188641536e0732033fa6d0f34 100644 --- a/bin/node/executor/tests/common.rs +++ b/bin/node/executor/tests/common.rs @@ -21,7 +21,7 @@ use sp_core::{ Blake2Hasher, NeverNativeValue, NativeOrEncoded, traits::CodeExecutor, }; -use sp_runtime::traits::Header as HeaderT; +use sp_runtime::{ApplyExtrinsicResult, traits::Header as HeaderT}; use sc_executor::{NativeExecutor, WasmExecutionMethod}; use sc_executor::error::Result; @@ -92,6 +92,10 @@ pub fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalitie ext } +/// Construct a fake block. +/// +/// `extrinsics` must be a list of valid extrinsics, i.e. none of the extrinsics for example +/// can report `ExhaustResources`. Otherwise, this function panics. pub fn construct_block( env: &mut TestExternalities, number: BlockNumber, @@ -104,10 +108,10 @@ pub fn construct_block( let extrinsics = extrinsics.into_iter().map(sign).collect::>(); // calculate the header fields that we can. - let extrinsics_root = Layout::::ordered_trie_root( - extrinsics.iter().map(Encode::encode) - ).to_fixed_bytes() - .into(); + let extrinsics_root = + Layout::::ordered_trie_root(extrinsics.iter().map(Encode::encode)) + .to_fixed_bytes() + .into(); let header = Header { parent_hash, @@ -126,14 +130,20 @@ pub fn construct_block( None, ).0.unwrap(); - for i in extrinsics.iter() { - executor_call:: _>( + for extrinsic in extrinsics.iter() { + // Try to apply the `extrinsic`. It should be valid, in the sense that it passes + // all pre-inclusion checks. + let r = executor_call:: _>( env, "BlockBuilder_apply_extrinsic", - &i.encode(), + &extrinsic.encode(), true, None, - ).0.unwrap(); + ).0.expect("application of an extrinsic failed").into_encoded(); + match ApplyExtrinsicResult::decode(&mut &r[..]).expect("apply result deserialization failed") { + Ok(_) => {}, + Err(e) => panic!("Applying extrinsic failed: {:?}", e), + } } let header = match executor_call:: _>( diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index ca34452031780c96ecebcc9019b83e0a7be82114..ba303a6feb6ff05698f59de15485f65d9beb999c 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -30,7 +30,7 @@ use sp_runtime::{ }; use node_runtime::{ CheckedExtrinsic, Call, Runtime, Balances, - TransactionPayment, TransferFee, TransactionBaseFee, TransactionByteFee, + TransactionPayment, TransactionBaseFee, TransactionByteFee, WeightFeeCoefficient, constants::currency::*, }; use node_runtime::impls::LinearWeightToFee; @@ -134,16 +134,15 @@ fn transaction_fee_is_correct_ultimate() { // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (100 * DOLLARS).encode() + >::hashed_key_for(alice()) => { + (0u32, 100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() }, - >::hashed_key_for(bob()) => { - (10 * DOLLARS).encode() + >::hashed_key_for(bob()) => { + (0u32, 10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() }, >::hashed_key().to_vec() => { (110 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -193,9 +192,7 @@ fn transaction_fee_is_correct_ultimate() { // we know that weight to fee multiplier is effect-less in block 1. assert_eq!(weight_fee as Balance, MILLICENTS); balance_alice -= weight_fee; - balance_alice -= tip; - balance_alice -= TransferFee::get(); assert_eq!(Balances::total_balance(&alice()), balance_alice); }); diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 9e91ffc76b38f33aca5de2a86c40d0e3652db1a3..b870cf40370f1ad8fde950f607cfc86159e6aefd 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -167,7 +167,8 @@ fn submitted_transaction_should_be_valid() { // add balance to the account let author = extrinsic.signature.clone().unwrap().0; let address = Indices::lookup(author).unwrap(); - >::insert(&address, 5_000_000_000_000); + let account = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() }; + >::insert(&address, (0u32, account)); // check validity let res = Executive::validate_transaction(extrinsic); diff --git a/bin/node/primitives/Cargo.toml b/bin/node/primitives/Cargo.toml index 1ecfd76792cc628f8c1040c8b0f29901886a18ee..5fc6ce8f101bdb807481917dcd65e679ab851cb5 100644 --- a/bin/node/primitives/Cargo.toml +++ b/bin/node/primitives/Cargo.toml @@ -3,6 +3,7 @@ name = "node-primitives" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } diff --git a/bin/node/rpc-client/Cargo.toml b/bin/node/rpc-client/Cargo.toml index 5ef9d1e99d16c1189d8b89be507e38589cd977fa..0d8106dceed7fff28e6d7bf1f7006a77362af920 100644 --- a/bin/node/rpc-client/Cargo.toml +++ b/bin/node/rpc-client/Cargo.toml @@ -3,6 +3,7 @@ name = "node-rpc-client" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] env_logger = "0.7.0" diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 174b2875166c2940720844d51a75f9617da26843..289d20ea018ac46c5d5c729dd97067aa334168b5 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -3,6 +3,7 @@ name = "node-rpc" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-client = { version = "0.8", path = "../../../client/" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index a473b43a7f189e8812b0bda939ef4ead6d0fe26f..4bf0338088a9f0cc836a0b3849d14115d5058d09 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -31,7 +31,7 @@ use std::sync::Arc; -use node_primitives::{Block, AccountId, Index, Balance}; +use node_primitives::{Block, BlockNumber, AccountId, Index, Balance}; use node_runtime::UncheckedExtrinsic; use sp_api::ProvideRuntimeApi; use sp_transaction_pool::TransactionPool; @@ -66,7 +66,7 @@ pub fn create( C: sc_client::blockchain::HeaderBackend, C: Send + Sync + 'static, C::Api: substrate_frame_rpc_system::AccountNonceApi, - C::Api: pallet_contracts_rpc::ContractsRuntimeApi, + C::Api: pallet_contracts_rpc::ContractsRuntimeApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, F: sc_client::light::fetcher::Fetcher + 'static, P: TransactionPool + 'static, diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 27b1e9382f933d0ca239c6873b06097a8d509fd6..3f8e8b6731477e4cbaabaf4e3012bb139d259e6d 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -4,8 +4,10 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" +license = "GPL-3.0" [dependencies] + # third-party dependencies codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] } integer-sqrt = { version = "0.1.2" } @@ -40,6 +42,7 @@ pallet-babe = { version = "2.0.0", default-features = false, path = "../../../fr pallet-balances = { version = "2.0.0", default-features = false, path = "../../../frame/balances" } pallet-collective = { version = "2.0.0", default-features = false, path = "../../../frame/collective" } pallet-contracts = { version = "2.0.0", default-features = false, path = "../../../frame/contracts" } +pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "../../../frame/contracts/common/" } pallet-contracts-rpc-runtime-api = { version = "0.8.0", default-features = false, path = "../../../frame/contracts/rpc/runtime-api/" } pallet-democracy = { version = "2.0.0", default-features = false, path = "../../../frame/democracy" } pallet-elections-phragmen = { version = "2.0.0", default-features = false, path = "../../../frame/elections-phragmen" } @@ -62,6 +65,7 @@ pallet-treasury = { version = "2.0.0", default-features = false, path = "../../. pallet-utility = { version = "2.0.0", default-features = false, path = "../../../frame/utility" } pallet-transaction-payment = { version = "2.0.0", default-features = false, path = "../../../frame/transaction-payment" } pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "../../../frame/transaction-payment/rpc/runtime-api/" } +pallet-vesting = { version = "2.0.0", default-features = false, path = "../../../frame/vesting" } [build-dependencies] wasm-builder-runner = { version = "1.0.4", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } @@ -81,8 +85,9 @@ std = [ "sp-block-builder/std", "codec/std", "pallet-collective/std", - "pallet-contracts-rpc-runtime-api/std", "pallet-contracts/std", + "pallet-contracts-primitives/std", + "pallet-contracts-rpc-runtime-api/std", "pallet-democracy/std", "pallet-elections-phragmen/std", "frame-executive/std", @@ -121,4 +126,5 @@ std = [ "sp-version/std", "pallet-society/std", "pallet-recovery/std", + "pallet-vesting/std", ] diff --git a/bin/node/runtime/build.rs b/bin/node/runtime/build.rs index 9c81ea6f38b8df4812805e75501414c862e50d11..647b4768141d2203155bf5325f9ff87f7d9c2446 100644 --- a/bin/node/runtime/build.rs +++ b/bin/node/runtime/build.rs @@ -14,17 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; +use wasm_builder_runner::WasmBuilder; fn main() { - build_current_project_with_rustflags( - "wasm_binary.rs", - WasmBuilderSource::CratesOrPath { - path: "../../../utils/wasm-builder", - version: "1.0.9", - }, - // This instructs LLD to export __heap_base as a global variable, which is used by the - // external memory allocator. - "-Clink-arg=--export=__heap_base", - ); + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_from_crates_or_path("1.0.9", "../../../utils/wasm-builder") + .export_heap_base() + .import_memory() + .build() } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e158cbe2cbb8848d4198e7d17165f3dd7017dbd3..cdcf670c640c6b3b1566fb20246c96956491e7a6 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -27,16 +27,18 @@ use frame_support::{ traits::{SplitTwoWays, Currency, Randomness}, }; use sp_core::u32_trait::{_1, _2, _3, _4}; -use node_primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Moment, Signature}; +pub use node_primitives::{AccountId, Signature}; +use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use sp_api::impl_runtime_apis; use sp_runtime::{ - Permill, Perbill, Percent, ApplyExtrinsicResult, impl_opaque_keys, generic, create_runtime_str + Permill, Perbill, Percent, ApplyExtrinsicResult, BenchmarkResults, + impl_opaque_keys, generic, create_runtime_str, }; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::transaction_validity::TransactionValidity; use sp_runtime::traits::{ self, BlakeTwo256, Block as BlockT, StaticLookup, SaturatedConversion, - OpaqueKeys, + ConvertInto, OpaqueKeys, Benchmarking, }; use sp_version::RuntimeVersion; #[cfg(any(feature = "std", test))] @@ -77,11 +79,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, // Per convention: if the runtime behavior changes, increment spec_version - // and set impl_version to equal spec_version. If only runtime + // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 204, - impl_version: 204, + spec_version: 218, + impl_version: 0, apis: RUNTIME_API_VERSIONS, }; @@ -128,6 +130,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = Version; type ModuleToIndex = ModuleToIndex; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Staking, Contracts, Session, Recovery); } parameter_types! { @@ -158,30 +163,27 @@ impl pallet_babe::Trait for Runtime { type EpochChangeTrigger = pallet_babe::ExternalTrigger; } +parameter_types! { + pub const IndexDeposit: Balance = 1 * DOLLARS; +} + impl pallet_indices::Trait for Runtime { type AccountIndex = AccountIndex; - type IsDeadAccount = Balances; - type ResolveHint = pallet_indices::SimpleResolveHint; type Event = Event; + type Currency = Balances; + type Deposit = IndexDeposit; } parameter_types! { pub const ExistentialDeposit: Balance = 1 * DOLLARS; - pub const TransferFee: Balance = 1 * CENTS; - pub const CreationFee: Balance = 1 * CENTS; } impl pallet_balances::Trait for Runtime { type Balance = Balance; - type OnFreeBalanceZero = ((Staking, Contracts), Session); - type OnReapAccount = (System, Recovery); - type OnNewAccount = Indices; - type Event = Event; type DustRemoval = (); - type TransferPayment = (); + type Event = Event; type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = frame_system::Module; } parameter_types! { @@ -236,13 +238,13 @@ parameter_types! { } impl pallet_session::Trait for Runtime { - type SessionManager = Staking; - type SessionHandler = ::KeyTypeIdProviders; - type ShouldEndSession = Babe; type Event = Event; - type Keys = SessionKeys; type ValidatorId = ::AccountId; type ValidatorIdOf = pallet_staking::StashOf; + type ShouldEndSession = Babe; + type SessionManager = Staking; + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; } @@ -304,7 +306,6 @@ impl pallet_democracy::Trait for Runtime { type EnactmentPeriod = EnactmentPeriod; type LaunchPeriod = LaunchPeriod; type VotingPeriod = VotingPeriod; - type EmergencyVotingPeriod = EmergencyVotingPeriod; type MinimumDeposit = MinimumDeposit; /// A straight majority of the council can decide what their next motion is. type ExternalOrigin = pallet_collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilCollective>; @@ -316,6 +317,7 @@ impl pallet_democracy::Trait for Runtime { /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote /// be tabled immediately and with a shorter voting/enactment period. type FastTrackOrigin = pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalCollective>; + type EmergencyVotingPeriod = EmergencyVotingPeriod; // To cancel a proposal which has been passed, 2/3 of the council must agree to it. type CancellationOrigin = pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilCollective>; // Any single technical committee member may veto a coming council proposal, however they can @@ -344,16 +346,16 @@ parameter_types! { impl pallet_elections_phragmen::Trait for Runtime { type Event = Event; type Currency = Balances; + type ChangeMembers = Council; type CurrencyToVote = CurrencyToVoteHandler; type CandidacyBond = CandidacyBond; type VotingBond = VotingBond; - type TermDuration = TermDuration; - type DesiredMembers = DesiredMembers; - type DesiredRunnersUp = DesiredRunnersUp; type LoserCandidate = (); type BadReport = (); type KickedMember = (); - type ChangeMembers = Council; + type DesiredMembers = DesiredMembers; + type DesiredRunnersUp = DesiredRunnersUp; + type TermDuration = TermDuration; } type TechnicalCollective = pallet_collective::Instance2; @@ -388,22 +390,20 @@ impl pallet_treasury::Trait for Runtime { type Currency = Balances; type ApproveOrigin = pallet_collective::EnsureMembers<_4, AccountId, CouncilCollective>; type RejectOrigin = pallet_collective::EnsureMembers<_2, AccountId, CouncilCollective>; + type Tippers = Elections; + type TipCountdown = TipCountdown; + type TipFindersFee = TipFindersFee; + type TipReportDepositBase = TipReportDepositBase; + type TipReportDepositPerByte = TipReportDepositPerByte; type Event = Event; type ProposalRejection = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; type SpendPeriod = SpendPeriod; type Burn = Burn; - type Tippers = Elections; - type TipCountdown = TipCountdown; - type TipFindersFee = TipFindersFee; - type TipReportDepositBase = TipReportDepositBase; - type TipReportDepositPerByte = TipReportDepositPerByte; } parameter_types! { - pub const ContractTransferFee: Balance = 1 * CENTS; - pub const ContractCreationFee: Balance = 1 * CENTS; pub const ContractTransactionBaseFee: Balance = 1 * CENTS; pub const ContractTransactionByteFee: Balance = 10 * MILLICENTS; pub const ContractFee: Balance = 1 * CENTS; @@ -430,8 +430,6 @@ impl pallet_contracts::Trait for Runtime { type RentByteFee = RentByteFee; type RentDepositOffset = RentDepositOffset; type SurchargeReward = SurchargeReward; - type TransferFee = ContractTransferFee; - type CreationFee = ContractCreationFee; type TransactionBaseFee = ContractTransactionBaseFee; type TransactionByteFee = ContractTransactionByteFee; type ContractFee = ContractFee; @@ -456,11 +454,11 @@ parameter_types! { impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; - type Call = Call; type Event = Event; + type Call = Call; type SubmitTransaction = SubmitTransaction; - type ReportUnresponsiveness = Offences; type SessionDuration = SessionDuration; + type ReportUnresponsiveness = Offences; } impl pallet_offences::Trait for Runtime { @@ -481,7 +479,7 @@ parameter_types! { } impl pallet_finality_tracker::Trait for Runtime { - type OnFinalizationStalled = Grandpa; + type OnFinalizationStalled = (); type WindowSize = WindowSize; type ReportLatency = ReportLatency; } @@ -490,19 +488,21 @@ parameter_types! { pub const BasicDeposit: Balance = 10 * DOLLARS; // 258 bytes on-chain pub const FieldDeposit: Balance = 250 * CENTS; // 66 bytes on-chain pub const SubAccountDeposit: Balance = 2 * DOLLARS; // 53 bytes on-chain - pub const MaximumSubAccounts: u32 = 100; + pub const MaxSubAccounts: u32 = 100; + pub const MaxAdditionalFields: u32 = 100; } impl pallet_identity::Trait for Runtime { type Event = Event; type Currency = Balances; - type Slashed = Treasury; type BasicDeposit = BasicDeposit; type FieldDeposit = FieldDeposit; type SubAccountDeposit = SubAccountDeposit; - type MaximumSubAccounts = MaximumSubAccounts; - type RegistrarOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type MaxSubAccounts = MaxSubAccounts; + type MaxAdditionalFields = MaxAdditionalFields; + type Slashed = Treasury; type ForceOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type RegistrarOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; } impl frame_system::offchain::CreateTransaction for Runtime { @@ -588,19 +588,25 @@ impl pallet_society::Trait for Runtime { type ChallengePeriod = ChallengePeriod; } +impl pallet_vesting::Trait for Runtime { + type Event = Event; + type Currency = Balances; + type BlockNumberToBalance = ConvertInto; +} + construct_runtime!( pub enum Runtime where Block = Block, NodeBlock = node_primitives::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: frame_system::{Module, Call, Storage, Config, Event}, + System: frame_system::{Module, Call, Config, Storage, Event}, Utility: pallet_utility::{Module, Call, Storage, Event}, Babe: pallet_babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent}, Authorship: pallet_authorship::{Module, Call, Storage, Inherent}, - Indices: pallet_indices, - Balances: pallet_balances, + Indices: pallet_indices::{Module, Call, Storage, Config, Event}, + Balances: pallet_balances::{Module, Call, Storage, Config, Event}, TransactionPayment: pallet_transaction_payment::{Module, Storage}, Staking: pallet_staking, Session: pallet_session::{Module, Call, Storage, Event, Config}, @@ -621,6 +627,7 @@ construct_runtime!( Identity: pallet_identity::{Module, Call, Storage, Event}, Society: pallet_society::{Module, Call, Storage, Event, Config}, Recovery: pallet_recovery::{Module, Call, Storage, Event}, + Vesting: pallet_vesting::{Module, Call, Storage, Event, Config}, } ); @@ -744,7 +751,9 @@ impl_runtime_apis! { } } - impl pallet_contracts_rpc_runtime_api::ContractsApi for Runtime { + impl pallet_contracts_rpc_runtime_api::ContractsApi + for Runtime + { fn call( origin: AccountId, dest: AccountId, @@ -752,13 +761,8 @@ impl_runtime_apis! { gas_limit: u64, input_data: Vec, ) -> ContractExecResult { - let exec_result = Contracts::bare_call( - origin, - dest.into(), - value, - gas_limit, - input_data, - ); + let exec_result = + Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data); match exec_result { Ok(v) => ContractExecResult::Success { status: v.status, @@ -771,16 +775,14 @@ impl_runtime_apis! { fn get_storage( address: AccountId, key: [u8; 32], - ) -> pallet_contracts_rpc_runtime_api::GetStorageResult { - Contracts::get_storage(address, key).map_err(|rpc_err| { - use pallet_contracts::GetStorageError; - use pallet_contracts_rpc_runtime_api::{GetStorageError as RpcGetStorageError}; - /// Map the contract error into the RPC layer error. - match rpc_err { - GetStorageError::ContractDoesntExist => RpcGetStorageError::ContractDoesntExist, - GetStorageError::IsTombstone => RpcGetStorageError::IsTombstone, - } - }) + ) -> pallet_contracts_primitives::GetStorageResult { + Contracts::get_storage(address, key) + } + + fn rent_projection( + address: AccountId, + ) -> pallet_contracts_primitives::RentProjectionResult { + Contracts::rent_projection(address) } } @@ -798,6 +800,33 @@ impl_runtime_apis! { fn generate_session_keys(seed: Option>) -> Vec { SessionKeys::generate(seed) } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl crate::Benchmark for Runtime { + fn dispatch_benchmark(module: Vec, extrinsic: Vec, steps: u32, repeat: u32) + -> Option> + { + match module.as_slice() { + b"pallet-balances" | b"balances" => Balances::run_benchmark(extrinsic, steps, repeat).ok(), + b"pallet-identity" | b"identity" => Identity::run_benchmark(extrinsic, steps, repeat).ok(), + b"pallet-timestamp" | b"timestamp" => Timestamp::run_benchmark(extrinsic, steps, repeat).ok(), + _ => return None, + } + } + } +} + +sp_api::decl_runtime_apis! { + pub trait Benchmark + { + fn dispatch_benchmark(module: Vec, extrinsic: Vec, steps: u32, repeat: u32) + -> Option>; } } diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml index 37e85452d54253dec5725540bf40f1e2a3303a4e..558c8f2f0b2c3c666658c5b9ea895486b66b402d 100644 --- a/bin/node/testing/Cargo.toml +++ b/bin/node/testing/Cargo.toml @@ -4,10 +4,13 @@ version = "2.0.0" authors = ["Parity Technologies "] description = "Test utilities for Substrate node." edition = "2018" +license = "GPL-3.0" [dependencies] pallet-balances = { version = "2.0.0", path = "../../../frame/balances" } sc-client = { version = "0.8", path = "../../../client/" } +sc-client-db = { version = "0.8", path = "../../../client/db/", features = ["kvdb-rocksdb"] } +sc-client-api = { version = "2.0", path = "../../../client/api/" } codec = { package = "parity-scale-codec", version = "1.0.0" } pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" } pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" } @@ -23,10 +26,30 @@ pallet-session = { version = "2.0.0", path = "../../../frame/session" } pallet-society = { version = "2.0.0", path = "../../../frame/society" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } pallet-staking = { version = "2.0.0", path = "../../../frame/staking" } -sc-executor = { version = "0.8", path = "../../../client/executor" } +sc-executor = { version = "0.8", path = "../../../client/executor", features = ["wasmtime"] } +sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" } frame-system = { version = "2.0.0", path = "../../../frame/system" } substrate-test-client = { version = "2.0.0", path = "../../../test-utils/client" } pallet-timestamp = { version = "2.0.0", path = "../../../frame/timestamp" } pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" } pallet-treasury = { version = "2.0.0", path = "../../../frame/treasury" } wabt = "0.9.2" +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-finality-tracker = { version = "2.0.0", default-features = false, path = "../../../primitives/finality-tracker" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../../primitives/timestamp" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +log = "0.4.8" + +[dev-dependencies] +criterion = "0.3.0" +tempdir = "0.3" +fs_extra = "1" +hex-literal = "0.2.1" +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sc-service = { version = "0.8.0", path = "../../../client/service", features = ["rocksdb"] } + +[[bench]] +name = "import" +harness = false diff --git a/bin/node/testing/benches/import.rs b/bin/node/testing/benches/import.rs new file mode 100644 index 0000000000000000000000000000000000000000..86092a78360157b4e67af9a01eef2d55a20cb154 --- /dev/null +++ b/bin/node/testing/benches/import.rs @@ -0,0 +1,503 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Block import benchmark. +//! +//! This benchmark is expected to measure block import operation of +//! some more or less full block. +//! +//! As we also want to protect against cold-cache attacks, this +//! benchmark should not rely on any caching (except those that +//! DO NOT depend on user input). Thus block generation should be +//! based on randomized operation. +//! +//! This is supposed to be very simple benchmark and is not subject +//! to much configuring - just block full of randomized transactions. +//! It is not supposed to measure runtime modules weight correctness + +use std::{sync::Arc, path::Path, collections::BTreeMap}; + +use node_primitives::Block; +use node_testing::client::{Client, Backend}; +use node_testing::keyring::*; +use sc_client_db::PruningMode; +use sc_executor::{NativeExecutor, WasmExecutionMethod}; +use sp_consensus::{ + BlockOrigin, BlockImport, BlockImportParams, + ForkChoiceStrategy, ImportResult, ImportedAux +}; +use sp_runtime::{ + generic::BlockId, + OpaqueExtrinsic, + traits::{Block as BlockT, Verify, Zero, IdentifyAccount}, +}; +use codec::{Decode, Encode}; +use node_runtime::{ + Call, + CheckedExtrinsic, + constants::currency::DOLLARS, + UncheckedExtrinsic, + MinimumPeriod, + BalancesCall, + AccountId, + Signature, +}; +use sp_core::ExecutionContext; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_inherents::InherentData; +use sc_client_api::{ + Backend as _, ExecutionStrategy, + execution_extensions::{ExecutionExtensions, ExecutionStrategies}, +}; +use sp_core::{Pair, Public, sr25519}; + +use criterion::{Criterion, criterion_group, criterion_main}; + +criterion_group!( + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_block_import +); +criterion_group!( + name = profile; + config = Criterion::default().sample_size(10); + targets = profile_block_import +); +criterion_main!(benches, profile); + +fn genesis(keyring: &BenchKeyring) -> node_runtime::GenesisConfig { + node_testing::genesis::config_endowed( + false, + Some(node_runtime::WASM_BINARY), + keyring.collect_account_ids(), + ) +} + +// this is deterministic keyring of ordered accounts +// //endowed-user//00 +// //endowed-user//01 +// ... +// //endowed-user//N +struct BenchKeyring { + accounts: BTreeMap, +} + +impl BenchKeyring { + fn new(num: usize) -> Self { + let mut accounts = BTreeMap::new(); + + for n in 0..num { + let seed = format!("//endowed-user/{}", n); + let pair = sr25519::Pair::from_string(&seed, None).expect("failed to generate pair"); + let account_id = AccountPublic::from(pair.public()).into_account(); + accounts.insert(account_id, pair); + } + + Self { accounts } + } + + fn collect_account_ids(&self) -> Vec { + self.accounts.keys().cloned().collect() + } + + fn at(&self, index: usize) -> AccountId { + self.accounts.keys().nth(index).expect("Failed to get account").clone() + } + + fn sign(&self, xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> UncheckedExtrinsic { + match xt.signed { + Some((signed, extra)) => { + let payload = (xt.function, extra.clone(), version, genesis_hash, genesis_hash); + let key = self.accounts.get(&signed).expect("Account id not found in keyring"); + let signature = payload.using_encoded(|b| { + if b.len() > 256 { + key.sign(&sp_io::hashing::blake2_256(b)) + } else { + key.sign(b) + } + }).into(); + UncheckedExtrinsic { + signature: Some((pallet_indices::address::Address::Id(signed), signature, extra)), + function: payload.0, + } + } + None => UncheckedExtrinsic { + signature: None, + function: xt.function, + }, + } + } +} + +#[derive(Clone, Copy, Debug)] +enum Profile { + Native, + Wasm, +} + +impl Profile { + fn into_execution_strategies(self) -> ExecutionStrategies { + match self { + Profile::Wasm => ExecutionStrategies { + syncing: ExecutionStrategy::AlwaysWasm, + importing: ExecutionStrategy::AlwaysWasm, + block_construction: ExecutionStrategy::AlwaysWasm, + offchain_worker: ExecutionStrategy::AlwaysWasm, + other: ExecutionStrategy::AlwaysWasm, + }, + Profile::Native => ExecutionStrategies { + syncing: ExecutionStrategy::NativeElseWasm, + importing: ExecutionStrategy::NativeElseWasm, + block_construction: ExecutionStrategy::NativeElseWasm, + offchain_worker: ExecutionStrategy::NativeElseWasm, + other: ExecutionStrategy::NativeElseWasm, + } + } + } +} + +// This should return client that is doing everything that full node +// is doing. +// +// - This client should use best wasm execution method. +// - This client should work with real database only. +fn bench_client(dir: &std::path::Path, profile: Profile, keyring: &BenchKeyring) -> (Client, std::sync::Arc) { + let db_config = sc_client_db::DatabaseSettings { + state_cache_size: 16*1024*1024, + state_cache_child_ratio: Some((0, 100)), + pruning: PruningMode::ArchiveAll, + source: sc_client_db::DatabaseSettingsSrc::Path { + path: dir.into(), + cache_size: None, + }, + }; + + let (client, backend) = sc_client_db::new_client( + db_config, + NativeExecutor::new(WasmExecutionMethod::Compiled, None), + &genesis(keyring), + None, + None, + ExecutionExtensions::new(profile.into_execution_strategies(), None), + ).expect("Should not fail"); + + (client, backend) +} + +struct Guard(tempdir::TempDir); + +struct BenchContext { + client: Client, + backend: Arc, + db_guard: Guard, + keyring: BenchKeyring, +} + +impl BenchContext { + fn new(profile: Profile) -> BenchContext { + let keyring = BenchKeyring::new(128); + + let dir = tempdir::TempDir::new("sub-bench").expect("temp dir creation failed"); + log::trace!( + target: "bench-logistics", + "Created seed db at {}", + dir.path().to_string_lossy(), + ); + let (client, backend) = bench_client(dir.path(), profile, &keyring); + let db_guard = Guard(dir); + + + BenchContext { client, backend, db_guard, keyring } + } + + fn new_from_seed(profile: Profile, seed_dir: &Path) -> BenchContext { + let keyring = BenchKeyring::new(128); + + let dir = tempdir::TempDir::new("sub-bench").expect("temp dir creation failed"); + + log::trace!( + target: "bench-logistics", + "Copying seed db from {} to {}", + seed_dir.to_string_lossy(), + dir.path().to_string_lossy(), + ); + let seed_db_files = std::fs::read_dir(seed_dir) + .expect("failed to list file in seed dir") + .map(|f_result| + f_result.expect("failed to read file in seed db") + .path() + .clone() + ).collect(); + fs_extra::copy_items( + &seed_db_files, + dir.path(), + &fs_extra::dir::CopyOptions::new(), + ).expect("Copy of seed database is ok"); + + let (client, backend) = bench_client(dir.path(), profile, &keyring); + let db_guard = Guard(dir); + + BenchContext { client, backend, db_guard, keyring } + } + + fn keep_db(self) -> Guard { + self.db_guard + } +} + +type AccountPublic = ::Signer; + +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public> +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +// Block generation. +fn generate_block_import(client: &Client, keyring: &BenchKeyring) -> Block { + let version = client.runtime_version_at(&BlockId::number(0)) + .expect("There should be runtime version at 0") + .spec_version; + let genesis_hash = client.block_hash(Zero::zero()) + .expect("Database error?") + .expect("Genesis block always exists; qed") + .into(); + + let mut block = client + .new_block(Default::default()) + .expect("Block creation failed"); + + let timestamp = 1 * MinimumPeriod::get(); + + let mut inherent_data = InherentData::new(); + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, ×tamp) + .expect("Put timestamb failed"); + inherent_data.put_data(sp_finality_tracker::INHERENT_IDENTIFIER, &0) + .expect("Put finality tracker failed"); + + for extrinsic in client.runtime_api() + .inherent_extrinsics_with_context( + &BlockId::number(0), + ExecutionContext::BlockConstruction, + inherent_data, + ).expect("Get inherents failed") + { + block.push(extrinsic).expect("Push inherent failed"); + } + + let mut iteration = 0; + let start = std::time::Instant::now(); + for _ in 0..100 { + + let sender = keyring.at(iteration); + let receiver = get_account_id_from_seed::( + &format!("random-user//{}", iteration) + ); + + let signed = keyring.sign( + CheckedExtrinsic { + signed: Some((sender, signed_extra(0, 1*DOLLARS))), + function: Call::Balances( + BalancesCall::transfer( + pallet_indices::address::Address::Id(receiver), + 1*DOLLARS + ) + ), + }, + version, + genesis_hash, + ); + + let encoded = Encode::encode(&signed); + + let opaque = OpaqueExtrinsic::decode(&mut &encoded[..]) + .expect("Failed to decode opaque"); + + match block.push(opaque) { + Err(sp_blockchain::Error::ApplyExtrinsicFailed( + sp_blockchain::ApplyExtrinsicFailed::Validity(e) + )) if e.exhausted_resources() => { + break; + }, + Err(err) => panic!("Error pushing transaction: {:?}", err), + Ok(_) => {}, + } + iteration += 1; + } + let block = block.build().expect("Block build failed").block; + + log::info!( + target: "bench-logistics", + "Block construction: {:#?} ({} tx)", + start.elapsed(), block.extrinsics.len() + ); + + block +} + +// Import generated block. +fn import_block(client: &mut Client, block: Block) { + let import_params = BlockImportParams { + origin: BlockOrigin::NetworkBroadcast, + header: block.header().clone(), + post_digests: Default::default(), + body: Some(block.extrinsics().to_vec()), + storage_changes: Default::default(), + finalized: false, + justification: Default::default(), + auxiliary: Default::default(), + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), + allow_missing_state: false, + import_existing: false, + }; + + assert_eq!(client.chain_info().best_number, 0); + + assert_eq!( + client.import_block(import_params, Default::default()) + .expect("Failed to import block"), + ImportResult::Imported( + ImportedAux { + header_only: false, + clear_justification_requests: false, + needs_justification: false, + bad_justification: false, + needs_finality_proof: false, + is_new_best: true, + } + ) + ); + + assert_eq!(client.chain_info().best_number, 1); +} + +fn bench_block_import(c: &mut Criterion) { + sc_cli::init_logger(""); + // for future uses, uncomment if something wrong. + // sc_cli::init_logger("sc_client=debug"); + + let (block, guard) = { + let context = BenchContext::new(Profile::Wasm); + let block = generate_block_import(&context.client, &context.keyring); + (block, context.keep_db()) + }; + + log::trace!( + target: "bench-logistics", + "Seed database directory: {}", + guard.0.path().to_string_lossy(), + ); + + c.bench_function_over_inputs("import block", + move |bencher, profile| { + bencher.iter_batched( + || { + let context = BenchContext::new_from_seed( + *profile, + guard.0.path(), + ); + + // mostly to just launch compiler before benching! + let version = context.client.runtime_version_at(&BlockId::Number(0)) + .expect("Failed to get runtime version") + .spec_version; + + log::trace!( + target: "bench-logistics", + "Next iteration database directory: {}, runtime version: {}", + context.db_guard.0.path().to_string_lossy(), version, + ); + + context + }, + |mut context| { + let start = std::time::Instant::now(); + import_block(&mut context.client, block.clone()); + let elapsed = start.elapsed(); + + log::info!( + target: "bench-logistics", + "imported block with {} tx, took: {:#?}", + block.extrinsics.len(), + elapsed, + ); + + log::info!( + target: "bench-logistics", + "usage info: {}", + context.backend.usage_info() + .expect("RocksDB backend always provides usage info!"), + ); + }, + criterion::BatchSize::PerIteration, + ); + }, + vec![Profile::Wasm, Profile::Native], + ); +} + + +// This is not an actual benchmark, so don't use it to measure anything. +// It just produces special pattern of cpu load that allows easy picking +// the part of block import for the profiling in the tool of choice. +fn profile_block_import(c: &mut Criterion) { + sc_cli::init_logger(""); + + let (block, guard) = { + let context = BenchContext::new(Profile::Wasm); + let block = generate_block_import(&context.client, &context.keyring); + (block, context.keep_db()) + }; + + c.bench_function("profile block", + move |bencher| { + bencher.iter_batched( + || { + let context = BenchContext::new_from_seed( + Profile::Native, + guard.0.path(), + ); + context + }, + |mut context| { + // until better osx signpost/callgrind signal is possible to use + // in rust, we just pause everything completely to help choosing + // actual profiling interval + std::thread::park_timeout(std::time::Duration::from_secs(2)); + import_block(&mut context.client, block.clone()); + // and here as well + std::thread::park_timeout(std::time::Duration::from_secs(2)); + log::info!( + target: "bench-logistics", + "imported block, usage info: {}", + context.backend.usage_info() + .expect("RocksDB backend always provides usage info!"), + ) + }, + criterion::BatchSize::PerIteration, + ); + }, + ); +} \ No newline at end of file diff --git a/bin/node/testing/src/client.rs b/bin/node/testing/src/client.rs index 140d1cbfc33379af90e49a2a238545fa284fd9ab..29b086c3c11977847cb0369deee4f109f30539bf 100644 --- a/bin/node/testing/src/client.rs +++ b/bin/node/testing/src/client.rs @@ -35,6 +35,9 @@ pub type Client = sc_client::Client< node_runtime::RuntimeApi, >; +/// Transaction for node-runtime. +pub type Transaction = sc_client_api::backend::TransactionFor; + /// Genesis configuration parameters for `TestClient`. #[derive(Default)] pub struct GenesisParameters { @@ -57,6 +60,7 @@ pub trait TestClientBuilderExt: Sized { } impl TestClientBuilderExt for substrate_test_client::TestClientBuilder< + node_primitives::Block, sc_client::LocalCallExecutor, Backend, GenesisParameters, diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 183515f27274b47e0c8bb8583e50839cec19ed45..9c163ea6153b2f11bf871883b570d1a7acd05e18 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -21,14 +21,38 @@ use sp_keyring::{Ed25519Keyring, Sr25519Keyring}; use node_runtime::{ GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, SystemConfig, GrandpaConfig, IndicesConfig, ContractsConfig, SocietyConfig, WASM_BINARY, + AccountId, }; use node_runtime::constants::currency::*; use sp_core::ChangesTrieConfiguration; use sp_runtime::Perbill; - /// Create genesis runtime configuration for tests. pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig { + config_endowed(support_changes_trie, code, Default::default()) +} + +/// Create genesis runtime configuration for tests with some extra +/// endowed accounts. +pub fn config_endowed( + support_changes_trie: bool, + code: Option<&[u8]>, + extra_endowed: Vec, +) -> GenesisConfig { + + let mut endowed = vec![ + (alice(), 111 * DOLLARS), + (bob(), 100 * DOLLARS), + (charlie(), 100_000_000 * DOLLARS), + (dave(), 111 * DOLLARS), + (eve(), 101 * DOLLARS), + (ferdie(), 100 * DOLLARS), + ]; + + endowed.extend( + extra_endowed.into_iter().map(|endowed| (endowed, 100*DOLLARS)) + ); + GenesisConfig { frame_system: Some(SystemConfig { changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration { @@ -38,18 +62,10 @@ pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig code: code.map(|x| x.to_vec()).unwrap_or_else(|| WASM_BINARY.to_vec()), }), pallet_indices: Some(IndicesConfig { - ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()], + indices: vec![], }), pallet_balances: Some(BalancesConfig { - balances: vec![ - (alice(), 111 * DOLLARS), - (bob(), 100 * DOLLARS), - (charlie(), 100_000_000 * DOLLARS), - (dave(), 111 * DOLLARS), - (eve(), 101 * DOLLARS), - (ferdie(), 100 * DOLLARS), - ], - vesting: vec![], + balances: endowed, }), pallet_session: Some(SessionConfig { keys: vec![ @@ -101,5 +117,6 @@ pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig pot: 0, max_members: 999, }), + pallet_vesting: Some(Default::default()), } } diff --git a/bin/node/transaction-factory/Cargo.toml b/bin/node/transaction-factory/Cargo.toml index f32a3c23e0e01e35df10f474b07747b349cef04b..cd40e2f0d939db732339659aeea02f184b062096 100644 --- a/bin/node/transaction-factory/Cargo.toml +++ b/bin/node/transaction-factory/Cargo.toml @@ -3,6 +3,7 @@ name = "node-transaction-factory" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } diff --git a/bin/node/transaction-factory/src/complex_mode.rs b/bin/node/transaction-factory/src/complex_mode.rs deleted file mode 100644 index 6d7e60c8d3c1f937301f34746faffdd734c26c36..0000000000000000000000000000000000000000 --- a/bin/node/transaction-factory/src/complex_mode.rs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -/// This module implements the `MasterToNToM` mode: -/// -/// Manufacture `num` transactions from the master account to `num` -/// randomly created accounts. From each of these randomly created -/// accounts manufacture a transaction to another randomly created -/// account. -/// Repeat this round `rounds` times. If `rounds` = 1 the behavior -/// is the same as `MasterToN`. -/// -/// A -> B -/// A -> C -/// A -> D -/// ... x `num` -/// -/// B -> E -/// C -> F -/// D -> G -/// ... -/// E -> H -/// F -> I -/// G -> J -/// ... -/// ... x `rounds` - -use std::sync::Arc; - -use log::info; -use sc_client::Client; -use sp_block_builder::BlockBuilder; -use sp_api::{ConstructRuntimeApi, ProvideRuntimeApi}; -use sp_runtime::generic::BlockId; -use sp_runtime::traits::{Block as BlockT, One, Zero}; - -use crate::{RuntimeAdapter, create_block}; - -pub fn next( - factory_state: &mut RA, - client: &Arc>, - version: u32, - genesis_hash: ::Hash, - prior_block_hash: ::Hash, - prior_block_id: BlockId, -) -> Option -where - Block: BlockT, - Exec: sc_client::CallExecutor + Send + Sync + Clone, - Backend: sc_client_api::backend::Backend + Send, - Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: - BlockBuilder + - sp_api::ApiExt, - RtApi: ConstructRuntimeApi> + Send + Sync, - RA: RuntimeAdapter, -{ - let total = factory_state.start_number() + factory_state.num() * factory_state.rounds(); - - if factory_state.block_no() >= total || factory_state.round() >= factory_state.rounds() { - return None; - } - - info!( - "Round {}: Creating {} transactions in total, {} per round. {} rounds in total.", - factory_state.round() + RA::Number::one(), - factory_state.num() * factory_state.rounds(), - factory_state.num(), - factory_state.rounds(), - ); - - let from = from::(factory_state); - - let seed = factory_state.start_number() + factory_state.block_no(); - let to = RA::gen_random_account_id(&seed); - - let rounds_left = factory_state.rounds() - factory_state.round(); - let amount = RA::minimum_balance() * rounds_left.into(); - - let transfer = factory_state.transfer_extrinsic( - &from.0, - &from.1, - &to, - &amount, - version, - &genesis_hash, - &prior_block_hash, - ); - - let inherents = factory_state.inherent_extrinsics(); - let inherents = client.runtime_api().inherent_extrinsics(&prior_block_id, inherents) - .expect("Failed to create inherent extrinsics"); - - let block = create_block::(&client, transfer, inherents); - info!( - "Created block {} with hash {}. Transferring {} from {} to {}.", - factory_state.block_no() + RA::Number::one(), - prior_block_hash, - amount, - from.0, - to - ); - - factory_state.set_block_no(factory_state.block_no() + RA::Number::one()); - - let new_round = factory_state.block_no() > RA::Number::zero() - && factory_state.block_no() % factory_state.num() == RA::Number::zero(); - if new_round { - factory_state.set_round(factory_state.round() + RA::Number::one()); - factory_state.set_block_in_round(RA::Number::zero()); - } else { - factory_state.set_block_in_round(factory_state.block_in_round() + RA::Number::one()); - } - - Some(block) -} - -/// Return the account which received tokens at this point in the previous round. -fn from( - factory_state: &mut RA -) -> (::AccountId, ::Secret) -where RA: RuntimeAdapter -{ - let is_first_round = factory_state.round() == RA::Number::zero(); - match is_first_round { - true => { - // first round always uses master account - (RA::master_account_id(), RA::master_account_secret()) - }, - _ => { - // the account to which was sent in the last round - let is_round_one = factory_state.round() == RA::Number::one(); - let seed = match is_round_one { - true => factory_state.start_number() + factory_state.block_in_round(), - _ => { - let block_no_in_prior_round = - factory_state.num() * (factory_state.round() - RA::Number::one()) + factory_state.block_in_round(); - factory_state.start_number() + block_no_in_prior_round - } - }; - (RA::gen_random_account_id(&seed), RA::gen_random_account_secret(&seed)) - }, - } -} diff --git a/bin/node/transaction-factory/src/lib.rs b/bin/node/transaction-factory/src/lib.rs index f3dcdb087500274289469d77dbd7af5817bfbdf3..acee44625f2b5546663a4ec4b86b13710b863ba2 100644 --- a/bin/node/transaction-factory/src/lib.rs +++ b/bin/node/transaction-factory/src/lib.rs @@ -37,39 +37,28 @@ use sp_consensus::block_import::BlockImport; use codec::{Decode, Encode}; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ - Block as BlockT, Header as HeaderT, SimpleArithmetic, One, Zero, + Block as BlockT, Header as HeaderT, AtLeast32Bit, One, Zero, }; -pub use crate::modes::Mode; - -pub mod modes; -mod complex_mode; -mod simple_modes; pub trait RuntimeAdapter { type AccountId: Display; - type Balance: Display + SimpleArithmetic + From; + type Balance: Display + AtLeast32Bit + From; type Block: BlockT; type Index: Copy; - type Number: Display + PartialOrd + SimpleArithmetic + Zero + One; + type Number: Display + PartialOrd + AtLeast32Bit + Zero + One; type Phase: Copy; type Secret; - fn new(mode: Mode, rounds: u64, start_number: u64) -> Self; + fn new(blocks: u32, transactions: u32) -> Self; - fn block_no(&self) -> Self::Number; - fn block_in_round(&self) -> Self::Number; - fn mode(&self) -> &Mode; - fn num(&self) -> Self::Number; - fn rounds(&self) -> Self::Number; - fn round(&self) -> Self::Number; - fn start_number(&self) -> Self::Number; + fn blocks(&self) -> u32; + fn transactions(&self) -> u32; - fn set_block_in_round(&mut self, val: Self::Number); - fn set_block_no(&mut self, val: Self::Number); - fn set_round(&mut self, val: Self::Number); + fn block_number(&self) -> u32; + fn set_block_number(&mut self, value: u32); fn transfer_extrinsic( - &self, + &mut self, sender: &Self::AccountId, key: &Self::Secret, destination: &Self::AccountId, @@ -84,14 +73,12 @@ pub trait RuntimeAdapter { fn minimum_balance() -> Self::Balance; fn master_account_id() -> Self::AccountId; fn master_account_secret() -> Self::Secret; - fn extract_index(&self, account_id: &Self::AccountId, block_hash: &::Hash) -> Self::Index; - fn extract_phase(&self, block_hash: ::Hash) -> Self::Phase; - fn gen_random_account_id(seed: &Self::Number) -> Self::AccountId; - fn gen_random_account_secret(seed: &Self::Number) -> Self::Secret; + + fn gen_random_account_id(seed: u32) -> Self::AccountId; + fn gen_random_account_secret(seed: u32) -> Self::Secret; } -/// Manufactures transactions. The exact amount depends on -/// `mode`, `num` and `rounds`. +/// Manufactures transactions. The exact amount depends on `num` and `rounds`. pub fn factory( mut factory_state: RA, client: &Arc>, @@ -110,11 +97,6 @@ where RA: RuntimeAdapter, Block::Hash: From, { - if *factory_state.mode() != Mode::MasterToNToM && factory_state.rounds() > RA::Number::one() { - let msg = "The factory can only be used with rounds set to 1 in this mode.".into(); - return Err(sc_cli::error::Error::Input(msg)); - } - let best_header: Result<::Header, sc_cli::error::Error> = select_chain.best_chain().map_err(|e| format!("{:?}", e).into()); let mut best_hash = best_header?.hash(); @@ -123,88 +105,75 @@ where let genesis_hash = client.block_hash(Zero::zero())? .expect("Genesis block always exists; qed").into(); - while let Some(block) = match factory_state.mode() { - Mode::MasterToNToM => complex_mode::next::( - &mut factory_state, - &client, - version, - genesis_hash, - best_hash.into(), - best_block_id, - ), - _ => simple_modes::next::( - &mut factory_state, - &client, - version, - genesis_hash, - best_hash.into(), - best_block_id, - ), - } { + while factory_state.block_number() < factory_state.blocks() { + let from = (RA::master_account_id(), RA::master_account_secret()); + let amount = RA::minimum_balance(); + + let inherents = RA::inherent_extrinsics(&factory_state); + let inherents = client.runtime_api().inherent_extrinsics(&best_block_id, inherents) + .expect("Failed to create inherent extrinsics"); + + let tx_per_block = factory_state.transactions(); + + let mut block = client.new_block(Default::default()).expect("Failed to create new block"); + + for tx_num in 0..tx_per_block { + let seed = tx_num * (factory_state.block_number() + 1); + let to = RA::gen_random_account_id(seed); + + let transfer = factory_state.transfer_extrinsic( + &from.0, + &from.1, + &to, + &amount, + version, + &genesis_hash, + &best_hash, + ); + + info!("Pushing transfer {}/{} to {} into block.", tx_num + 1, tx_per_block, to); + + block.push( + Decode::decode(&mut &transfer.encode()[..]) + .expect("Failed to decode transfer extrinsic") + ).expect("Failed to push transfer extrinsic into block"); + } + + for inherent in inherents { + block.push(inherent).expect("Failed ..."); + } + + let block = block.build().expect("Failed to bake block").block; + + factory_state.set_block_number(factory_state.block_number() + 1); + + info!( + "Created block {} with hash {}.", + factory_state.block_number(), + best_hash, + ); + best_hash = block.header().hash(); best_block_id = BlockId::::hash(best_hash); - import_block(client.clone(), block); - info!("Imported block at {}", factory_state.block_no()); + let import = BlockImportParams { + origin: BlockOrigin::File, + header: block.header().clone(), + post_digests: Vec::new(), + body: Some(block.extrinsics().to_vec()), + storage_changes: None, + finalized: false, + justification: None, + auxiliary: Vec::new(), + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), + allow_missing_state: false, + import_existing: false, + }; + client.clone().import_block(import, HashMap::new()).expect("Failed to import block"); + + info!("Imported block at {}", factory_state.block_number()); } Ok(()) } - -/// Create a baked block from a transfer extrinsic and timestamp inherent. -pub fn create_block( - client: &Arc>, - transfer: ::Extrinsic, - inherent_extrinsics: Vec<::Extrinsic>, -) -> Block -where - Block: BlockT, - Exec: sc_client::CallExecutor + Send + Sync + Clone, - Backend: sc_client_api::backend::Backend + Send, - Client: ProvideRuntimeApi, - RtApi: ConstructRuntimeApi> + Send + Sync, - as ProvideRuntimeApi>::Api: - BlockBuilder + - ApiExt, - RA: RuntimeAdapter, -{ - let mut block = client.new_block(Default::default()).expect("Failed to create new block"); - block.push( - Decode::decode(&mut &transfer.encode()[..]) - .expect("Failed to decode transfer extrinsic") - ).expect("Failed to push transfer extrinsic into block"); - - for inherent in inherent_extrinsics { - block.push(inherent).expect("Failed ..."); - } - - block.build().expect("Failed to bake block").block -} - -fn import_block( - mut client: Arc>, - block: Block -) -> () where - Block: BlockT, - Exec: sc_client::CallExecutor + Send + Sync + Clone, - Backend: sc_client_api::backend::Backend + Send, - Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: - sp_api::Core + - ApiExt, -{ - let import = BlockImportParams { - origin: BlockOrigin::File, - header: block.header().clone(), - post_digests: Vec::new(), - body: Some(block.extrinsics().to_vec()), - storage_changes: None, - finalized: false, - justification: None, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; - client.import_block(import, HashMap::new()).expect("Failed to import block"); -} diff --git a/bin/node/transaction-factory/src/simple_modes.rs b/bin/node/transaction-factory/src/simple_modes.rs deleted file mode 100644 index fba328731a98fe44ba5a17660c8bb816147b6f80..0000000000000000000000000000000000000000 --- a/bin/node/transaction-factory/src/simple_modes.rs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -/// This module implements two manufacturing modes: -/// -/// # MasterToN -/// Manufacture `num` transactions from the master account -/// to `num` randomly created accounts, one each. -/// -/// A -> B -/// A -> C -/// ... x `num` -/// -/// -/// # MasterTo1 -/// Manufacture `num` transactions from the master account -/// to exactly one other randomly created account. -/// -/// A -> B -/// A -> B -/// ... x `num` - -use std::sync::Arc; - -use log::info; -use sc_client::Client; -use sp_block_builder::BlockBuilder; -use sp_api::{ConstructRuntimeApi, ProvideRuntimeApi}; -use sp_runtime::traits::{Block as BlockT, One}; -use sp_runtime::generic::BlockId; - -use crate::{Mode, RuntimeAdapter, create_block}; - -pub fn next( - factory_state: &mut RA, - client: &Arc>, - version: u32, - genesis_hash: ::Hash, - prior_block_hash: ::Hash, - prior_block_id: BlockId, -) -> Option -where - Block: BlockT, - Exec: sc_client::CallExecutor + Send + Sync + Clone, - Backend: sc_client_api::backend::Backend + Send, - Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: - BlockBuilder + - sp_api::ApiExt, - RtApi: ConstructRuntimeApi> + Send + Sync, - RA: RuntimeAdapter, -{ - if factory_state.block_no() >= factory_state.num() { - return None; - } - - let from = (RA::master_account_id(), RA::master_account_secret()); - - let seed = match factory_state.mode() { - // choose the same receiver for all transactions - Mode::MasterTo1 => factory_state.start_number(), - - // different receiver for each transaction - Mode::MasterToN => factory_state.start_number() + factory_state.block_no(), - _ => unreachable!("Mode not covered!"), - }; - let to = RA::gen_random_account_id(&seed); - - let amount = RA::minimum_balance(); - - let transfer = factory_state.transfer_extrinsic( - &from.0, - &from.1, - &to, - &amount, - version, - &genesis_hash, - &prior_block_hash, - ); - - let inherents = RA::inherent_extrinsics(&factory_state); - let inherents = client.runtime_api().inherent_extrinsics(&prior_block_id, inherents) - .expect("Failed to create inherent extrinsics"); - - let block = create_block::(&client, transfer, inherents); - - factory_state.set_block_no(factory_state.block_no() + RA::Number::one()); - - info!( - "Created block {} with hash {}. Transferring {} from {} to {}.", - factory_state.block_no(), - prior_block_hash, - amount, - from.0, - to - ); - - Some(block) -} diff --git a/bin/utils/chain-spec-builder/Cargo.toml b/bin/utils/chain-spec-builder/Cargo.toml index c8d79afbced1139af502d15430780a6daa357cdf..8f122a35a02e67a63acdd3bf7f551d779f668d9d 100644 --- a/bin/utils/chain-spec-builder/Cargo.toml +++ b/bin/utils/chain-spec-builder/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" +license = "GPL-3.0" [dependencies] ansi_term = "0.12.1" @@ -11,4 +12,4 @@ sc-keystore = { version = "2.0.0", path = "../../../client/keystore" } node-cli = { version = "2.0.0", path = "../../node/cli" } sp-core = { version = "2.0.0", path = "../../../primitives/core" } rand = "0.7.2" -structopt = "=0.3.7" +structopt = "0.3.8" diff --git a/bin/utils/subkey/Cargo.toml b/bin/utils/subkey/Cargo.toml index b4398d9f8b2f37100e4131b2bd43d684c347d4ef..e29ee9a6c5ba1d638e0311b639ed1fe95ce3bb20 100644 --- a/bin/utils/subkey/Cargo.toml +++ b/bin/utils/subkey/Cargo.toml @@ -3,6 +3,7 @@ name = "subkey" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] futures = "0.1.29" @@ -27,6 +28,8 @@ derive_more = { version = "0.99.2" } sc-rpc = { version = "2.0.0", path = "../../../client/rpc" } jsonrpc-core-client = { version = "14.0.3", features = ["http"] } hyper = "0.12.35" +libp2p = "0.15.0" +serde_json = "1.0" [features] bench = [] diff --git a/bin/utils/subkey/src/main.rs b/bin/utils/subkey/src/main.rs index c2fd7e28c254861345d42de49993431358740d47..4d7e2206422700a80324f8999ae51824b4108bdc 100644 --- a/bin/utils/subkey/src/main.rs +++ b/bin/utils/subkey/src/main.rs @@ -23,8 +23,10 @@ use clap::{App, ArgMatches, SubCommand}; use codec::{Decode, Encode}; use hex_literal::hex; use itertools::Itertools; +use libp2p::identity::{ed25519 as libp2p_ed25519, PublicKey}; use node_primitives::{Balance, Hash, Index, AccountId, Signature}; use node_runtime::{BalancesCall, Call, Runtime, SignedPayload, UncheckedExtrinsic, VERSION}; +use serde_json::json; use sp_core::{ crypto::{set_default_ss58_version, Ss58AddressFormat, Ss58Codec}, ed25519, sr25519, ecdsa, Pair, Public, H256, hexdisplay::HexDisplay, @@ -37,6 +39,24 @@ use std::{ mod rpc; mod vanity; +enum OutputType { + Json, + Text, +} + +impl<'a> TryFrom<&'a str> for OutputType { + type Error = (); + + fn try_from(s: &'a str) -> Result { + match s { + "json" => Ok(OutputType::Json), + "text" => Ok(OutputType::Text), + _ => Err(()), + } + } + +} + trait Crypto: Sized { type Pair: Pair; type Public: Public + Ss58Codec + AsRef<[u8]> + std::hash::Hash; @@ -55,50 +75,97 @@ trait Crypto: Sized { uri: &str, password: Option<&str>, network_override: Option, + output: OutputType, ) where ::Public: PublicT, { if let Ok((pair, seed)) = Self::Pair::from_phrase(uri, password) { let public_key = Self::public_from_pair(&pair); - println!("Secret phrase `{}` is account:\n \ - Secret seed: {}\n \ - Public key (hex): {}\n \ - Account ID: {}\n \ - SS58 Address: {}", - uri, - format_seed::(seed), - format_public_key::(public_key.clone()), - format_account_id::(public_key), - Self::ss58_from_pair(&pair) - ); + + match output { + OutputType::Json => { + let json = json!({ + "secretPhrase": uri, + "secretSeed": format_seed::(seed), + "publicKey": format_public_key::(public_key.clone()), + "accountId": format_account_id::(public_key), + "ss58Address": Self::ss58_from_pair(&pair), + }); + println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed")); + }, + OutputType::Text => { + println!("Secret phrase `{}` is account:\n \ + Secret seed: {}\n \ + Public key (hex): {}\n \ + Account ID: {}\n \ + SS58 Address: {}", + uri, + format_seed::(seed), + format_public_key::(public_key.clone()), + format_account_id::(public_key), + Self::ss58_from_pair(&pair), + ); + }, + } } else if let Ok((pair, seed)) = Self::Pair::from_string_with_seed(uri, password) { let public_key = Self::public_from_pair(&pair); - println!("Secret Key URI `{}` is account:\n \ - Secret seed: {}\n \ - Public key (hex): {}\n \ - Account ID: {}\n \ - SS58 Address: {}", - uri, - if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() }, - format_public_key::(public_key.clone()), - format_account_id::(public_key), - Self::ss58_from_pair(&pair) - ); + + match output { + OutputType::Json => { + let json = json!({ + "secretKeyUri": uri, + "secretSeed": if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() }, + "publicKey": format_public_key::(public_key.clone()), + "accountId": format_account_id::(public_key), + "ss58Address": Self::ss58_from_pair(&pair), + }); + println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed")); + }, + OutputType::Text => { + println!("Secret Key URI `{}` is account:\n \ + Secret seed: {}\n \ + Public key (hex): {}\n \ + Account ID: {}\n \ + SS58 Address: {}", + uri, + if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() }, + format_public_key::(public_key.clone()), + format_account_id::(public_key), + Self::ss58_from_pair(&pair), + ); + }, + } + } else if let Ok((public_key, v)) = ::Public::from_string_with_version(uri) { let v = network_override.unwrap_or(v); - println!("Public Key URI `{}` is account:\n \ - Network ID/version: {}\n \ - Public key (hex): {}\n \ - Account ID: {}\n \ - SS58 Address: {}", - uri, - String::from(v), - format_public_key::(public_key.clone()), - format_account_id::(public_key.clone()), - public_key.to_ss58check_with_version(v) - ); + + match output { + OutputType::Json => { + let json = json!({ + "publicKeyUri": uri, + "networkId": String::from(v), + "publicKey": format_public_key::(public_key.clone()), + "accountId": format_account_id::(public_key.clone()), + "ss58Address": public_key.to_ss58check_with_version(v), + }); + println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed")); + }, + OutputType::Text => { + println!("Public Key URI `{}` is account:\n \ + Network ID/version: {}\n \ + Public key (hex): {}\n \ + Account ID: {}\n \ + SS58 Address: {}", + uri, + String::from(v), + format_public_key::(public_key.clone()), + format_account_id::(public_key.clone()), + public_key.to_ss58check_with_version(v), + ); + }, + } } else { println!("Invalid phrase/URI given"); } @@ -165,6 +232,7 @@ fn get_usage() -> String { [network] -n, --network 'Specify a network. One of {}. Default is {}' [password] -p, --password 'The password for the key' --password-interactive 'You will be prompted for the password for the key.' + [output] -o, --output 'Specify an output format. One of text, json. Default is text.' ", networks, default_network) } @@ -181,6 +249,9 @@ fn get_app<'a, 'b>(usage: &'a str) -> App<'a, 'b> { 'The number of words in the phrase to generate. One of 12 \ (default), 15, 18, 21 and 24.' "), + SubCommand::with_name("generate-node-key") + .about("Generate a random node libp2p key, save it to file and print its peer ID") + .args_from_usage("[file] 'Name of file to save secret key to'"), SubCommand::with_name("inspect") .about("Gets a public key and a SS58 address from the provided Secret URI") .args_from_usage("[uri] 'A Key URI to be inspected. May be a secret seed, \ @@ -329,13 +400,31 @@ where if let Some(network) = maybe_network { set_default_ss58_version(network); } + + let output: OutputType = match matches.value_of("output").map(TryInto::try_into) { + Some(Err(_)) => return Err(Error::Static("Invalid output name. See --help for available outputs.")), + Some(Ok(v)) => v, + None => OutputType::Text, + }; + match matches.subcommand() { ("generate", Some(matches)) => { let mnemonic = generate_mnemonic(matches)?; - C::print_from_uri(mnemonic.phrase(), password, maybe_network); + C::print_from_uri(mnemonic.phrase(), password, maybe_network, output); + } + ("generate-node-key", Some(matches)) => { + let file = matches.value_of("file").ok_or(Error::Static("Output file name is required"))?; + + let keypair = libp2p_ed25519::Keypair::generate(); + let secret = keypair.secret(); + let peer_id = PublicKey::Ed25519(keypair.public()).into_peer_id(); + + fs::write(file, secret.as_ref())?; + + println!("{}", peer_id); } ("inspect", Some(matches)) => { - C::print_from_uri(&get_uri("uri", &matches)?, password, maybe_network); + C::print_from_uri(&get_uri("uri", &matches)?, password, maybe_network, output); } ("sign", Some(matches)) => { let suri = get_uri("suri", &matches)?; @@ -364,7 +453,7 @@ where .unwrap_or_default(); let result = vanity::generate_key::(&desired)?; let formated_seed = format_seed::(result.seed); - C::print_from_uri(&formated_seed, None, maybe_network); + C::print_from_uri(&formated_seed, None, maybe_network, output); } ("transfer", Some(matches)) => { let signer = read_pair::(matches.value_of("from"), password)?; diff --git a/client/Cargo.toml b/client/Cargo.toml index 7413fc23f9e2c58279f56e08c94af46082659da0..c89fe88145d148a6d64e70dcae4adac7eb97021d 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-client" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-block-builder = { version = "0.8", path = "block-builder" } @@ -18,9 +19,9 @@ hash-db = { version = "0.15.2" } hex-literal = { version = "0.2.1" } sp-inherents = { version = "2.0.0", path = "../primitives/inherents" } sp-keyring = { version = "2.0.0", path = "../primitives/keyring" } -kvdb = "0.3.0" +kvdb = "0.4.0" log = { version = "0.4.8" } -parking_lot = { version = "0.9.0" } +parking_lot = "0.10.0" sp-core = { version = "2.0.0", path = "../primitives/core" } sp-std = { version = "2.0.0", path = "../primitives/std" } sp-version = { version = "2.0.0", path = "../primitives/version" } @@ -36,5 +37,5 @@ tracing = "0.1.10" env_logger = "0.7.0" tempfile = "3.1.0" substrate-test-runtime-client = { version = "2.0.0", path = "../test-utils/runtime/client" } -kvdb-memorydb = "0.3.0" +kvdb-memorydb = "0.4.0" sp-panic-handler = { version = "2.0.0", path = "../primitives/panic-handler" } diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index 651ee1434f7fc017257b729fb0061d6288d0f6f2..27a40c4d94cfbd70a77bfeac94dc770c264b7268 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-client-api" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } @@ -17,9 +18,9 @@ sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } hex-literal = { version = "0.2.1" } sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } -kvdb = "0.3.0" +kvdb = "0.4.0" log = { version = "0.4.8" } -parking_lot = { version = "0.9.0" } +parking_lot = "0.10.0" sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-version = { version = "2.0.0", default-features = false, path = "../../primitives/version" } diff --git a/client/api/src/client.rs b/client/api/src/client.rs index 65952ce787bb23552a10989faa18dc64b72e5982..7503ce4a79e4627eb9fac7527e3670cfb35c23f3 100644 --- a/client/api/src/client.rs +++ b/client/api/src/client.rs @@ -125,6 +125,8 @@ pub struct IoInfo { pub state_reads: u64, /// State reads (keys) from cache. pub state_reads_cache: u64, + /// State reads (keys) from cache. + pub state_writes: u64, } /// Usage statistics for running client instance. @@ -143,7 +145,7 @@ pub struct UsageInfo { impl fmt::Display for UsageInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, - "caches: ({} state, {} db overlay), i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total)", + "caches: ({} state, {} db overlay), i/o: ({} tx, {} write, {} read, {} avg tx, {}/{} key cache reads/total, {} key writes)", self.memory.state_cache, self.memory.database_cache, self.io.transactions, @@ -152,6 +154,7 @@ impl fmt::Display for UsageInfo { self.io.average_transaction_size, self.io.state_reads_cache, self.io.state_reads, + self.io.state_writes, ) } } diff --git a/client/api/src/light.rs b/client/api/src/light.rs index fc9b6dc6fd28c2002bbf6707a6e9ffbb7d99cb44..c0bebc1740a8a0047e1f1d93c259cb3d237994d3 100644 --- a/client/api/src/light.rs +++ b/client/api/src/light.rs @@ -21,10 +21,10 @@ use std::collections::{BTreeMap, HashMap}; use std::future::Future; use sp_runtime::{ - traits::{ - Block as BlockT, Header as HeaderT, NumberFor, - }, - generic::BlockId + traits::{ + Block as BlockT, Header as HeaderT, NumberFor, + }, + generic::BlockId }; use sp_core::ChangesTrieConfigurationRange; use sp_state_machine::StorageProof; @@ -307,8 +307,8 @@ pub trait RemoteBlockchain: Send + Sync { pub mod tests { use futures::future::Ready; use parking_lot::Mutex; - use sp_blockchain::Error as ClientError; - use sp_test_primitives::{Block, Header, Extrinsic}; + use sp_blockchain::Error as ClientError; + use sp_test_primitives::{Block, Header, Extrinsic}; use super::*; pub type OkCallFetcher = Mutex>; diff --git a/client/api/src/notifications.rs b/client/api/src/notifications.rs index 13bf06396d1639a63a7e1c8e3193fc1a3faeaa75..72a9f357fce337c7dec32758ff5aa2d0a8b6bd6a 100644 --- a/client/api/src/notifications.rs +++ b/client/api/src/notifications.rs @@ -323,7 +323,7 @@ mod tests { let child_filters = Some([ (StorageKey(vec![4]), None), (StorageKey(vec![5]), None), - ].into_iter().cloned().collect()); + ].iter().cloned().collect()); StorageChangeSet { changes: Arc::new(changes.0), child_changes: Arc::new(changes.1), diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index 25c3f161d401839a3fffd6920d460c1ac2a32d55..40b1b301730672aa499a977eaf27e8e988ec2adb 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -4,17 +4,18 @@ version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" +license = "GPL-3.0" [build-dependencies] -prost-build = "0.5.0" +prost-build = "0.6.1" [dependencies] bytes = "0.4.12" codec = { package = "parity-scale-codec", default-features = false, version = "1.0.3" } derive_more = "0.99.2" futures = "0.3.1" -futures-timer = "2.0" -libp2p = { version = "0.14.0-alpha.1", default-features = false, features = ["secp256k1", "libp2p-websocket"] } +futures-timer = "3.0.1" +libp2p = { version = "0.15.0", default-features = false, features = ["secp256k1", "libp2p-websocket"] } log = "0.4.8" prost = "0.6.1" rand = "0.7.2" diff --git a/client/basic-authorship/Cargo.toml b/client/basic-authorship/Cargo.toml index 8fa0b9f5cc20e044cec72e512bab09269d53e110..6a013f7a74fce889d329bc930da20c8aca02cbd9 100644 --- a/client/basic-authorship/Cargo.toml +++ b/client/basic-authorship/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-basic-authorship" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4.8" @@ -24,4 +25,4 @@ tokio-executor = { version = "0.2.0-alpha.6", features = ["blocking"] } [dev-dependencies] sc-transaction-pool = { version = "2.0.0", path = "../../client/transaction-pool" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } -parking_lot = "0.9" +parking_lot = "0.10.0" diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index 6c5b0630200732927b2d1aeba419b184c5573dd0..a9e3d28e21e30614c8f880c493a07ea8f58606a0 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -251,10 +251,11 @@ impl ProposerInner, let (block, storage_changes, proof) = block_builder.build()?.into_inner(); - info!("Prepared block for proposing at {} [hash: {:?}; parent_hash: {}; extrinsics: [{}]]", + info!("Prepared block for proposing at {} [hash: {:?}; parent_hash: {}; extrinsics ({}): [{}]]", block.header().number(), ::Hash::from(block.header().hash()), block.header().parent_hash(), + block.extrinsics().len(), block.extrinsics() .iter() .map(|xt| format!("{}", BlakeTwo256::hash_of(xt))) @@ -306,7 +307,9 @@ mod tests { fn should_cease_building_block_when_deadline_is_reached() { // given let client = Arc::new(substrate_test_runtime_client::new()); - let txpool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))); + let txpool = Arc::new( + BasicPool::new(Default::default(), Arc::new(FullChainApi::new(client.clone()))) + ); futures::executor::block_on( txpool.submit_at(&BlockId::number(0), vec![extrinsic(0), extrinsic(1)]) @@ -346,7 +349,9 @@ mod tests { let (client, backend) = substrate_test_runtime_client::TestClientBuilder::new() .build_with_backend(); let client = Arc::new(client); - let txpool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))); + let txpool = Arc::new( + BasicPool::new(Default::default(), Arc::new(FullChainApi::new(client.clone()))) + ); let genesis_hash = client.info().best_hash; let block_id = BlockId::Hash(genesis_hash); diff --git a/client/basic-authorship/src/lib.rs b/client/basic-authorship/src/lib.rs index f62134ce42aa7ba2655390e8b0e22d41269fedec..9f2bc6a761eb01f14d5a4b11a64989bdbd4b1114 100644 --- a/client/basic-authorship/src/lib.rs +++ b/client/basic-authorship/src/lib.rs @@ -26,7 +26,7 @@ //! # use substrate_test_runtime_client::{self, runtime::{Extrinsic, Transfer}, AccountKeyring}; //! # use sc_transaction_pool::{BasicPool, FullChainApi}; //! # let client = Arc::new(substrate_test_runtime_client::new()); -//! # let txpool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))); +//! # let txpool = Arc::new(BasicPool::new(Default::default(), Arc::new(FullChainApi::new(client.clone())))); //! // The first step is to create a `ProposerFactory`. //! let mut proposer_factory = ProposerFactory { //! client: client.clone(), diff --git a/client/block-builder/Cargo.toml b/client/block-builder/Cargo.toml index 29531c4288bedb96660ccf3cf488e80efaaccb57..383a931b2f8998288a362713a6a24aacaeff70c9 100644 --- a/client/block-builder/Cargo.toml +++ b/client/block-builder/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-block-builder" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-state-machine = { version = "0.8", path = "../../primitives/state-machine" } diff --git a/client/block-builder/src/lib.rs b/client/block-builder/src/lib.rs index 9b403dead44158a938da2308d62c23b9c0ce5a56..d0eb8b2892630f177e6cce45e9def226643b077a 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -197,8 +197,6 @@ where )?; let parent_hash = self.parent_hash; - // The unsafe is required because the consume requires that we drop/consume the inner api - // (what we do here). let storage_changes = self.api.into_storage_changes( &state, changes_trie_state.as_ref(), diff --git a/client/chain-spec/Cargo.toml b/client/chain-spec/Cargo.toml index fee166df322665f8148e62e1c44f3201fea9af99..222914145e160cbc71e9e7007a7e1f70c01d1380 100644 --- a/client/chain-spec/Cargo.toml +++ b/client/chain-spec/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-chain-spec" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-chain-spec-derive = { version = "2.0.0", path = "./derive" } diff --git a/client/chain-spec/derive/Cargo.toml b/client/chain-spec/derive/Cargo.toml index 108644066496fcc4bcf24a703d85da8bd83bbdc4..566948883bccb18890de62e54ae3a3547737357c 100644 --- a/client/chain-spec/derive/Cargo.toml +++ b/client/chain-spec/derive/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-chain-spec-derive" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 1c1d967c7d8d54426aac148a0c13ee192643f568..66b0a9a9b88f368278bb0f397912ca72e63f68f7 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Substrate CLI interface." edition = "2018" +license = "GPL-3.0" [dependencies] clap = "2.33.0" @@ -16,10 +17,11 @@ time = "0.1.42" ansi_term = "0.12.1" lazy_static = "1.4.0" app_dirs = "1.2.1" -tokio = "0.2" +tokio = { version = "0.2.9", features = [ "signal", "rt-core", "rt-threaded" ] } futures = "0.3.1" fdlimit = "0.1.1" serde_json = "1.0.41" +sc-informant = { version = "0.8", path = "../informant" } sp-panic-handler = { version = "2.0.0", path = "../../primitives/panic-handler" } sc-client-api = { version = "2.0.0", path = "../api" } sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } @@ -32,8 +34,10 @@ sc-telemetry = { version = "2.0.0", path = "../telemetry" } prometheus-exporter = { path = "../../utils/prometheus" } sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } names = "0.11.0" -structopt = "=0.3.7" +structopt = "0.3.8" sc-tracing = { version = "2.0.0", path = "../tracing" } +chrono = "0.4.10" +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [target.'cfg(not(target_os = "unknown"))'.dependencies] rpassword = "4.0.1" diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index 93a79c820523b64f693bd7698a0ca30b64ba63b5..37a9a456c949a8b388e1d15382a435eba8e88599 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -24,91 +24,62 @@ mod traits; mod params; mod execution_strategy; pub mod error; -pub mod informant; +mod runtime; +mod node_key; use sc_client_api::execution_extensions::ExecutionStrategies; use sc_service::{ config::{Configuration, DatabaseConfig, KeystoreConfig}, ServiceBuilderCommand, RuntimeGenesis, ChainSpecExtension, PruningMode, ChainSpec, + AbstractService, Roles as ServiceRoles, }; +pub use sc_service::config::VersionInfo; use sc_network::{ self, multiaddr::Protocol, config::{ - NetworkConfiguration, TransportConfig, NonReservedPeerMode, NodeKeyConfig, build_multiaddr + NetworkConfiguration, TransportConfig, NonReservedPeerMode, }, }; -use sp_core::H256; -use execution_strategy::*; use std::{ - io::{Write, Read, Seek, Cursor, stdin, stdout, ErrorKind}, iter, fmt::Debug, fs::{self, File}, - net::{Ipv4Addr, SocketAddr}, path::{Path, PathBuf}, str::FromStr, pin::Pin, task::Poll + io::Write, iter, fmt::Debug, fs, + net::{Ipv4Addr, SocketAddr}, path::PathBuf, }; -use names::{Generator, Name}; use regex::Regex; -use structopt::{StructOpt, StructOptInternal, clap::AppSettings}; -#[doc(hidden)] -pub use structopt::clap::App; +use structopt::{StructOpt, clap}; +pub use structopt; use params::{ - RunCmd, PurgeChainCmd, RevertCmd, ImportBlocksCmd, ExportBlocksCmd, BuildSpecCmd, - NetworkConfigurationParams, MergeParameters, TransactionPoolParams, - NodeKeyParams, NodeKeyType, Cors, CheckBlockCmd, + NetworkConfigurationParams, TransactionPoolParams, Cors, +}; +pub use params::{ + SharedParams, ImportParams, ExecutionStrategy, Subcommand, RunCmd, BuildSpecCmd, + ExportBlocksCmd, ImportBlocksCmd, CheckBlockCmd, PurgeChainCmd, RevertCmd, + BenchmarkCmd, }; -pub use params::{NoCustom, CoreParams, SharedParams, ImportParams, ExecutionStrategy}; pub use traits::GetSharedParams; use app_dirs::{AppInfo, AppDataType}; use log::info; use lazy_static::lazy_static; -use futures::{Future, executor::block_on}; use sc_telemetry::TelemetryEndpoints; -use sp_runtime::generic::BlockId; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +pub use crate::runtime::{run_until_exit, run_service_until_exit}; +use execution_strategy::*; +use names::{Generator, Name}; +use chrono::prelude::*; /// default sub directory to store network config const DEFAULT_NETWORK_CONFIG_PATH : &'static str = "network"; /// default sub directory to store database const DEFAULT_DB_CONFIG_PATH : &'static str = "db"; /// default sub directory for the key store -const DEFAULT_KEYSTORE_CONFIG_PATH : &'static str = "keystore"; +const DEFAULT_KEYSTORE_CONFIG_PATH : &'static str = "keystore"; /// The maximum number of characters for a node name. const NODE_NAME_MAX_LENGTH: usize = 32; -/// The file name of the node's Ed25519 secret key inside the chain-specific -/// network config directory, if neither `--node-key` nor `--node-key-file` -/// is specified in combination with `--node-key-type=ed25519`. -const NODE_KEY_ED25519_FILE: &str = "secret_ed25519"; - -/// Executable version. Used to pass version information from the root crate. -#[derive(Clone)] -pub struct VersionInfo { - /// Implementaiton name. - pub name: &'static str, - /// Implementation version. - pub version: &'static str, - /// SCM Commit hash. - pub commit: &'static str, - /// Executable file name. - pub executable_name: &'static str, - /// Executable file description. - pub description: &'static str, - /// Executable file author. - pub author: &'static str, - /// Support URL. - pub support_url: &'static str, -} - -/// Something that can be converted into an exit signal. -pub trait IntoExit { - /// Exit signal type. - type Exit: Future + Unpin + Send + 'static; - /// Convert into exit signal. - fn into_exit(self) -> Self::Exit; -} - fn get_chain_key(cli: &SharedParams) -> String { match cli.chain { Some(ref chain) => chain.clone(), @@ -129,8 +100,12 @@ fn generate_node_name() -> String { result } -/// Load spec give shared params and spec factory. -pub fn load_spec(cli: &SharedParams, factory: F) -> error::Result> where +/// Load spec to `Configuration` from shared params and spec factory. +pub fn load_spec<'a, G, E, F>( + mut config: &'a mut Configuration, + cli: &SharedParams, + factory: F, +) -> error::Result<&'a ChainSpec> where G: RuntimeGenesis, E: ChainSpecExtension, F: FnOnce(&str) -> Result>, String>, @@ -140,16 +115,17 @@ pub fn load_spec(cli: &SharedParams, factory: F) -> error::Result spec, None => ChainSpec::from_json_file(PathBuf::from(chain_key))? }; - Ok(spec) + + config.network.boot_nodes = spec.boot_nodes().to_vec(); + config.telemetry_endpoints = spec.telemetry_endpoints().clone(); + + config.chain_spec = Some(spec); + + Ok(config.chain_spec.as_ref().unwrap()) } -fn base_path( - cli: &SharedParams, - version: &VersionInfo, - default_base_path: Option, -) -> PathBuf { +fn base_path(cli: &SharedParams, version: &VersionInfo) -> PathBuf { cli.base_path.clone() - .or(default_base_path) .unwrap_or_else(|| app_dirs::get_app_root( AppDataType::UserData, @@ -161,637 +137,232 @@ fn base_path( ) } -/// Check whether a node name is considered as valid -fn is_node_name_valid(_name: &str) -> Result<(), &str> { - let name = _name.to_string(); - if name.chars().count() >= NODE_NAME_MAX_LENGTH { - return Err("Node name too long"); - } - - let invalid_chars = r"[\\.@]"; - let re = Regex::new(invalid_chars).unwrap(); - if re.is_match(&name) { - return Err("Node name should not contain invalid chars such as '.' and '@'"); - } - - let invalid_patterns = r"(https?:\\/+)?(www)+"; - let re = Regex::new(invalid_patterns).unwrap(); - if re.is_match(&name) { - return Err("Node name should not contain urls"); - } - - Ok(()) +/// Helper function used to parse the command line arguments. This is the equivalent of +/// `structopt`'s `from_args()` except that it takes a `VersionInfo` argument to provide the name of +/// the application, author, "about" and version. +/// +/// Gets the struct from the command line arguments. Print the +/// error message and quit the program in case of failure. +pub fn from_args(version: &VersionInfo) -> T +where + T: StructOpt + Sized, +{ + from_iter::(&mut std::env::args_os(), version) } -/// Parse command line interface arguments and prepares the command for execution. -/// -/// Before returning, this function performs various initializations, such as initializing the -/// panic handler and the logger, or increasing the limit for file descriptors. +/// Helper function used to parse the command line arguments. This is the equivalent of +/// `structopt`'s `from_iter()` except that it takes a `VersionInfo` argument to provide the name of +/// the application, author, "about" and version. /// -/// # Remarks -/// -/// `CC` is a custom subcommand. This needs to be an `enum`! If no custom subcommand is required, -/// `NoCustom` can be used as type here. -/// -/// `RP` are custom parameters for the run command. This needs to be a `struct`! The custom -/// parameters are visible to the user as if they were normal run command parameters. If no custom -/// parameters are required, `NoCustom` can be used as type here. -pub fn parse_and_prepare<'a, CC, RP, I>( - version: &'a VersionInfo, - impl_name: &'static str, - args: I, -) -> ParseAndPrepare<'a, CC, RP> +/// Gets the struct from any iterator such as a `Vec` of your making. +/// Print the error message and quit the program in case of failure. +pub fn from_iter(iter: I, version: &VersionInfo) -> T where - CC: StructOpt + Clone + GetSharedParams, - RP: StructOpt + Clone + StructOptInternal, + T: StructOpt + Sized, I: IntoIterator, - ::Item: Into + Clone, + I::Item: Into + Clone, { - let full_version = sc_service::config::full_version_from_strs( + let app = T::clap(); + + let mut full_version = sc_service::config::full_version_from_strs( version.version, version.commit ); + full_version.push_str("\n"); - sp_panic_handler::set(version.support_url, &full_version); - let matches = CoreParams::::clap() + let app = app .name(version.executable_name) .author(version.author) .about(version.description) - .version(&(full_version + "\n")[..]) - .setting(AppSettings::GlobalVersion) - .setting(AppSettings::ArgsNegateSubcommands) - .setting(AppSettings::SubcommandsNegateReqs) - .get_matches_from(args); - let cli_args = CoreParams::::from_clap(&matches); - fdlimit::raise_fd_limit(); + .version(full_version.as_str()); - let args = match cli_args { - params::CoreParams::Run(params) => ParseAndPrepare::Run( - ParseAndPrepareRun { params, impl_name, version } - ), - params::CoreParams::BuildSpec(params) => ParseAndPrepare::BuildSpec( - ParseAndPrepareBuildSpec { params, version } - ), - params::CoreParams::ExportBlocks(params) => ParseAndPrepare::ExportBlocks( - ParseAndPrepareExport { params, version } - ), - params::CoreParams::ImportBlocks(params) => ParseAndPrepare::ImportBlocks( - ParseAndPrepareImport { params, version } - ), - params::CoreParams::CheckBlock(params) => ParseAndPrepare::CheckBlock( - CheckBlock { params, version } - ), - params::CoreParams::PurgeChain(params) => ParseAndPrepare::PurgeChain( - ParseAndPreparePurge { params, version } - ), - params::CoreParams::Revert(params) => ParseAndPrepare::RevertChain( - ParseAndPrepareRevert { params, version } - ), - params::CoreParams::Custom(params) => ParseAndPrepare::CustomCommand(params), - }; - init_logger(args.shared_params().and_then(|p| p.log.as_ref()).map(|v| v.as_ref()).unwrap_or("")); - args + T::from_clap(&app.get_matches_from(iter)) } -/// Returns a string displaying the node role, special casing the sentry mode -/// (returning `SENTRY`), since the node technically has an `AUTHORITY` role but -/// doesn't participate. -pub fn display_role(config: &Configuration) -> String { - if config.sentry_mode { - "SENTRY".to_string() - } else { - format!("{:?}", config.roles) - } -} +/// Helper function used to parse the command line arguments. This is the equivalent of +/// `structopt`'s `try_from_iter()` except that it takes a `VersionInfo` argument to provide the +/// name of the application, author, "about" and version. +/// +/// Gets the struct from any iterator such as a `Vec` of your making. +/// Print the error message and quit the program in case of failure. +/// +/// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are +/// used. It will return a [`clap::Error`], where the [`kind`] is a +/// [`ErrorKind::HelpDisplayed`] or [`ErrorKind::VersionDisplayed`] respectively. You must call +/// [`Error::exit`] or perform a [`std::process::exit`]. +pub fn try_from_iter(iter: I, version: &VersionInfo) -> clap::Result +where + T: StructOpt + Sized, + I: IntoIterator, + I::Item: Into + Clone, +{ + let app = T::clap(); -/// Output of calling `parse_and_prepare`. -#[must_use] -pub enum ParseAndPrepare<'a, CC, RP> { - /// Command ready to run the main client. - Run(ParseAndPrepareRun<'a, RP>), - /// Command ready to build chain specs. - BuildSpec(ParseAndPrepareBuildSpec<'a>), - /// Command ready to export the chain. - ExportBlocks(ParseAndPrepareExport<'a>), - /// Command ready to import the chain. - ImportBlocks(ParseAndPrepareImport<'a>), - /// Command to check a block. - CheckBlock(CheckBlock<'a>), - /// Command ready to purge the chain. - PurgeChain(ParseAndPreparePurge<'a>), - /// Command ready to revert the chain. - RevertChain(ParseAndPrepareRevert<'a>), - /// An additional custom command passed to `parse_and_prepare`. - CustomCommand(CC), -} + let mut full_version = sc_service::config::full_version_from_strs( + version.version, + version.commit, + ); + full_version.push_str("\n"); -impl<'a, CC, RP> ParseAndPrepare<'a, CC, RP> where CC: GetSharedParams { - /// Return common set of parameters shared by all commands. - pub fn shared_params(&self) -> Option<&SharedParams> { - match self { - ParseAndPrepare::Run(c) => Some(&c.params.left.shared_params), - ParseAndPrepare::BuildSpec(c) => Some(&c.params.shared_params), - ParseAndPrepare::ExportBlocks(c) => Some(&c.params.shared_params), - ParseAndPrepare::ImportBlocks(c) => Some(&c.params.shared_params), - ParseAndPrepare::CheckBlock(c) => Some(&c.params.shared_params), - ParseAndPrepare::PurgeChain(c) => Some(&c.params.shared_params), - ParseAndPrepare::RevertChain(c) => Some(&c.params.shared_params), - ParseAndPrepare::CustomCommand(c) => c.shared_params(), - } - } -} + let app = app + .name(version.executable_name) + .author(version.author) + .about(version.description) + .version(full_version.as_str()); -impl<'a, CC, RP> ParseAndPrepare<'a, CC, RP> { - /// Convert ParseAndPrepare to Configuration - pub fn into_configuration( - self, - spec_factory: S, - default_base_path: Option, - ) -> error::Result>> - where - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - S: FnOnce(&str) -> Result>, String>, - { - match self { - ParseAndPrepare::Run(c) => - Some(create_run_node_config( - c.params.left, - spec_factory, - c.impl_name, - c.version, - default_base_path, - )).transpose(), - ParseAndPrepare::BuildSpec(c) => { - let spec = load_spec(&c.params.shared_params, spec_factory)?; - - Some(create_build_spec_config( - &spec, - &c.params.shared_params, - c.version, - default_base_path, - )).transpose() - }, - ParseAndPrepare::ExportBlocks(c) => - Some(create_config_with_db_path( - spec_factory, - &c.params.shared_params, - c.version, - default_base_path, - )).transpose(), - ParseAndPrepare::ImportBlocks(c) => - Some(create_config_with_db_path( - spec_factory, - &c.params.shared_params, - c.version, - default_base_path, - )).transpose(), - ParseAndPrepare::CheckBlock(c) => - Some(create_config_with_db_path( - spec_factory, - &c.params.shared_params, - c.version, - default_base_path, - )).transpose(), - ParseAndPrepare::PurgeChain(c) => - Some(create_config_with_db_path( - spec_factory, - &c.params.shared_params, - c.version, - default_base_path, - )).transpose(), - ParseAndPrepare::RevertChain(c) => - Some(create_config_with_db_path( - spec_factory, - &c.params.shared_params, - c.version, - default_base_path, - )).transpose(), - ParseAndPrepare::CustomCommand(_) => Ok(None), - } - } -} + let matches = app.get_matches_from_safe(iter)?; -/// Command ready to run the main client. -pub struct ParseAndPrepareRun<'a, RP> { - params: MergeParameters, - impl_name: &'static str, - version: &'a VersionInfo, + Ok(T::from_clap(&matches)) } -impl<'a, RP> ParseAndPrepareRun<'a, RP> { - /// Runs the command and runs the main client. - pub fn run( - self, - spec_factory: S, - exit: Exit, - run_service: RS, - ) -> error::Result<()> - where - S: FnOnce(&str) -> Result>, String>, - E: Into, - RP: StructOpt + Clone, - C: Default, - G: RuntimeGenesis, - CE: ChainSpecExtension, - Exit: IntoExit, - RS: FnOnce(Exit, RunCmd, RP, Configuration) -> Result<(), E> - { - let config = create_run_node_config( - self.params.left.clone(), - spec_factory, - self.impl_name, - self.version, - None, - )?; - - run_service(exit, self.params.left, self.params.right, config).map_err(Into::into) - } +/// A helper function that initializes and runs the node +pub fn run( + mut config: Configuration, + run_cmd: RunCmd, + new_light: FNL, + new_full: FNF, + spec_factory: F, + version: &VersionInfo, +) -> error::Result<()> +where + F: FnOnce(&str) -> Result>, String>, + FNL: FnOnce(Configuration) -> Result, + FNF: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + SL: AbstractService + Unpin, + SF: AbstractService + Unpin, +{ + init(&run_cmd.shared_params, version)?; + init_config(&mut config, &run_cmd.shared_params, version, spec_factory)?; + run_cmd.run(config, new_light, new_full, version) } -/// Command ready to build chain specs. -pub struct ParseAndPrepareBuildSpec<'a> { - params: BuildSpecCmd, - version: &'a VersionInfo, +/// A helper function that initializes and runs any of the subcommand variants of `CoreParams`. +pub fn run_subcommand( + mut config: Configuration, + subcommand: Subcommand, + spec_factory: F, + builder: B, + version: &VersionInfo, +) -> error::Result<()> +where + F: FnOnce(&str) -> Result>, String>, + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, +{ + let shared_params = subcommand.get_shared_params(); + init(shared_params, version)?; + init_config(&mut config, shared_params, version, spec_factory)?; + subcommand.run(config, builder) } -impl<'a> ParseAndPrepareBuildSpec<'a> { - /// Runs the command and build the chain specs. - pub fn run( - self, - spec_factory: S - ) -> error::Result<()> where - S: FnOnce(&str) -> Result>, String>, - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - { - info!("Building chain spec"); - let raw_output = self.params.raw; - let mut spec = load_spec(&self.params.shared_params, spec_factory)?; - - if spec.boot_nodes().is_empty() && !self.params.disable_default_bootnode { - let cfg = create_build_spec_config::( - &spec, - &self.params.shared_params, - self.version, - None, - )?; - let node_key = node_key_config( - self.params.node_key_params, - &Some(cfg.in_chain_config_dir(DEFAULT_NETWORK_CONFIG_PATH).expect("We provided a base_path")) - )?; - let keys = node_key.into_keypair()?; - let peer_id = keys.public().into_peer_id(); - let addr = build_multiaddr![ - Ip4([127, 0, 0, 1]), - Tcp(30333u16), - P2p(peer_id) - ]; - spec.add_boot_node(addr) - } - - let json = sc_service::chain_ops::build_spec(spec, raw_output)?; - - print!("{}", json); +/// Initialize substrate. This must be done only once. +/// +/// This method: +/// +/// 1. Set the panic handler +/// 2. Raise the FD limit +/// 3. Initialize the logger +pub fn init(shared_params: &SharedParams, version: &VersionInfo) -> error::Result<()> { + let full_version = sc_service::config::full_version_from_strs( + version.version, + version.commit + ); + sp_panic_handler::set(version.support_url, &full_version); - Ok(()) - } -} + fdlimit::raise_fd_limit(); + init_logger(shared_params.log.as_ref().map(|v| v.as_ref()).unwrap_or("")); -/// Command ready to export the chain. -pub struct ParseAndPrepareExport<'a> { - params: ExportBlocksCmd, - version: &'a VersionInfo, + Ok(()) } -impl<'a> ParseAndPrepareExport<'a> { - /// Runs the command and exports from the chain. - pub fn run_with_builder( - self, - builder: F, - spec_factory: S, - exit: Exit, - ) -> error::Result<()> - where S: FnOnce(&str) -> Result>, String>, - F: FnOnce(Configuration) -> Result, - B: ServiceBuilderCommand, - <<<::Block as BlockT>::Header as HeaderT> - ::Number as FromStr>::Err: Debug, - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - Exit: IntoExit - { - let mut config = create_config_with_db_path( - spec_factory, - &self.params.shared_params, - self.version, - None, - )?; - fill_config_keystore_in_memory(&mut config)?; - - if let DatabaseConfig::Path { ref path, .. } = &config.database { - info!("DB path: {}", path.display()); - } - let from = self.params.from.and_then(|f| f.parse().ok()).unwrap_or(1); - let to = self.params.to.and_then(|t| t.parse().ok()); - - let json = self.params.json; - - let file: Box = match self.params.output { - Some(filename) => Box::new(File::create(filename)?), - None => Box::new(stdout()), - }; - - // Note: while we would like the user to handle the exit themselves, we handle it here - // for backwards compatibility reasons. - let (exit_send, exit_recv) = std::sync::mpsc::channel(); - let exit = exit.into_exit(); - std::thread::spawn(move || { - block_on(exit); - let _ = exit_send.send(()); - }); - - let mut export_fut = builder(config)? - .export_blocks(file, from.into(), to, json); - let fut = futures::future::poll_fn(|cx| { - if exit_recv.try_recv().is_ok() { - return Poll::Ready(Ok(())); - } - Pin::new(&mut export_fut).poll(cx) - }); +/// Initialize the given `config`. +/// +/// This will load the chain spec, set the `config_dir` and the `database_dir`. +pub fn init_config( + config: &mut Configuration, + shared_params: &SharedParams, + version: &VersionInfo, + spec_factory: F, +) -> error::Result<()> where + F: FnOnce(&str) -> Result>, String>, + G: RuntimeGenesis, + E: ChainSpecExtension, +{ + load_spec(config, shared_params, spec_factory)?; - let mut runtime = tokio::runtime::Runtime::new().unwrap(); - runtime.block_on(fut)?; - Ok(()) + if config.config_dir.is_none() { + config.config_dir = Some(base_path(&shared_params, version)); } -} - -/// Command ready to import the chain. -pub struct ParseAndPrepareImport<'a> { - params: ImportBlocksCmd, - version: &'a VersionInfo, -} -impl<'a> ParseAndPrepareImport<'a> { - /// Runs the command and imports to the chain. - pub fn run_with_builder( - self, - builder: F, - spec_factory: S, - exit: Exit, - ) -> error::Result<()> - where S: FnOnce(&str) -> Result>, String>, - F: FnOnce(Configuration) -> Result, - B: ServiceBuilderCommand, - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - Exit: IntoExit - { - let mut config = create_config_with_db_path( - spec_factory, - &self.params.shared_params, - self.version, - None, - )?; - fill_import_params( - &mut config, - &self.params.import_params, - sc_service::Roles::FULL, - self.params.shared_params.dev, - )?; - - let file: Box = match self.params.input { - Some(filename) => Box::new(File::open(filename)?), - None => { - let mut buffer = Vec::new(); - stdin().read_to_end(&mut buffer)?; - Box::new(Cursor::new(buffer)) - }, - }; - - // Note: while we would like the user to handle the exit themselves, we handle it here - // for backwards compatibility reasons. - let (exit_send, exit_recv) = std::sync::mpsc::channel(); - let exit = exit.into_exit(); - std::thread::spawn(move || { - block_on(exit); - let _ = exit_send.send(()); + if config.database.is_none() { + config.database = Some(DatabaseConfig::Path { + path: config + .in_chain_config_dir(DEFAULT_DB_CONFIG_PATH) + .expect("We provided a base_path/config_dir."), + cache_size: None, }); - - let mut import_fut = builder(config)? - .import_blocks(file, false); - let fut = futures::future::poll_fn(|cx| { - if exit_recv.try_recv().is_ok() { - return Poll::Ready(Ok(())); - } - Pin::new(&mut import_fut).poll(cx) - }); - - let mut runtime = tokio::runtime::Runtime::new().unwrap(); - runtime.block_on(fut)?; - Ok(()) - } -} - -/// Command to check a block. -pub struct CheckBlock<'a> { - params: CheckBlockCmd, - version: &'a VersionInfo, -} - -impl<'a> CheckBlock<'a> { - /// Runs the command and imports to the chain. - pub fn run_with_builder( - self, - builder: F, - spec_factory: S, - _exit: Exit, - ) -> error::Result<()> - where S: FnOnce(&str) -> Result>, String>, - F: FnOnce(Configuration) -> Result, - B: ServiceBuilderCommand, - <::Block as BlockT>::Hash: FromStr, - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - Exit: IntoExit - { - let mut config = create_config_with_db_path( - spec_factory, - &self.params.shared_params, - self.version, - None, - )?; - fill_import_params( - &mut config, - &self.params.import_params, - sc_service::Roles::FULL, - self.params.shared_params.dev, - )?; - fill_config_keystore_in_memory(&mut config)?; - - let input = if self.params.input.starts_with("0x") { &self.params.input[2..] } else { &self.params.input[..] }; - let block_id = match FromStr::from_str(input) { - Ok(hash) => BlockId::hash(hash), - Err(_) => match self.params.input.parse::() { - Ok(n) => BlockId::number((n as u32).into()), - Err(_) => return Err(error::Error::Input("Invalid hash or number specified".into())), - } - }; - - let start = std::time::Instant::now(); - let check = builder(config)? - .check_block(block_id); - let mut runtime = tokio::runtime::Runtime::new().unwrap(); - runtime.block_on(check)?; - println!("Completed in {} ms.", start.elapsed().as_millis()); - Ok(()) - } -} - -/// Command ready to purge the chain. -pub struct ParseAndPreparePurge<'a> { - params: PurgeChainCmd, - version: &'a VersionInfo, -} - -impl<'a> ParseAndPreparePurge<'a> { - /// Runs the command and purges the chain. - pub fn run( - self, - spec_factory: S - ) -> error::Result<()> where - S: FnOnce(&str) -> Result>, String>, - G: RuntimeGenesis, - E: ChainSpecExtension, - { - let mut config = create_config_with_db_path::<(), _, _, _>( - spec_factory, - &self.params.shared_params, - self.version, - None, - )?; - fill_config_keystore_in_memory(&mut config)?; - let db_path = match config.database { - DatabaseConfig::Path { path, .. } => path, - _ => { - eprintln!("Cannot purge custom database implementation"); - return Ok(()); - } - }; - - if !self.params.yes { - print!("Are you sure to remove {:?}? [y/N]: ", &db_path); - stdout().flush().expect("failed to flush stdout"); - - let mut input = String::new(); - stdin().read_line(&mut input)?; - let input = input.trim(); - - match input.chars().nth(0) { - Some('y') | Some('Y') => {}, - _ => { - println!("Aborted"); - return Ok(()); - }, - } - } - - match fs::remove_dir_all(&db_path) { - Result::Ok(_) => { - println!("{:?} removed.", &db_path); - Ok(()) - }, - Result::Err(ref err) if err.kind() == ErrorKind::NotFound => { - eprintln!("{:?} did not exist.", &db_path); - Ok(()) - }, - Result::Err(err) => Result::Err(err.into()) - } } -} - -/// Command ready to revert the chain. -pub struct ParseAndPrepareRevert<'a> { - params: RevertCmd, - version: &'a VersionInfo, -} - -impl<'a> ParseAndPrepareRevert<'a> { - /// Runs the command and reverts the chain. - pub fn run_with_builder( - self, - builder: F, - spec_factory: S - ) -> error::Result<()> where - S: FnOnce(&str) -> Result>, String>, - F: FnOnce(Configuration) -> Result, - B: ServiceBuilderCommand, - <<<::Block as BlockT>::Header as HeaderT> - ::Number as FromStr>::Err: Debug, - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - { - let mut config = create_config_with_db_path( - spec_factory, - &self.params.shared_params, - self.version, - None, - )?; - fill_config_keystore_in_memory(&mut config)?; - let blocks = self.params.num.parse()?; - builder(config)?.revert_chain(blocks)?; - Ok(()) - } + Ok(()) } -/// Create a `NodeKeyConfig` from the given `NodeKeyParams` in the context -/// of an optional network config storage directory. -fn node_key_config

(params: NodeKeyParams, net_config_dir: &Option

) - -> error::Result +/// Run the node +/// +/// Builds and runs either a full or a light node, depending on the `role` within the `Configuration`. +pub fn run_node( + config: Configuration, + new_light: FNL, + new_full: FNF, + version: &VersionInfo, +) -> error::Result<()> where - P: AsRef + FNL: FnOnce(Configuration) -> Result, + FNF: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + SL: AbstractService + Unpin, + SF: AbstractService + Unpin, { - match params.node_key_type { - NodeKeyType::Ed25519 => - params.node_key.as_ref().map(parse_ed25519_secret).unwrap_or_else(|| - Ok(params.node_key_file - .or_else(|| net_config_file(net_config_dir, NODE_KEY_ED25519_FILE)) - .map(sc_network::config::Secret::File) - .unwrap_or(sc_network::config::Secret::New))) - .map(NodeKeyConfig::Ed25519) + info!("{}", version.name); + info!(" version {}", config.full_version()); + info!(" by {}, {}-{}", version.author, version.copyright_start_year, Local::today().year()); + info!("Chain specification: {}", config.expect_chain_spec().name()); + info!("Node name: {}", config.name); + info!("Roles: {}", display_role(&config)); + + match config.roles { + ServiceRoles::LIGHT => run_service_until_exit( + config, + new_light, + ), + _ => run_service_until_exit( + config, + new_full, + ), } } -fn net_config_file

(net_config_dir: &Option

, name: &str) -> Option -where - P: AsRef -{ - net_config_dir.as_ref().map(|d| d.as_ref().join(name)) -} - -/// Create an error caused by an invalid node key argument. -fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { - error::Error::Input(format!("Invalid node key: {}", e)) -} - -/// Parse a Ed25519 secret key from a hex string into a `sc_network::Secret`. -fn parse_ed25519_secret(hex: &String) -> error::Result { - H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes| - sc_network::config::identity::ed25519::SecretKey::from_bytes(bytes) - .map(sc_network::config::Secret::Input) - .map_err(invalid_node_key)) +/// Returns a string displaying the node role, special casing the sentry mode +/// (returning `SENTRY`), since the node technically has an `AUTHORITY` role but +/// doesn't participate. +pub fn display_role(config: &Configuration) -> String { + if config.sentry_mode { + "SENTRY".to_string() + } else { + format!("{:?}", config.roles) + } } /// Fill the given `PoolConfiguration` by looking at the cli parameters. -fn fill_transaction_pool_configuration( - options: &mut Configuration, +fn fill_transaction_pool_configuration( + options: &mut Configuration, params: TransactionPoolParams, ) -> error::Result<()> { // ready queue @@ -843,10 +414,8 @@ fn fill_network_configuration( ]; } - config.public_addresses = Vec::new(); - config.client_version = client_id; - config.node_key = node_key_config(cli.node_key_params, &config.net_config_path)?; + config.node_key = node_key::node_key_config(cli.node_key_params, &config.net_config_path)?; config.in_peers = cli.in_peers; config.out_peers = cli.out_peers; @@ -855,6 +424,7 @@ fn fill_network_configuration( enable_mdns: !is_dev && !cli.no_mdns, allow_private_ipv4: !cli.no_private_ipv4, wasm_external_transport: None, + use_yamux_flow_control: cli.use_yamux_flow_control }; config.max_parallel_downloads = cli.max_parallel_downloads; @@ -869,7 +439,7 @@ fn input_keystore_password() -> Result { } /// Use in memory keystore config when it is not required at all. -fn fill_config_keystore_in_memory(config: &mut sc_service::Configuration) +pub fn fill_config_keystore_in_memory(config: &mut sc_service::Configuration) -> Result<(), String> { match &mut config.keystore { @@ -879,8 +449,8 @@ fn fill_config_keystore_in_memory(config: &mut sc_service::Configuratio } /// Fill the password field of the given config instance. -fn fill_config_keystore_password_and_path( - config: &mut sc_service::Configuration, +fn fill_config_keystore_password_and_path( + config: &mut sc_service::Configuration, cli: &RunCmd, ) -> Result<(), String> { let password = if cli.password_interactive { @@ -911,21 +481,17 @@ fn fill_config_keystore_password_and_path( } /// Put block import CLI params into `config` object. -pub fn fill_import_params( - config: &mut Configuration, +pub fn fill_import_params( + config: &mut Configuration, cli: &ImportParams, role: sc_service::Roles, is_dev: bool, ) -> error::Result<()> - where - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, +where + G: RuntimeGenesis, { - match config.database { - DatabaseConfig::Path { ref mut cache_size, .. } => - *cache_size = Some(cli.database_cache_size), - DatabaseConfig::Custom(_) => {}, + if let Some(DatabaseConfig::Path { ref mut cache_size, .. }) = config.database { + *cache_size = Some(cli.database_cache_size); } config.state_cache_size = cli.state_cache_size; @@ -975,32 +541,23 @@ pub fn fill_import_params( Ok(()) } -fn create_run_node_config( +/// Update and prepare a `Configuration` with command line parameters of `RunCmd` and `VersionInfo` +pub fn update_config_for_running_node( + mut config: &mut Configuration, cli: RunCmd, - spec_factory: S, - impl_name: &'static str, - version: &VersionInfo, - default_base_path: Option, -) -> error::Result> +) -> error::Result<()> where - C: Default, G: RuntimeGenesis, - E: ChainSpecExtension, - S: FnOnce(&str) -> Result>, String>, { - let mut config = create_config_with_db_path( - spec_factory, - &cli.shared_params, - &version, - default_base_path, - )?; - fill_config_keystore_password_and_path(&mut config, &cli)?; + let keyring = cli.get_keyring(); let is_dev = cli.shared_params.dev; - let is_authority = cli.validator || cli.sentry || is_dev || cli.keyring.account.is_some(); + let is_light = cli.light; + let is_authority = (cli.validator || cli.sentry || is_dev || keyring.is_some()) + && !is_light; let role = - if cli.light { + if is_light { sc_service::Roles::LIGHT } else if is_authority { sc_service::Roles::AUTHORITY @@ -1010,24 +567,18 @@ where fill_import_params(&mut config, &cli.import_params, role, is_dev)?; - config.impl_name = impl_name; - config.impl_commit = version.commit; - config.impl_version = version.version; - - config.name = match cli.name.or(cli.keyring.account.map(|a| a.to_string())) { - None => generate_node_name(), - Some(name) => name, + config.name = match (cli.name.as_ref(), keyring) { + (Some(name), _) => name.to_string(), + (_, Some(keyring)) => keyring.to_string(), + (None, None) => generate_node_name(), }; - match is_node_name_valid(&config.name) { - Ok(_) => (), - Err(msg) => Err( - error::Error::Input( - format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", - config.name, - msg - ) + if let Err(msg) = node_key::is_node_name_valid(&config.name) { + return Err(error::Error::Input( + format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", + config.name, + msg, ) - )? + )); } // set sentry mode (i.e. act as an authority but **never** actively participate) @@ -1054,20 +605,38 @@ where fill_transaction_pool_configuration(&mut config, cli.pool_config)?; - config.dev_key_seed = cli.keyring.account + config.dev_key_seed = keyring .map(|a| format!("//{}", a)).or_else(|| { - if is_dev { + if is_dev && !is_light { Some("//Alice".into()) } else { None } }); +<<<<<<< HEAD let rpc_interface: &str = interface_str(cli.rpc_external, cli.unsafe_rpc_external, cli.validator)?; let ws_interface: &str = interface_str(cli.ws_external, cli.unsafe_ws_external, cli.validator)?; config.rpc_http = Some(parse_address(&format!("{}:{}", rpc_interface, 9933), cli.rpc_port)?); config.rpc_ws = Some(parse_address(&format!("{}:{}", ws_interface, 9944), cli.ws_port)?); +======= + if config.rpc_http.is_none() || cli.rpc_port.is_some() { + let rpc_interface: &str = interface_str(cli.rpc_external, cli.unsafe_rpc_external, cli.validator)?; + config.rpc_http = Some(parse_address(&format!("{}:{}", rpc_interface, 9933), cli.rpc_port)?); + } + if config.rpc_ws.is_none() || cli.ws_port.is_some() { + let ws_interface: &str = interface_str(cli.ws_external, cli.unsafe_ws_external, cli.validator)?; + config.rpc_ws = Some(parse_address(&format!("{}:{}", ws_interface, 9944), cli.ws_port)?); + } + + if config.grafana_port.is_none() || cli.grafana_port.is_some() { + let grafana_interface: &str = if cli.grafana_external { "0.0.0.0" } else { "127.0.0.1" }; + config.grafana_port = Some( + parse_address(&format!("{}:{}", grafana_interface, 9955), cli.grafana_port)? + ); + } +>>>>>>> master config.rpc_ws_max_connections = cli.ws_max_connections; config.rpc_cors = cli.rpc_cors.unwrap_or_else(|| if is_dev { @@ -1104,7 +673,7 @@ where // Imply forced authoring on --dev config.force_authoring = cli.shared_params.dev || cli.force_authoring; - Ok(config) + Ok(()) } fn interface_str( @@ -1128,61 +697,6 @@ fn interface_str( } } -/// Creates a configuration including the database path. -pub fn create_config_with_db_path( - spec_factory: S, - cli: &SharedParams, - version: &VersionInfo, - default_base_path: Option, -) -> error::Result> -where - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, - S: FnOnce(&str) -> Result>, String>, -{ - let spec = load_spec(cli, spec_factory)?; - let base_path = base_path(cli, version, default_base_path); - - let mut config = sc_service::Configuration::default_with_spec_and_base_path( - spec.clone(), - Some(base_path), - ); - - config.database = DatabaseConfig::Path { - path: config.in_chain_config_dir(DEFAULT_DB_CONFIG_PATH).expect("We provided a base_path."), - cache_size: None, - }; - - Ok(config) -} - -/// Creates a configuration including the base path and the shared params -fn create_build_spec_config( - spec: &ChainSpec, - cli: &SharedParams, - version: &VersionInfo, - default_base_path: Option, -) -> error::Result> -where - C: Default, - G: RuntimeGenesis, - E: ChainSpecExtension, -{ - let base_path = base_path(&cli, version, default_base_path); - let cfg = sc_service::Configuration::::default_with_spec_and_base_path( - spec.clone(), - Some(base_path), - ); - - Ok(cfg) -} - -/// Internal trait used to cast to a dynamic type that implements Read and Seek. -trait ReadPlusSeek: Read + Seek {} - -impl ReadPlusSeek for T {} - fn parse_address( address: &str, port: Option, @@ -1197,7 +711,8 @@ fn parse_address( Ok(address) } -fn init_logger(pattern: &str) { +/// Initialize the logger +pub fn init_logger(pattern: &str) { use ansi_term::Colour; let mut builder = env_logger::Builder::new(); @@ -1269,118 +784,17 @@ fn kill_color(s: &str) -> String { #[cfg(test)] mod tests { use super::*; - use sc_network::config::identity::ed25519; - - #[test] - fn tests_node_name_good() { - assert!(is_node_name_valid("short name").is_ok()); - } - - #[test] - fn tests_node_name_bad() { - assert!(is_node_name_valid("long names are not very cool for the ui").is_err()); - assert!(is_node_name_valid("Dots.not.Ok").is_err()); - assert!(is_node_name_valid("http://visit.me").is_err()); - assert!(is_node_name_valid("https://visit.me").is_err()); - assert!(is_node_name_valid("www.visit.me").is_err()); - assert!(is_node_name_valid("email@domain").is_err()); - } - - #[test] - fn test_node_key_config_input() { - fn secret_input(net_config_dir: Option) -> error::Result<()> { - NodeKeyType::variants().into_iter().try_for_each(|t| { - let node_key_type = NodeKeyType::from_str(t).unwrap(); - let sk = match node_key_type { - NodeKeyType::Ed25519 => ed25519::SecretKey::generate().as_ref().to_vec() - }; - let params = NodeKeyParams { - node_key_type, - node_key: Some(format!("{:x}", H256::from_slice(sk.as_ref()))), - node_key_file: None - }; - node_key_config(params, &net_config_dir).and_then(|c| match c { - NodeKeyConfig::Ed25519(sc_network::config::Secret::Input(ref ski)) - if node_key_type == NodeKeyType::Ed25519 && - &sk[..] == ski.as_ref() => Ok(()), - _ => Err(error::Error::Input("Unexpected node key config".into())) - }) - }) - } - - assert!(secret_input(None).is_ok()); - assert!(secret_input(Some("x".to_string())).is_ok()); - } - - #[test] - fn test_node_key_config_file() { - fn secret_file(net_config_dir: Option) -> error::Result<()> { - NodeKeyType::variants().into_iter().try_for_each(|t| { - let node_key_type = NodeKeyType::from_str(t).unwrap(); - let tmp = tempfile::Builder::new().prefix("alice").tempdir()?; - let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf(); - let params = NodeKeyParams { - node_key_type, - node_key: None, - node_key_file: Some(file.clone()) - }; - node_key_config(params, &net_config_dir).and_then(|c| match c { - NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) - if node_key_type == NodeKeyType::Ed25519 && f == &file => Ok(()), - _ => Err(error::Error::Input("Unexpected node key config".into())) - }) - }) - } - - assert!(secret_file(None).is_ok()); - assert!(secret_file(Some("x".to_string())).is_ok()); - } - - #[test] - fn test_node_key_config_default() { - fn with_def_params(f: F) -> error::Result<()> - where - F: Fn(NodeKeyParams) -> error::Result<()> - { - NodeKeyType::variants().into_iter().try_for_each(|t| { - let node_key_type = NodeKeyType::from_str(t).unwrap(); - f(NodeKeyParams { - node_key_type, - node_key: None, - node_key_file: None - }) - }) - } - - fn no_config_dir() -> error::Result<()> { - with_def_params(|params| { - let typ = params.node_key_type; - node_key_config::(params, &None) - .and_then(|c| match c { - NodeKeyConfig::Ed25519(sc_network::config::Secret::New) - if typ == NodeKeyType::Ed25519 => Ok(()), - _ => Err(error::Error::Input("Unexpected node key config".into())) - }) - }) - } - - fn some_config_dir(net_config_dir: String) -> error::Result<()> { - with_def_params(|params| { - let dir = PathBuf::from(net_config_dir.clone()); - let typ = params.node_key_type; - node_key_config(params, &Some(net_config_dir.clone())) - .and_then(move |c| match c { - NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) - if typ == NodeKeyType::Ed25519 && - f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()), - _ => Err(error::Error::Input("Unexpected node key config".into())) - }) - }) - } - assert!(no_config_dir().is_ok()); - assert!(some_config_dir("x".to_string()).is_ok()); - } + const TEST_VERSION_INFO: &'static VersionInfo = &VersionInfo { + name: "node-test", + version: "0.1.0", + commit: "some_commit", + executable_name: "node-test", + description: "description", + author: "author", + support_url: "http://example.org", + copyright_start_year: 2020, + }; #[test] fn keystore_path_is_generated_correctly() { @@ -1392,30 +806,20 @@ mod tests { None, None, None, - None, + None::<()>, ); - let version_info = VersionInfo { - name: "test", - version: "42", - commit: "234234", - executable_name: "test", - description: "cool test", - author: "universe", - support_url: "com", - }; - for keystore_path in vec![None, Some("/keystore/path")] { - let mut run_cmds = RunCmd::from_args(); - run_cmds.shared_params.base_path = Some(PathBuf::from("/test/path")); + let args: Vec<&str> = vec![]; + let mut run_cmds = RunCmd::from_iter(args); run_cmds.keystore_path = keystore_path.clone().map(PathBuf::from); - let node_config = create_run_node_config::<(), _, _, _>( + let mut node_config = Configuration::default(); + node_config.config_dir = Some(PathBuf::from("/test/path")); + node_config.chain_spec = Some(chain_spec.clone()); + update_config_for_running_node( + &mut node_config, run_cmds.clone(), - |_| Ok(Some(chain_spec.clone())), - "test", - &version_info, - None, ).unwrap(); let expected_path = match keystore_path { @@ -1428,42 +832,64 @@ mod tests { } #[test] - fn parse_and_prepare_into_configuration() { + fn ensure_load_spec_provide_defaults() { let chain_spec = ChainSpec::from_genesis( "test", "test-id", || (), - Vec::new(), + vec!["boo".to_string()], + Some(TelemetryEndpoints::new(vec![("foo".to_string(), 42)])), None, None, + None::<()>, + ); + + let args: Vec<&str> = vec![]; + let cli = RunCmd::from_iter(args); + + let mut config = Configuration::new(TEST_VERSION_INFO); + load_spec(&mut config, &cli.shared_params, |_| Ok(Some(chain_spec))).unwrap(); + + assert!(config.chain_spec.is_some()); + assert!(!config.network.boot_nodes.is_empty()); + assert!(config.telemetry_endpoints.is_some()); + } + + #[test] + fn ensure_update_config_for_running_node_provides_defaults() { + let chain_spec = ChainSpec::from_genesis( + "test", + "test-id", + || (), + vec![], + None, None, None, + None::<()>, ); - let version = VersionInfo { - name: "test", - version: "42", - commit: "234234", - executable_name: "test", - description: "cool test", - author: "universe", - support_url: "com", - }; - let spec_factory = |_: &str| Ok(Some(chain_spec.clone())); - - let args = vec!["substrate", "--dev", "--state-cache-size=42"]; - let pnp = parse_and_prepare::(&version, "test", args); - let config = pnp.into_configuration::<(), _, _, _>(spec_factory, None).unwrap().unwrap(); - assert_eq!(config.roles, sc_service::Roles::AUTHORITY); - assert_eq!(config.state_cache_size, 42); - - let args = vec!["substrate", "import-blocks", "--dev"]; - let pnp = parse_and_prepare::(&version, "test", args); - let config = pnp.into_configuration::<(), _, _, _>(spec_factory, None).unwrap().unwrap(); - assert_eq!(config.roles, sc_service::Roles::FULL); - - let args = vec!["substrate", "--base-path=/foo"]; - let pnp = parse_and_prepare::(&version, "test", args); - let config = pnp.into_configuration::<(), _, _, _>(spec_factory, Some("/bar".into())).unwrap().unwrap(); - assert_eq!(config.config_dir, Some("/foo".into())); + + let args: Vec<&str> = vec![]; + let cli = RunCmd::from_iter(args); + + let mut config = Configuration::new(TEST_VERSION_INFO); + init(&cli.shared_params, &TEST_VERSION_INFO).unwrap(); + init_config( + &mut config, + &cli.shared_params, + &TEST_VERSION_INFO, + |_| Ok(Some(chain_spec)), + ).unwrap(); + update_config_for_running_node(&mut config, cli).unwrap(); + + assert!(config.config_dir.is_some()); + assert!(config.database.is_some()); + if let Some(DatabaseConfig::Path { ref cache_size, .. }) = config.database { + assert!(cache_size.is_some()); + } else { + panic!("invalid config.database variant"); + } + assert!(!config.name.is_empty()); + assert!(config.network.config_path.is_some()); + assert!(!config.network.listen_addresses.is_empty()); } } diff --git a/client/cli/src/node_key.rs b/client/cli/src/node_key.rs new file mode 100644 index 0000000000000000000000000000000000000000..4401481ca56ce1ca171cc8ab9c9ade5ee5874ee7 --- /dev/null +++ b/client/cli/src/node_key.rs @@ -0,0 +1,209 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use sc_network::{ + self, + config::{ + NodeKeyConfig, + }, +}; +use sp_core::H256; +use regex::Regex; +use std::{path::{Path, PathBuf}, str::FromStr}; +use crate::error; +use crate::params::{NodeKeyParams, NodeKeyType}; + +/// The file name of the node's Ed25519 secret key inside the chain-specific +/// network config directory, if neither `--node-key` nor `--node-key-file` +/// is specified in combination with `--node-key-type=ed25519`. +const NODE_KEY_ED25519_FILE: &str = "secret_ed25519"; + +/// Check whether a node name is considered as valid +pub fn is_node_name_valid(_name: &str) -> Result<(), &str> { + let name = _name.to_string(); + if name.chars().count() >= crate::NODE_NAME_MAX_LENGTH { + return Err("Node name too long"); + } + + let invalid_chars = r"[\\.@]"; + let re = Regex::new(invalid_chars).unwrap(); + if re.is_match(&name) { + return Err("Node name should not contain invalid chars such as '.' and '@'"); + } + + let invalid_patterns = r"(https?:\\/+)?(www)+"; + let re = Regex::new(invalid_patterns).unwrap(); + if re.is_match(&name) { + return Err("Node name should not contain urls"); + } + + Ok(()) +} + +/// Create a `NodeKeyConfig` from the given `NodeKeyParams` in the context +/// of an optional network config storage directory. +pub fn node_key_config

(params: NodeKeyParams, net_config_dir: &Option

) + -> error::Result +where + P: AsRef +{ + match params.node_key_type { + NodeKeyType::Ed25519 => + params.node_key.as_ref().map(parse_ed25519_secret).unwrap_or_else(|| + Ok(params.node_key_file + .or_else(|| net_config_file(net_config_dir, NODE_KEY_ED25519_FILE)) + .map(sc_network::config::Secret::File) + .unwrap_or(sc_network::config::Secret::New))) + .map(NodeKeyConfig::Ed25519) + } +} + +/// Create an error caused by an invalid node key argument. +fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { + error::Error::Input(format!("Invalid node key: {}", e)) +} + +/// Parse a Ed25519 secret key from a hex string into a `sc_network::Secret`. +fn parse_ed25519_secret(hex: &String) -> error::Result { + H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes| + sc_network::config::identity::ed25519::SecretKey::from_bytes(bytes) + .map(sc_network::config::Secret::Input) + .map_err(invalid_node_key)) +} + +fn net_config_file

(net_config_dir: &Option

, name: &str) -> Option +where + P: AsRef +{ + net_config_dir.as_ref().map(|d| d.as_ref().join(name)) +} + +#[cfg(test)] +mod tests { + use sc_network::config::identity::ed25519; + use super::*; + + #[test] + fn tests_node_name_good() { + assert!(is_node_name_valid("short name").is_ok()); + } + + #[test] + fn tests_node_name_bad() { + assert!(is_node_name_valid("long names are not very cool for the ui").is_err()); + assert!(is_node_name_valid("Dots.not.Ok").is_err()); + assert!(is_node_name_valid("http://visit.me").is_err()); + assert!(is_node_name_valid("https://visit.me").is_err()); + assert!(is_node_name_valid("www.visit.me").is_err()); + assert!(is_node_name_valid("email@domain").is_err()); + } + + #[test] + fn test_node_key_config_input() { + fn secret_input(net_config_dir: Option) -> error::Result<()> { + NodeKeyType::variants().iter().try_for_each(|t| { + let node_key_type = NodeKeyType::from_str(t).unwrap(); + let sk = match node_key_type { + NodeKeyType::Ed25519 => ed25519::SecretKey::generate().as_ref().to_vec() + }; + let params = NodeKeyParams { + node_key_type, + node_key: Some(format!("{:x}", H256::from_slice(sk.as_ref()))), + node_key_file: None + }; + node_key_config(params, &net_config_dir).and_then(|c| match c { + NodeKeyConfig::Ed25519(sc_network::config::Secret::Input(ref ski)) + if node_key_type == NodeKeyType::Ed25519 && + &sk[..] == ski.as_ref() => Ok(()), + _ => Err(error::Error::Input("Unexpected node key config".into())) + }) + }) + } + + assert!(secret_input(None).is_ok()); + assert!(secret_input(Some("x".to_string())).is_ok()); + } + + #[test] + fn test_node_key_config_file() { + fn secret_file(net_config_dir: Option) -> error::Result<()> { + NodeKeyType::variants().iter().try_for_each(|t| { + let node_key_type = NodeKeyType::from_str(t).unwrap(); + let tmp = tempfile::Builder::new().prefix("alice").tempdir()?; + let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf(); + let params = NodeKeyParams { + node_key_type, + node_key: None, + node_key_file: Some(file.clone()) + }; + node_key_config(params, &net_config_dir).and_then(|c| match c { + NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) + if node_key_type == NodeKeyType::Ed25519 && f == &file => Ok(()), + _ => Err(error::Error::Input("Unexpected node key config".into())) + }) + }) + } + + assert!(secret_file(None).is_ok()); + assert!(secret_file(Some("x".to_string())).is_ok()); + } + + #[test] + fn test_node_key_config_default() { + fn with_def_params(f: F) -> error::Result<()> + where + F: Fn(NodeKeyParams) -> error::Result<()> + { + NodeKeyType::variants().iter().try_for_each(|t| { + let node_key_type = NodeKeyType::from_str(t).unwrap(); + f(NodeKeyParams { + node_key_type, + node_key: None, + node_key_file: None + }) + }) + } + + fn no_config_dir() -> error::Result<()> { + with_def_params(|params| { + let typ = params.node_key_type; + node_key_config::(params, &None) + .and_then(|c| match c { + NodeKeyConfig::Ed25519(sc_network::config::Secret::New) + if typ == NodeKeyType::Ed25519 => Ok(()), + _ => Err(error::Error::Input("Unexpected node key config".into())) + }) + }) + } + + fn some_config_dir(net_config_dir: String) -> error::Result<()> { + with_def_params(|params| { + let dir = PathBuf::from(net_config_dir.clone()); + let typ = params.node_key_type; + node_key_config(params, &Some(net_config_dir.clone())) + .and_then(move |c| match c { + NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) + if typ == NodeKeyType::Ed25519 && + f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()), + _ => Err(error::Error::Input("Unexpected node key config".into())) + }) + }) + } + + assert!(no_config_dir().is_ok()); + assert!(some_config_dir("x".to_string()).is_ok()); + } +} diff --git a/client/cli/src/params.rs b/client/cli/src/params.rs index 1a4b284432b1914a1b6dc072cf48398446e9555e..bb369eb56156775f39131d6bdd0b6cba63705e6d 100644 --- a/client/cli/src/params.rs +++ b/client/cli/src/params.rs @@ -14,10 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::traits::GetSharedParams; - use std::{str::FromStr, path::PathBuf}; -use structopt::{StructOpt, StructOptInternal, clap::{arg_enum, App, AppSettings, SubCommand, Arg}}; +use structopt::{StructOpt, clap::arg_enum}; +use sc_service::{ + AbstractService, Configuration, ChainSpecExtension, RuntimeGenesis, ServiceBuilderCommand, + config::DatabaseConfig, +}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use crate::VersionInfo; +use crate::error; +use std::fmt::Debug; +use log::info; +use sc_network::config::build_multiaddr; +use std::io; +use std::fs; +use std::io::{Read, Write, Seek}; +use sp_runtime::generic::BlockId; +use crate::runtime::run_until_exit; +use crate::node_key::node_key_config; use crate::execution_strategy::*; pub use crate::execution_strategy::ExecutionStrategy; @@ -141,6 +155,20 @@ pub struct ImportParams { /// Specify the state cache size. #[structopt(long = "state-cache-size", value_name = "Bytes", default_value = "67108864")] pub state_cache_size: usize, + + /// Comma separated list of targets for tracing + #[structopt(long = "tracing-targets", value_name = "TARGETS")] + pub tracing_targets: Option, + + /// Receiver to process tracing messages + #[structopt( + long = "tracing-receiver", + value_name = "RECEIVER", + possible_values = &TracingReceiver::variants(), + case_insensitive = true, + default_value = "Log" + )] + pub tracing_receiver: TracingReceiver, } /// Parameters used to create the network configuration. @@ -210,6 +238,10 @@ pub struct NetworkConfigurationParams { #[allow(missing_docs)] #[structopt(flatten)] pub node_key_params: NodeKeyParams, + + /// Experimental feature flag. + #[structopt(long = "use-yamux-flow-control")] + pub use_yamux_flow_control: bool, } arg_enum! { @@ -286,10 +318,10 @@ pub struct NodeKeyParams { #[derive(Debug, StructOpt, Clone)] pub struct TransactionPoolParams { /// Maximum number of transactions in the transaction pool. - #[structopt(long = "pool-limit", value_name = "COUNT", default_value = "512")] + #[structopt(long = "pool-limit", value_name = "COUNT", default_value = "8192")] pub pool_limit: usize, /// Maximum number of kilobytes of all transactions stored in the pool. - #[structopt(long = "pool-kbytes", value_name = "COUNT", default_value = "10240")] + #[structopt(long = "pool-kbytes", value_name = "COUNT", default_value = "20480")] pub pool_kbytes: usize, } @@ -406,7 +438,7 @@ pub struct RunCmd { /// available to relay to private nodes. #[structopt( long = "sentry", - conflicts_with_all = &[ "validator" ] + conflicts_with_all = &[ "validator", "light" ] )] pub sentry: bool, @@ -415,7 +447,7 @@ pub struct RunCmd { pub no_grandpa: bool, /// Experimental: Run in light client mode. - #[structopt(long = "light")] + #[structopt(long = "light", conflicts_with = "sentry")] pub light: bool, /// Listen to all RPC interfaces. @@ -533,28 +565,42 @@ pub struct RunCmd { #[structopt(flatten)] pub pool_config: TransactionPoolParams, - #[allow(missing_docs)] - #[structopt(flatten)] - pub keyring: Keyring, + /// Shortcut for `--name Alice --validator` with session keys for `Alice` added to keystore. + #[structopt(long, conflicts_with_all = &["bob", "charlie", "dave", "eve", "ferdie", "one", "two"])] + pub alice: bool, + + /// Shortcut for `--name Bob --validator` with session keys for `Bob` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "charlie", "dave", "eve", "ferdie", "one", "two"])] + pub bob: bool, + + /// Shortcut for `--name Charlie --validator` with session keys for `Charlie` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "bob", "dave", "eve", "ferdie", "one", "two"])] + pub charlie: bool, + + /// Shortcut for `--name Dave --validator` with session keys for `Dave` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "eve", "ferdie", "one", "two"])] + pub dave: bool, + + /// Shortcut for `--name Eve --validator` with session keys for `Eve` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "ferdie", "one", "two"])] + pub eve: bool, + + /// Shortcut for `--name Ferdie --validator` with session keys for `Ferdie` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "one", "two"])] + pub ferdie: bool, + + /// Shortcut for `--name One --validator` with session keys for `One` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "ferdie", "two"])] + pub one: bool, + + /// Shortcut for `--name Two --validator` with session keys for `Two` added to keystore. + #[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "ferdie", "one"])] + pub two: bool, /// Enable authoring even when offline. #[structopt(long = "force-authoring")] pub force_authoring: bool, - /// Comma separated list of targets for tracing - #[structopt(long = "tracing-targets", value_name = "TARGETS")] - pub tracing_targets: Option, - - /// Receiver to process tracing messages - #[structopt( - long = "tracing-receiver", - value_name = "RECEIVER", - possible_values = &TracingReceiver::variants(), - case_insensitive = true, - default_value = "Log" - )] - pub tracing_receiver: TracingReceiver, - /// Specify custom keystore path. #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] pub keystore_path: Option, @@ -583,71 +629,20 @@ pub struct RunCmd { pub password_filename: Option } -/// Stores all required Cli values for a keyring test account. -struct KeyringTestAccountCliValues { - help: String, - conflicts_with: Vec, - name: String, - variant: sp_keyring::Sr25519Keyring, -} - -lazy_static::lazy_static! { - /// The Cli values for all test accounts. - static ref TEST_ACCOUNTS_CLI_VALUES: Vec = { - sp_keyring::Sr25519Keyring::iter().map(|a| { - let help = format!( - "Shortcut for `--name {} --validator` with session keys for `{}` added to keystore.", - a, - a, - ); - let conflicts_with = sp_keyring::Sr25519Keyring::iter() - .filter(|b| a != *b) - .map(|b| b.to_string().to_lowercase()) - .chain(std::iter::once("name".to_string())) - .collect::>(); - let name = a.to_string().to_lowercase(); - - KeyringTestAccountCliValues { - help, - conflicts_with, - name, - variant: a, - } - }).collect() - }; -} - -/// Wrapper for exposing the keyring test accounts into the Cli. -#[derive(Debug, Clone)] -pub struct Keyring { - pub account: Option, -} - -impl StructOpt for Keyring { - fn clap<'a, 'b>() -> App<'a, 'b> { - unimplemented!("Should not be called for `TestAccounts`.") - } - - fn from_clap(m: &structopt::clap::ArgMatches) -> Self { - Keyring { - account: TEST_ACCOUNTS_CLI_VALUES.iter().find(|a| m.is_present(&a.name)).map(|a| a.variant), - } - } -} - -impl StructOptInternal for Keyring { - fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { - TEST_ACCOUNTS_CLI_VALUES.iter().fold(app, |app, a| { - let conflicts_with_strs = a.conflicts_with.iter().map(|s| s.as_str()).collect::>(); - - app.arg( - Arg::with_name(&a.name) - .long(&a.name) - .help(&a.help) - .conflicts_with_all(&conflicts_with_strs) - .takes_value(false) - ) - }) +impl RunCmd { + /// Get the `Sr25519Keyring` matching one of the flag + pub fn get_keyring(&self) -> Option { + use sp_keyring::Sr25519Keyring::*; + + if self.alice { Some(Alice) } + else if self.bob { Some(Bob) } + else if self.charlie { Some(Charlie) } + else if self.dave { Some(Dave) } + else if self.eve { Some(Eve) } + else if self.ferdie { Some(Ferdie) } + else if self.one { Some(One) } + else if self.two { Some(Two) } + else { None } } } @@ -858,16 +853,56 @@ pub struct PurgeChainCmd { pub shared_params: SharedParams, } +/// The `benchmark` command used to benchmark FRAME Pallets. +#[derive(Debug, StructOpt, Clone)] +pub struct BenchmarkCmd { + /// Select a FRAME Pallet to benchmark. + #[structopt(short, long)] + pub pallet: String, + + /// Select an extrinsic to benchmark. + #[structopt(short, long)] + pub extrinsic: String, + + /// Select how many samples we should take across the variable components. + #[structopt(short, long, default_value = "1")] + pub steps: u32, + + /// Select how many repetitions of this benchmark should run. + #[structopt(short, long, default_value = "1")] + pub repeat: u32, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + /// The execution strategy that should be used for benchmarks + #[structopt( + long = "execution", + value_name = "STRATEGY", + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + )] + pub execution: Option, + + /// Method for executing Wasm runtime code. + #[structopt( + long = "wasm-execution", + value_name = "METHOD", + possible_values = &WasmExecutionMethod::enabled_variants(), + case_insensitive = true, + default_value = "Interpreted" + )] + pub wasm_method: WasmExecutionMethod, +} + /// All core commands that are provided by default. /// /// The core commands are split into multiple subcommands and `Run` is the default subcommand. From /// the CLI user perspective, it is not visible that `Run` is a subcommand. So, all parameters of /// `Run` are exported as main executable parameters. -#[derive(Debug, Clone)] -pub enum CoreParams { - /// Run a node. - Run(MergeParameters), - +#[derive(Debug, Clone, StructOpt)] +pub enum Subcommand { /// Build a spec.json file, outputing to stdout. BuildSpec(BuildSpecCmd), @@ -886,111 +921,353 @@ pub enum CoreParams { /// Remove the whole chain data. PurgeChain(PurgeChainCmd), - /// Further custom subcommands. - Custom(CC), -} - -impl StructOpt for CoreParams where - CC: StructOpt + GetSharedParams, - RP: StructOpt + StructOptInternal, -{ - fn clap<'a, 'b>() -> App<'a, 'b> { - RP::augment_clap( - RunCmd::augment_clap( - CC::clap().unset_setting(AppSettings::SubcommandRequiredElseHelp) - ) - ).subcommand( - BuildSpecCmd::augment_clap(SubCommand::with_name("build-spec")) - .about("Build a spec.json file, outputting to stdout.") - ) - .subcommand( - ExportBlocksCmd::augment_clap(SubCommand::with_name("export-blocks")) - .about("Export blocks to a file. This file can only be re-imported \ - if it is in binary format (not JSON!)." - ) - ) - .subcommand( - ImportBlocksCmd::augment_clap(SubCommand::with_name("import-blocks")) - .about("Import blocks from file.") - ) - .subcommand( - CheckBlockCmd::augment_clap(SubCommand::with_name("check-block")) - .about("Re-validate a known block.") - ) - .subcommand( - RevertCmd::augment_clap(SubCommand::with_name("revert")) - .about("Revert chain to the previous state.") - ) - .subcommand( - PurgeChainCmd::augment_clap(SubCommand::with_name("purge-chain")) - .about("Remove the whole chain data.") - ) + /// Run runtime benchmarks. + Benchmark(BenchmarkCmd), +} + +impl Subcommand { + /// Get the shared parameters of a `CoreParams` command + pub fn get_shared_params(&self) -> &SharedParams { + use Subcommand::*; + + match self { + BuildSpec(params) => ¶ms.shared_params, + ExportBlocks(params) => ¶ms.shared_params, + ImportBlocks(params) => ¶ms.shared_params, + CheckBlock(params) => ¶ms.shared_params, + Revert(params) => ¶ms.shared_params, + PurgeChain(params) => ¶ms.shared_params, + Benchmark(params) => ¶ms.shared_params, + } } - fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self { - match matches.subcommand() { - ("build-spec", Some(matches)) => - CoreParams::BuildSpec(BuildSpecCmd::from_clap(matches)), - ("export-blocks", Some(matches)) => - CoreParams::ExportBlocks(ExportBlocksCmd::from_clap(matches)), - ("import-blocks", Some(matches)) => - CoreParams::ImportBlocks(ImportBlocksCmd::from_clap(matches)), - ("check-block", Some(matches)) => - CoreParams::CheckBlock(CheckBlockCmd::from_clap(matches)), - ("revert", Some(matches)) => CoreParams::Revert(RevertCmd::from_clap(matches)), - ("purge-chain", Some(matches)) => - CoreParams::PurgeChain(PurgeChainCmd::from_clap(matches)), - (_, None) => CoreParams::Run(MergeParameters::from_clap(matches)), - _ => CoreParams::Custom(CC::from_clap(matches)), + /// Run any `CoreParams` command + pub fn run( + self, + config: Configuration, + builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); + + match self { + Subcommand::BuildSpec(cmd) => cmd.run(config), + Subcommand::ExportBlocks(cmd) => cmd.run(config, builder), + Subcommand::ImportBlocks(cmd) => cmd.run(config, builder), + Subcommand::CheckBlock(cmd) => cmd.run(config, builder), + Subcommand::PurgeChain(cmd) => cmd.run(config), + Subcommand::Benchmark(cmd) => cmd.run(config, builder), + Subcommand::Revert(cmd) => cmd.run(config, builder), } } } -/// A special commandline parameter that expands to nothing. -/// Should be used as custom subcommand/run arguments if no custom values are required. -#[derive(Clone, Debug, Default)] -pub struct NoCustom {} +impl RunCmd { + /// Run the command that runs the node + pub fn run( + self, + mut config: Configuration, + new_light: FNL, + new_full: FNF, + version: &VersionInfo, + ) -> error::Result<()> + where + G: RuntimeGenesis, + E: ChainSpecExtension, + FNL: FnOnce(Configuration) -> Result, + FNF: FnOnce(Configuration) -> Result, + SL: AbstractService + Unpin, + SF: AbstractService + Unpin, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); -impl StructOpt for NoCustom { - fn clap<'a, 'b>() -> App<'a, 'b> { - App::new("NoCustom") - } + crate::update_config_for_running_node(&mut config, self)?; - fn from_clap(_: &::structopt::clap::ArgMatches) -> Self { - NoCustom {} + crate::run_node(config, new_light, new_full, &version) } } -impl StructOptInternal for NoCustom { - fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { - app +impl BuildSpecCmd { + /// Run the build-spec command + pub fn run( + self, + config: Configuration, + ) -> error::Result<()> + where + G: RuntimeGenesis, + E: ChainSpecExtension, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); + + info!("Building chain spec"); + let mut spec = config.expect_chain_spec().clone(); + let raw_output = self.raw; + + if spec.boot_nodes().is_empty() && !self.disable_default_bootnode { + let node_key = node_key_config( + self.node_key_params.clone(), + &Some(config + .in_chain_config_dir(crate::DEFAULT_NETWORK_CONFIG_PATH) + .expect("We provided a base_path")), + )?; + let keys = node_key.into_keypair()?; + let peer_id = keys.public().into_peer_id(); + let addr = build_multiaddr![ + Ip4([127, 0, 0, 1]), + Tcp(30333u16), + P2p(peer_id) + ]; + spec.add_boot_node(addr) + } + + let json = sc_service::chain_ops::build_spec(spec, raw_output)?; + + print!("{}", json); + + Ok(()) } } -impl GetSharedParams for NoCustom { - fn shared_params(&self) -> Option<&SharedParams> { - None +impl ExportBlocksCmd { + /// Run the export-blocks command + pub fn run( + self, + mut config: Configuration, + builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); + + crate::fill_config_keystore_in_memory(&mut config)?; + + if let DatabaseConfig::Path { ref path, .. } = config.expect_database() { + info!("DB path: {}", path.display()); + } + let from = self.from.as_ref().and_then(|f| f.parse().ok()).unwrap_or(1); + let to = self.to.as_ref().and_then(|t| t.parse().ok()); + + let json = self.json; + + let file: Box = match &self.output { + Some(filename) => Box::new(fs::File::create(filename)?), + None => Box::new(io::stdout()), + }; + + run_until_exit(config, |config| { + Ok(builder(config)?.export_blocks(file, from.into(), to, json)) + }) } } -/// Merge all CLI parameters of `L` and `R` into the same level. -#[derive(Clone, Debug)] -pub struct MergeParameters { - /// The left side parameters. - pub left: L, - /// The right side parameters. - pub right: R, +/// Internal trait used to cast to a dynamic type that implements Read and Seek. +trait ReadPlusSeek: Read + Seek {} + +impl ReadPlusSeek for T {} + +impl ImportBlocksCmd { + /// Run the import-blocks command + pub fn run( + self, + mut config: Configuration, + builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + { + crate::fill_import_params( + &mut config, + &self.import_params, + sc_service::Roles::FULL, + self.shared_params.dev, + )?; + + let file: Box = match &self.input { + Some(filename) => Box::new(fs::File::open(filename)?), + None => { + let mut buffer = Vec::new(); + io::stdin().read_to_end(&mut buffer)?; + Box::new(io::Cursor::new(buffer)) + }, + }; + + run_until_exit(config, |config| { + Ok(builder(config)?.import_blocks(file, false)) + }) + } } -impl StructOpt for MergeParameters where L: StructOpt + StructOptInternal, R: StructOpt { - fn clap<'a, 'b>() -> App<'a, 'b> { - L::augment_clap(R::clap()) +impl CheckBlockCmd { + /// Run the check-block command + pub fn run( + self, + mut config: Configuration, + builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); + + crate::fill_import_params( + &mut config, + &self.import_params, + sc_service::Roles::FULL, + self.shared_params.dev, + )?; + crate::fill_config_keystore_in_memory(&mut config)?; + + let input = if self.input.starts_with("0x") { &self.input[2..] } else { &self.input[..] }; + let block_id = match FromStr::from_str(input) { + Ok(hash) => BlockId::hash(hash), + Err(_) => match self.input.parse::() { + Ok(n) => BlockId::number((n as u32).into()), + Err(_) => return Err(error::Error::Input("Invalid hash or number specified".into())), + } + }; + + let start = std::time::Instant::now(); + run_until_exit(config, |config| { + Ok(builder(config)?.check_block(block_id)) + })?; + println!("Completed in {} ms.", start.elapsed().as_millis()); + + Ok(()) } +} - fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self { - MergeParameters { - left: L::from_clap(matches), - right: R::from_clap(matches), +impl PurgeChainCmd { + /// Run the purge command + pub fn run( + self, + mut config: Configuration, + ) -> error::Result<()> + where + G: RuntimeGenesis, + E: ChainSpecExtension, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); + + crate::fill_config_keystore_in_memory(&mut config)?; + + let db_path = match config.expect_database() { + DatabaseConfig::Path { path, .. } => path, + _ => { + eprintln!("Cannot purge custom database implementation"); + return Ok(()); + } + }; + + if !self.yes { + print!("Are you sure to remove {:?}? [y/N]: ", &db_path); + io::stdout().flush().expect("failed to flush stdout"); + + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + let input = input.trim(); + + match input.chars().nth(0) { + Some('y') | Some('Y') => {}, + _ => { + println!("Aborted"); + return Ok(()); + }, + } } + + match fs::remove_dir_all(&db_path) { + Ok(_) => { + println!("{:?} removed.", &db_path); + Ok(()) + }, + Err(ref err) if err.kind() == io::ErrorKind::NotFound => { + eprintln!("{:?} did not exist.", &db_path); + Ok(()) + }, + Err(err) => Result::Err(err.into()) + } + } +} + +impl RevertCmd { + /// Run the revert command + pub fn run( + self, + mut config: Configuration, + builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + { + assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing"); + + crate::fill_config_keystore_in_memory(&mut config)?; + + let blocks = self.num.parse()?; + builder(config)?.revert_chain(blocks)?; + + Ok(()) } } + +impl BenchmarkCmd { + /// Runs the command and benchmarks the chain. + pub fn run( + self, + config: Configuration, + _builder: B, + ) -> error::Result<()> + where + B: FnOnce(Configuration) -> Result, + G: RuntimeGenesis, + E: ChainSpecExtension, + BC: ServiceBuilderCommand + Unpin, + BB: sp_runtime::traits::Block + Debug, + <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + ::Hash: std::str::FromStr, + { + let spec = config.chain_spec.expect("chain_spec is always Some"); + let execution_strategy = self.execution.unwrap_or(ExecutionStrategy::Native).into(); + let wasm_method = self.wasm_method.into(); + let pallet = self.pallet; + let extrinsic = self.extrinsic; + let steps = self.steps; + let repeat = self.repeat; + sc_service::chain_ops::benchmark_runtime::(spec, execution_strategy, wasm_method, pallet, extrinsic, steps, repeat)?; + Ok(()) + } +} + diff --git a/client/cli/src/runtime.rs b/client/cli/src/runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..157b75f2050d1aeab8705c2a7e510c4664408eae --- /dev/null +++ b/client/cli/src/runtime.rs @@ -0,0 +1,139 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use std::sync::Arc; + +use futures::{Future, future, future::FutureExt}; +use futures::select; +use futures::pin_mut; +use sc_service::{AbstractService, Configuration}; +use crate::error; + +#[cfg(target_family = "unix")] +async fn main(func: F) -> Result<(), Box> +where + F: Future> + future::FusedFuture, + E: 'static + std::error::Error, +{ + use tokio::signal::unix::{signal, SignalKind}; + + let mut stream_int = signal(SignalKind::interrupt())?; + let mut stream_term = signal(SignalKind::terminate())?; + + let t1 = stream_int.recv().fuse(); + let t2 = stream_term.recv().fuse(); + let t3 = func; + + pin_mut!(t1, t2, t3); + + select! { + _ = t1 => {}, + _ = t2 => {}, + res = t3 => res?, + } + + Ok(()) +} + +#[cfg(not(unix))] +async fn main(func: F) -> Result<(), Box> +where + F: Future> + future::FusedFuture, + E: 'static + std::error::Error, +{ + use tokio::signal::ctrl_c; + + let t1 = ctrl_c().fuse(); + let t2 = func; + + pin_mut!(t1, t2); + + select! { + _ = t1 => {}, + res = t2 => res?, + } + + Ok(()) +} + +fn build_runtime() -> Result { + tokio::runtime::Builder::new() + .thread_name("main-tokio-") + .threaded_scheduler() + .enable_all() + .build() +} + +/// A helper function that runs a future with tokio and stops if the process receives the signal +/// SIGTERM or SIGINT +pub fn run_until_exit( + mut config: Configuration, + future_builder: F, +) -> error::Result<()> +where + F: FnOnce(Configuration) -> error::Result, + FUT: Future> + future::Future, + ERR: 'static + std::error::Error, +{ + let mut runtime = build_runtime()?; + + config.task_executor = { + let runtime_handle = runtime.handle().clone(); + Some(Arc::new(move |fut| { runtime_handle.spawn(fut); })) + }; + + let f = future_builder(config)?; + let f = f.fuse(); + pin_mut!(f); + + runtime.block_on(main(f)).map_err(|e| e.to_string())?; + + Ok(()) +} + +/// A helper function that runs an `AbstractService` with tokio and stops if the process receives +/// the signal SIGTERM or SIGINT +pub fn run_service_until_exit( + mut config: Configuration, + service_builder: F, +) -> error::Result<()> +where + F: FnOnce(Configuration) -> Result, + T: AbstractService + Unpin, +{ + let mut runtime = build_runtime()?; + + config.task_executor = { + let runtime_handle = runtime.handle().clone(); + Some(Arc::new(move |fut| { runtime_handle.spawn(fut); })) + }; + + let service = service_builder(config)?; + + let informant_future = sc_informant::build(&service, sc_informant::OutputFormat::Coloured); + let _informant_handle = runtime.spawn(informant_future); + + // we eagerly drop the service so that the internal exit future is fired, + // but we need to keep holding a reference to the global telemetry guard + let _telemetry = service.telemetry(); + + let f = service.fuse(); + pin_mut!(f); + + runtime.block_on(main(f)).map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/client/consensus/aura/Cargo.toml b/client/consensus/aura/Cargo.toml index 3d4c5a9157bef3d49f695a89ea2c80c61f2d0ed0..e67f1e15a3e76f6d2cd2d9a6820e774583ad947b 100644 --- a/client/consensus/aura/Cargo.toml +++ b/client/consensus/aura/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Aura consensus algorithm for substrate" edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", path = "../../../primitives/application-crypto" } @@ -15,11 +16,11 @@ codec = { package = "parity-scale-codec", version = "1.0.0" } sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" } derive_more = "0.99.2" futures = "0.3.1" -futures-timer = "0.4.0" +futures-timer = "3.0.1" sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } sc-keystore = { version = "2.0.0", path = "../../keystore" } log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" sp-core = { version = "2.0.0", path = "../../../primitives/core" } sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } sp-io = { version = "2.0.0", path = "../../../primitives/io" } diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index 95dc08afaf5ef3d4e2b9947bb839e7c02bdda489..434314a85353e7f44e662d45e30077f36eb51aa6 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -65,7 +65,7 @@ use sp_timestamp::{ use sc_telemetry::{telemetry, CONSENSUS_TRACE, CONSENSUS_DEBUG, CONSENSUS_INFO}; use sc_consensus_slots::{ - CheckedHeader, SlotWorker, SlotInfo, SlotCompatible, StorageChanges, check_equivocation, + CheckedHeader, SlotWorker, SlotInfo, SlotCompatible, StorageChanges, check_equivocation, }; use sc_keystore::KeyStorePtr; @@ -274,8 +274,9 @@ impl sc_consensus_slots::SimpleSlotWorker for AuraW Vec, StorageChanges, B>, Self::Claim, + Self::EpochData, ) -> sp_consensus::BlockImportParams> + Send> { - Box::new(|header, header_hash, body, storage_changes, pair| { + Box::new(|header, header_hash, body, storage_changes, pair, _epoch| { // sign the pre-sealed hash of the block and then // add it to a digest item. let signature = pair.sign(header_hash.as_ref()); @@ -290,7 +291,8 @@ impl sc_consensus_slots::SimpleSlotWorker for AuraW storage_changes: Some(storage_changes), finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, } @@ -644,7 +646,8 @@ impl Verifier for AuraVerifier where finalized: false, justification, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }; diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 55b0e0cf202177eb471d94887c674a76066bc977..c36b5216c2d1f5ca4809b50ff6f2db4718e0a2f0 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "BABE consensus algorithm for substrate" edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } @@ -21,6 +22,7 @@ sc-telemetry = { version = "2.0.0", path = "../../telemetry" } sc-keystore = { version = "2.0.0", path = "../../keystore" } sc-client-api = { version = "2.0.0", path = "../../api" } sc-client = { version = "0.8", path = "../../" } +sc-consensus-epochs = { version = "0.8", path = "../epochs" } sp-api = { version = "2.0.0", path = "../../../primitives/api" } sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } @@ -30,8 +32,8 @@ sc-consensus-slots = { version = "0.8", path = "../slots" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } fork-tree = { version = "2.0.0", path = "../../../utils/fork-tree" } futures = "0.3.1" -futures-timer = "0.4.0" -parking_lot = "0.9.0" +futures-timer = "3.0.1" +parking_lot = "0.10.0" log = "0.4.8" schnorrkel = { version = "0.8.5", features = ["preaudit_deprecated"] } rand = "0.7.2" diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 62667ef3978c04fa8beb907723656e817a12702d..8b28aefa2f77a1ce276de51b020ea30e8f0f9875 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -17,13 +17,17 @@ //! BABE authority selection and slot claiming. use merlin::Transcript; -use sp_consensus_babe::{AuthorityId, BabeAuthorityWeight, BABE_ENGINE_ID, BABE_VRF_PREFIX}; -use sp_consensus_babe::{Epoch, SlotNumber, AuthorityPair, BabePreDigest, BabeConfiguration}; +use sp_consensus_babe::{ + AuthorityId, BabeAuthorityWeight, BABE_ENGINE_ID, BABE_VRF_PREFIX, + SlotNumber, AuthorityPair, BabeConfiguration +}; +use sp_consensus_babe::digests::PreDigest; use sp_core::{U256, blake2_256}; use codec::Encode; use schnorrkel::vrf::VRFInOut; use sp_core::Pair; use sc_keystore::KeyStorePtr; +use super::Epoch; /// Calculates the primary selection threshold for a given authority, taking /// into account `c` (`1 - c` represents the probability of a slot being empty). @@ -104,7 +108,7 @@ fn claim_secondary_slot( authorities: &[(AuthorityId, BabeAuthorityWeight)], keystore: &KeyStorePtr, randomness: [u8; 32], -) -> Option<(BabePreDigest, AuthorityPair)> { +) -> Option<(PreDigest, AuthorityPair)> { if authorities.is_empty() { return None; } @@ -124,7 +128,7 @@ fn claim_secondary_slot( }) { if pair.public() == *expected_author { - let pre_digest = BabePreDigest::Secondary { + let pre_digest = PreDigest::Secondary { slot_number, authority_index: authority_index as u32, }; @@ -145,7 +149,7 @@ pub(super) fn claim_slot( epoch: &Epoch, config: &BabeConfiguration, keystore: &KeyStorePtr, -) -> Option<(BabePreDigest, AuthorityPair)> { +) -> Option<(PreDigest, AuthorityPair)> { claim_primary_slot(slot_number, epoch, config.c, keystore) .or_else(|| { if config.secondary_slots { @@ -175,7 +179,7 @@ fn claim_primary_slot( epoch: &Epoch, c: (u64, u64), keystore: &KeyStorePtr, -) -> Option<(BabePreDigest, AuthorityPair)> { +) -> Option<(PreDigest, AuthorityPair)> { let Epoch { authorities, randomness, epoch_index, .. } = epoch; let keystore = keystore.read(); @@ -196,7 +200,7 @@ fn claim_primary_slot( let pre_digest = get_keypair(&pair) .vrf_sign_after_check(transcript, |inout| super::authorship::check_primary_threshold(inout, threshold)) .map(|s| { - BabePreDigest::Primary { + PreDigest::Primary { slot_number, vrf_output: s.0.to_output(), vrf_proof: s.1, diff --git a/client/consensus/babe/src/aux_schema.rs b/client/consensus/babe/src/aux_schema.rs index 170c2bf42d4e0e999e1013f90c78c9ce4d8b1ca6..2f64157f22951dd79a8a6c0d4d238e4eccbf1f42 100644 --- a/client/consensus/babe/src/aux_schema.rs +++ b/client/consensus/babe/src/aux_schema.rs @@ -16,6 +16,8 @@ //! Schema for BABE epoch changes in the aux-db. +use std::sync::Arc; +use parking_lot::Mutex; use log::info; use codec::{Decode, Encode}; @@ -23,8 +25,8 @@ use sc_client_api::backend::AuxStore; use sp_blockchain::{Result as ClientResult, Error as ClientError}; use sp_runtime::traits::Block as BlockT; use sp_consensus_babe::BabeBlockWeight; - -use super::{epoch_changes::EpochChangesFor, SharedEpochChanges}; +use sc_consensus_epochs::{EpochChangesFor, SharedEpochChanges}; +use crate::Epoch; const BABE_EPOCH_CHANGES: &[u8] = b"babe_epoch_changes"; @@ -49,14 +51,14 @@ fn load_decode(backend: &B, key: &[u8]) -> ClientResult> /// Load or initialize persistent epoch change data from backend. pub(crate) fn load_epoch_changes( backend: &B, -) -> ClientResult> { - let epoch_changes = load_decode::<_, EpochChangesFor>(backend, BABE_EPOCH_CHANGES)? - .map(Into::into) +) -> ClientResult> { + let epoch_changes = load_decode::<_, EpochChangesFor>(backend, BABE_EPOCH_CHANGES)? + .map(|v| Arc::new(Mutex::new(v))) .unwrap_or_else(|| { info!(target: "babe", "Creating empty BABE epoch changes on what appears to be first startup." ); - SharedEpochChanges::new() + SharedEpochChanges::::default() }); // rebalance the tree after deserialization. this isn't strictly necessary @@ -70,7 +72,7 @@ pub(crate) fn load_epoch_changes( /// Update the epoch changes on disk after a change. pub(crate) fn write_epoch_changes( - epoch_changes: &EpochChangesFor, + epoch_changes: &EpochChangesFor, write_aux: F, ) -> R where F: FnOnce(&[(&'static [u8], &[u8])]) -> R, diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 770f8a4eec6e2dd0d39681632fdad33400eb3437..f9e3ef98d67350d5bde63ac736dcd9ec0ab45fa8 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -59,11 +59,16 @@ #![forbid(unsafe_code)] #![warn(missing_docs)] pub use sp_consensus_babe::{ - BabeApi, ConsensusLog, BABE_ENGINE_ID, BabePreDigest, SlotNumber, BabeConfiguration, - CompatibleDigestItem, + BabeApi, ConsensusLog, BABE_ENGINE_ID, SlotNumber, BabeConfiguration, + AuthorityId, AuthorityPair, AuthoritySignature, + BabeAuthorityWeight, VRF_OUTPUT_LENGTH, + digests::{PreDigest, CompatibleDigestItem, NextEpochDescriptor}, }; pub use sp_consensus::SyncOracle; -use std::{collections::HashMap, sync::Arc, u64, pin::Pin, time::{Instant, Duration}}; +use std::{ + collections::HashMap, sync::Arc, u64, pin::Pin, time::{Instant, Duration}, + any::Any, borrow::Cow +}; use sp_consensus_babe; use sp_consensus::{ImportResult, CanAuthorWith}; use sp_consensus::import_queue::{ @@ -101,26 +106,60 @@ use log::{warn, debug, info, trace}; use sc_consensus_slots::{ SlotWorker, SlotInfo, SlotCompatible, StorageChanges, CheckedHeader, check_equivocation, }; -use epoch_changes::descendent_query; +use sc_consensus_epochs::{ + descendent_query, ViableEpoch, SharedEpochChanges, EpochChangesFor, Epoch as EpochT +}; use sp_blockchain::{ Result as ClientResult, Error as ClientError, HeaderBackend, ProvideCache, HeaderMetadata }; use schnorrkel::SignatureError; - +use codec::{Encode, Decode}; use sp_api::ApiExt; mod aux_schema; mod verification; -mod epoch_changes; mod authorship; #[cfg(test)] mod tests; -pub use sp_consensus_babe::{ - AuthorityId, AuthorityPair, AuthoritySignature, Epoch, NextEpochDescriptor, -}; -pub use epoch_changes::{EpochChanges, EpochChangesFor, SharedEpochChanges}; +/// BABE epoch information +#[derive(Decode, Encode, Default, PartialEq, Eq, Clone, Debug)] +pub struct Epoch { + /// The epoch index + pub epoch_index: u64, + /// The starting slot of the epoch, + pub start_slot: SlotNumber, + /// The duration of this epoch + pub duration: SlotNumber, + /// The authorities and their weights + pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, + /// Randomness for this epoch + pub randomness: [u8; VRF_OUTPUT_LENGTH], +} + +impl EpochT for Epoch { + type NextEpochDescriptor = NextEpochDescriptor; + type SlotNumber = SlotNumber; + + fn increment(&self, descriptor: NextEpochDescriptor) -> Epoch { + Epoch { + epoch_index: self.epoch_index + 1, + start_slot: self.start_slot + self.duration, + duration: self.duration, + authorities: descriptor.authorities, + randomness: descriptor.randomness, + } + } + + fn start_slot(&self) -> SlotNumber { + self.start_slot + } + + fn end_slot(&self) -> SlotNumber { + self.start_slot + self.duration + } +} #[derive(derive_more::Display, Debug)] enum Error { @@ -162,10 +201,6 @@ enum Error { FetchParentHeader(sp_blockchain::Error), #[display(fmt = "Expected epoch change to happen at {:?}, s{}", _0, _1)] ExpectedEpochChange(B::Hash, u64), - #[display(fmt = "Could not look up epoch: {:?}", _0)] - CouldNotLookUpEpoch(Box>), - #[display(fmt = "Block {} is not valid under any epoch.", _0)] - BlockNotValid(B::Hash), #[display(fmt = "Unexpected epoch change")] UnexpectedEpochChange, #[display(fmt = "Parent block of {} has no associated weight", _0)] @@ -197,6 +232,16 @@ macro_rules! babe_info { }; } + +/// Intermediate value passed to block importer. +pub struct BabeIntermediate { + /// The epoch data, if available. + pub epoch: ViableEpoch, +} + +/// Intermediate key for Babe engine. +pub static INTERMEDIATE_KEY: &[u8] = b"babe1"; + /// A slot duration. Create with `get_or_compute`. // FIXME: Once Rust has higher-kinded types, the duplication between this // and `super::babe::Config` can be eliminated. @@ -343,7 +388,7 @@ struct BabeWorker { sync_oracle: SO, force_authoring: bool, keystore: KeyStorePtr, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, config: Config, } @@ -360,8 +405,8 @@ impl sc_consensus_slots::SimpleSlotWorker for BabeWork SO: SyncOracle + Send + Clone, Error: std::error::Error + Send + From + From + 'static, { - type EpochData = Epoch; - type Claim = (BabePreDigest, AuthorityPair); + type EpochData = ViableEpoch; + type Claim = (PreDigest, AuthorityPair); type SyncOracle = SO; type CreateProposer = Pin> + Send + 'static @@ -390,24 +435,23 @@ impl sc_consensus_slots::SimpleSlotWorker for BabeWork |slot| self.config.genesis_epoch(slot) ) .map_err(|e| ConsensusError::ChainLookup(format!("{:?}", e)))? - .map(|e| e.into_inner()) .ok_or(sp_consensus::Error::InvalidAuthoritiesSet) } fn authorities_len(&self, epoch_data: &Self::EpochData) -> usize { - epoch_data.authorities.len() + epoch_data.as_ref().authorities.len() } fn claim_slot( &self, _parent_header: &B::Header, slot_number: SlotNumber, - epoch_data: &Epoch, + epoch_data: &ViableEpoch, ) -> Option { debug!(target: "babe", "Attempting to claim slot {}", slot_number); let s = authorship::claim_slot( slot_number, - epoch_data, + epoch_data.as_ref(), &*self.config, &self.keystore, ); @@ -435,8 +479,9 @@ impl sc_consensus_slots::SimpleSlotWorker for BabeWork Vec, StorageChanges, Self::Claim, + Self::EpochData, ) -> sp_consensus::BlockImportParams + Send> { - Box::new(|header, header_hash, body, storage_changes, (_, pair)| { + Box::new(|header, header_hash, body, storage_changes, (_, pair), epoch| { // sign the pre-sealed hash of the block and then // add it to a digest item. let signature = pair.sign(header_hash.as_ref()); @@ -451,10 +496,15 @@ impl sc_consensus_slots::SimpleSlotWorker for BabeWork storage_changes: Some(storage_changes), finalized: false, auxiliary: Vec::new(), // block-weight is written in block import. - // TODO: block-import handles fork choice and this shouldn't even have the - // option to specify one. - // https://github.com/paritytech/substrate/issues/3623 - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: { + let mut intermediates = HashMap::new(); + intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + intermediates + }, + fork_choice: None, allow_missing_state: false, import_existing: false, } @@ -535,12 +585,12 @@ impl SlotWorker for BabeWorker where /// Extract the BABE pre digest from the given header. Pre-runtime digests are /// mandatory, the function will return `Err` if none is found. -fn find_pre_digest(header: &B::Header) -> Result> +fn find_pre_digest(header: &B::Header) -> Result> { // genesis block doesn't contain a pre digest so let's generate a // dummy one to not break any invariants in the rest of the code if header.number().is_zero() { - return Ok(BabePreDigest::Secondary { + return Ok(PreDigest::Secondary { slot_number: 0, authority_index: 0, }); @@ -599,16 +649,29 @@ impl SlotCompatible for TimeSource { #[derive(Clone)] pub struct BabeLink { time_source: TimeSource, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, config: Config, } + +impl BabeLink { + /// Get the epoch changes of this link. + pub fn epoch_changes(&self) -> &SharedEpochChanges { + &self.epoch_changes + } + + /// Get the config of this link. + pub fn config(&self) -> &Config { + &self.config + } +} + /// A verifier for Babe blocks. pub struct BabeVerifier { client: Arc>, api: Arc, inherent_data_providers: sp_inherents::InherentDataProviders, config: Config, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, time_source: TimeSource, } @@ -713,7 +776,7 @@ impl Verifier for BabeVerifier::Runtime)?; + .map_err(Error::::Runtime)?; let (_, slot_now, _) = self.time_source.extract_timestamp_and_slot(&inherent_data) .map_err(Error::::Extraction)?; @@ -798,6 +861,14 @@ impl Verifier for BabeVerifier ?pre_header); + let mut intermediates = HashMap::new(); + intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { + epoch, + }) as Box, + ); + let block_import_params = BlockImportParams { origin, header: pre_header, @@ -807,10 +878,8 @@ impl Verifier for BabeVerifier { inner: I, client: Arc>, api: Arc, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, config: Config, } @@ -879,7 +948,7 @@ impl BabeBlockImport { fn new( client: Arc>, api: Arc, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, block_import: I, config: Config, ) -> Self { @@ -967,20 +1036,11 @@ impl BlockImport for BabeBlockImport| ConsensusError::ChainLookup( - babe_err(Error::::CouldNotLookUpEpoch(Box::new(e))).into() - ))? - .ok_or_else(|| ConsensusError::ClientImport( - babe_err(Error::::BlockNotValid(hash)).into() - ))?; + let intermediate = block.take_intermediate::( + INTERMEDIATE_KEY + )?; + let epoch = intermediate.epoch; let first_in_epoch = parent_slot < epoch.as_ref().start_slot; (epoch, first_in_epoch, parent_weight) }; @@ -1085,13 +1145,13 @@ impl BlockImport for BabeBlockImport last_best_weight { + Some(ForkChoiceStrategy::Custom(if total_weight > last_best_weight { true } else if total_weight == last_best_weight { number > last_best_number } else { false - }) + })) }; let import_result = self.inner.import_block(block, new_cache); @@ -1118,7 +1178,7 @@ impl BlockImport for BabeBlockImport( client: &Client, - epoch_changes: &mut EpochChangesFor, + epoch_changes: &mut EpochChangesFor, ) -> Result<(), ConsensusError> where Block: BlockT, E: CallExecutor + Send + Sync, @@ -1165,7 +1225,7 @@ pub fn block_import( RA: Send + Sync, Client: AuxStore, { - let epoch_changes = aux_schema::load_epoch_changes(&*client)?; + let epoch_changes = aux_schema::load_epoch_changes::(&*client)?; let link = BabeLink { epoch_changes: epoch_changes.clone(), time_source: Default::default(), @@ -1249,7 +1309,7 @@ pub mod test_helpers { client: &C, keystore: &KeyStorePtr, link: &BabeLink, - ) -> Option where + ) -> Option where B: BlockT, C: ProvideRuntimeApi + ProvideCache + diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 880e028000b42bc75e5aaf45868340d3735bd0be..687f23e646f668b0733007ff5322a4bdbeac2cdf 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -59,7 +59,7 @@ type Mutator = Arc; #[derive(Clone)] struct DummyFactory { client: Arc, - epoch_changes: crate::SharedEpochChanges, + epoch_changes: SharedEpochChanges, config: Config, mutator: Mutator, } @@ -105,7 +105,6 @@ impl DummyProposer { > > { - use codec::Encode; let block_builder = self.factory.client.new_block_at( &BlockId::Hash(self.parent_hash), pre_digests, @@ -558,7 +557,7 @@ fn propose_and_import_block( let pre_digest = sp_runtime::generic::Digest { logs: vec![ Item::babe_pre_digest( - BabePreDigest::Secondary { + PreDigest::Secondary { authority_index: 0, slot_number, }, @@ -566,8 +565,18 @@ fn propose_and_import_block( ], }; + let parent_hash = parent.hash(); + let mut block = futures::executor::block_on(proposer.propose_with(pre_digest)).unwrap().block; + let epoch = proposer_factory.epoch_changes.lock().epoch_for_child_of( + descendent_query(&*proposer_factory.client), + &parent_hash, + *parent.number(), + slot_number, + |slot| proposer_factory.config.genesis_epoch(slot) + ).unwrap().unwrap(); + let seal = { // sign the pre-sealed hash of the block and then // add it to a digest item. @@ -594,7 +603,15 @@ fn propose_and_import_block( storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: { + let mut intermediates = HashMap::new(); + intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + intermediates + }, + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }, diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index ee5a99ec9d5339764139b294610abd8706cd4d3c..70418b8aea1e38c4ac890ded5de1f64674b389df 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -18,11 +18,11 @@ use schnorrkel::vrf::{VRFOutput, VRFProof}; use sp_runtime::{traits::Header, traits::DigestItemFor}; use sp_core::{Pair, Public}; -use sp_consensus_babe::{Epoch, BabePreDigest, CompatibleDigestItem, AuthorityId}; -use sp_consensus_babe::{AuthoritySignature, SlotNumber, AuthorityIndex, AuthorityPair}; +use sp_consensus_babe::{AuthoritySignature, SlotNumber, AuthorityIndex, AuthorityPair, AuthorityId}; +use sp_consensus_babe::digests::{PreDigest, CompatibleDigestItem}; use sc_consensus_slots::CheckedHeader; use log::{debug, trace}; -use super::{find_pre_digest, babe_err, BlockT, Error}; +use super::{find_pre_digest, babe_err, Epoch, BlockT, Error}; use super::authorship::{make_transcript, calculate_primary_threshold, check_primary_threshold, secondary_slot_author}; /// BABE verification parameters @@ -32,7 +32,7 @@ pub(super) struct VerificationParams<'a, B: 'a + BlockT> { /// the pre-digest of the header being verified. this is optional - if prior /// verification code had to read it, it can be included here to avoid duplicate /// work. - pub(super) pre_digest: Option, + pub(super) pre_digest: Option, /// the slot number of the current time. pub(super) slot_now: SlotNumber, /// epoch descriptor of the epoch this block _should_ be under, if it's valid. @@ -93,7 +93,7 @@ pub(super) fn check_header( }; match &pre_digest { - BabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => { + PreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => { debug!(target: "babe", "Verifying Primary block"); let digest = (vrf_output, vrf_proof, *authority_index, *slot_number); @@ -106,7 +106,7 @@ pub(super) fn check_header( config.c, )?; }, - BabePreDigest::Secondary { authority_index, slot_number } if config.secondary_slots => { + PreDigest::Secondary { authority_index, slot_number } if config.secondary_slots => { debug!(target: "babe", "Verifying Secondary block"); let digest = (*authority_index, *slot_number); diff --git a/client/consensus/epochs/Cargo.toml b/client/consensus/epochs/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e08553a241d1c41e7c8d175bcaedd3fc03961610 --- /dev/null +++ b/client/consensus/epochs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "sc-consensus-epochs" +version = "0.8.0" +authors = ["Parity Technologies "] +description = "Generic epochs-based utilities for consensus" +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } +parking_lot = "0.10.0" +fork-tree = { version = "2.0.0", path = "../../../utils/fork-tree" } +sp-runtime = { path = "../../../primitives/runtime" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sc-client-api = { path = "../../api" } diff --git a/client/consensus/babe/src/epoch_changes.rs b/client/consensus/epochs/src/lib.rs similarity index 79% rename from client/consensus/babe/src/epoch_changes.rs rename to client/consensus/epochs/src/lib.rs index 01e957c4998edb77a9f59a861ca3f770ae394d48..cf3d9f5c4c2c2e178789ecd535128d14465c102d 100644 --- a/client/consensus/babe/src/epoch_changes.rs +++ b/client/consensus/epochs/src/lib.rs @@ -14,20 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Handling epoch changes in BABE. -//! -//! This exposes the `SharedEpochChanges`, which is a wrapper around a -//! persistent DAG superimposed over the forks of the blockchain. +//! Generic utilities for epoch-based consensus engines. -use std::sync::Arc; -use sp_consensus_babe::{Epoch, SlotNumber, NextEpochDescriptor}; -use fork_tree::ForkTree; -use parking_lot::{Mutex, MutexGuard}; -use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero}; +use std::{sync::Arc, ops::Add}; +use parking_lot::Mutex; use codec::{Encode, Decode}; +use fork_tree::ForkTree; use sc_client_api::utils::is_descendent_of; use sp_blockchain::{HeaderMetadata, HeaderBackend, Error as ClientError}; -use std::ops::Add; +use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero}; /// A builder for `is_descendent_of` functions. pub trait IsDescendentOfBuilder { @@ -48,13 +43,13 @@ pub trait IsDescendentOfBuilder { } /// Produce a descendent query object given the client. -pub(crate) fn descendent_query(client: &H) -> HeaderBackendDescendentBuilder<&H, Block> { +pub fn descendent_query(client: &H) -> HeaderBackendDescendentBuilder<&H, Block> { HeaderBackendDescendentBuilder(client, std::marker::PhantomData) } /// Wrapper to get around unconstrained type errors when implementing /// `IsDescendentOfBuilder` for header backends. -pub(crate) struct HeaderBackendDescendentBuilder(H, std::marker::PhantomData); +pub struct HeaderBackendDescendentBuilder(H, std::marker::PhantomData); impl<'a, H, Block> IsDescendentOfBuilder for HeaderBackendDescendentBuilder<&'a H, Block> where @@ -71,49 +66,73 @@ impl<'a, H, Block> IsDescendentOfBuilder } } +/// Epoch data, distinguish whether it is genesis or not. +pub trait Epoch { + /// Descriptor for the next epoch. + type NextEpochDescriptor; + /// Type of the slot number. + type SlotNumber: Ord; + + /// Increment the epoch data, using the next epoch descriptor. + fn increment(&self, descriptor: Self::NextEpochDescriptor) -> Self; + + /// Produce the "end slot" of the epoch. This is NOT inclusive to the epoch, + /// i.e. the slots covered by the epoch are `self.start_slot() .. self.end_slot()`. + fn end_slot(&self) -> Self::SlotNumber; + /// Produce the "start slot" of the epoch. + fn start_slot(&self) -> Self::SlotNumber; +} + /// An unimported genesis epoch. -pub struct UnimportedGenesis(Epoch); +pub struct UnimportedGenesisEpoch(Epoch); /// The viable epoch under which a block can be verified. /// /// If this is the first non-genesis block in the chain, then it will /// hold an `UnimportedGenesis` epoch. -pub enum ViableEpoch { - Genesis(UnimportedGenesis), +pub enum ViableEpoch { + /// Genesis viable epoch data. + Genesis(UnimportedGenesisEpoch), + /// Regular viable epoch data. Regular(Epoch), } -impl From for ViableEpoch { - fn from(epoch: Epoch) -> ViableEpoch { +impl From for ViableEpoch { + fn from(epoch: Epoch) -> ViableEpoch { ViableEpoch::Regular(epoch) } } -impl AsRef for ViableEpoch { +impl AsRef for ViableEpoch { fn as_ref(&self) -> &Epoch { match *self { - ViableEpoch::Genesis(UnimportedGenesis(ref e)) => e, + ViableEpoch::Genesis(UnimportedGenesisEpoch(ref e)) => e, ViableEpoch::Regular(ref e) => e, } } } -impl ViableEpoch { +impl ViableEpoch where + Epoch: crate::Epoch + Clone, +{ /// Extract the underlying epoch, disregarding the fact that a genesis /// epoch may be unimported. pub fn into_inner(self) -> Epoch { match self { - ViableEpoch::Genesis(UnimportedGenesis(e)) => e, + ViableEpoch::Genesis(UnimportedGenesisEpoch(e)) => e, ViableEpoch::Regular(e) => e, } } /// Increment the epoch, yielding an `IncrementedEpoch` to be imported /// into the fork-tree. - pub fn increment(&self, next_descriptor: NextEpochDescriptor) -> IncrementedEpoch { + pub fn increment( + &self, + next_descriptor: Epoch::NextEpochDescriptor + ) -> IncrementedEpoch { let next = self.as_ref().increment(next_descriptor); let to_persist = match *self { - ViableEpoch::Genesis(UnimportedGenesis(ref epoch_0)) => + ViableEpoch::Genesis(UnimportedGenesisEpoch(ref epoch_0)) => PersistedEpoch::Genesis(epoch_0.clone(), next), ViableEpoch::Regular(_) => PersistedEpoch::Regular(next), }; @@ -123,12 +142,11 @@ impl ViableEpoch { } /// The datatype encoded on disk. -// This really shouldn't be public, but the encode/decode derives force it to be. #[derive(Clone, Encode, Decode)] -pub enum PersistedEpoch { - // epoch_0, epoch_1, +pub enum PersistedEpoch { + /// Genesis persisted epoch data. epoch_0, epoch_1. Genesis(Epoch, Epoch), - // epoch_n + /// Regular persisted epoch data. epoch_n. Regular(Epoch), } @@ -136,9 +154,9 @@ pub enum PersistedEpoch { /// /// Create this with `ViableEpoch::increment`. #[must_use = "Freshly-incremented epoch must be imported with `EpochChanges::import`"] -pub struct IncrementedEpoch(PersistedEpoch); +pub struct IncrementedEpoch(PersistedEpoch); -impl AsRef for IncrementedEpoch { +impl AsRef for IncrementedEpoch { fn as_ref(&self) -> &Epoch { match self.0 { PersistedEpoch::Genesis(_, ref epoch_1) => epoch_1, @@ -151,7 +169,7 @@ impl AsRef for IncrementedEpoch { /// the hash and block number of the block signaling the epoch change, and the /// epoch that was signalled at that block. /// -/// BABE special-cases the first epoch, epoch_0, by saying that it starts at +/// The first epoch, epoch_0, is special cased by saying that it starts at /// slot number of the first block in the chain. When bootstrapping a chain, /// there can be multiple competing block #1s, so we have to ensure that the overlayed /// DAG doesn't get confused. @@ -163,8 +181,8 @@ impl AsRef for IncrementedEpoch { /// /// Further epochs (epoch_2, ..., epoch_n) each get their own entry. #[derive(Clone, Encode, Decode)] -pub struct EpochChanges { - inner: ForkTree, +pub struct EpochChanges { + inner: ForkTree>, } // create a fake header hash which hasn't been included in the chain. @@ -176,13 +194,23 @@ fn fake_head_hash + AsMut<[u8]> + Clone>(parent_hash: &H) -> H { h } -impl EpochChanges where +impl Default for EpochChanges where + Hash: PartialEq, + Number: Ord, +{ + fn default() -> Self { + EpochChanges { inner: ForkTree::new() } + } +} + +impl EpochChanges where Hash: PartialEq + AsRef<[u8]> + AsMut<[u8]> + Copy, Number: Ord + One + Zero + Add + Copy, + Epoch: crate::Epoch + Clone, { - /// Create a new epoch-change tracker. - fn new() -> Self { - EpochChanges { inner: ForkTree::new() } + /// Create a new epoch change. + pub fn new() -> Self { + Self::default() } /// Rebalances the tree of epoch changes so that it is sorted by length of @@ -199,12 +227,12 @@ impl EpochChanges where descendent_of_builder: D, hash: &Hash, number: Number, - slot: SlotNumber, + slot: Epoch::SlotNumber, ) -> Result<(), fork_tree::Error> { let is_descendent_of = descendent_of_builder .build_is_descendent_of(None); - let predicate = |epoch: &PersistedEpoch| match *epoch { + let predicate = |epoch: &PersistedEpoch| match *epoch { PersistedEpoch::Genesis(_, ref epoch_1) => slot >= epoch_1.end_slot(), PersistedEpoch::Regular(ref epoch_n) => @@ -233,10 +261,10 @@ impl EpochChanges where descendent_of_builder: D, parent_hash: &Hash, parent_number: Number, - slot_number: SlotNumber, + slot_number: Epoch::SlotNumber, make_genesis: G, - ) -> Result, fork_tree::Error> - where G: FnOnce(SlotNumber) -> Epoch + ) -> Result>, fork_tree::Error> + where G: FnOnce(Epoch::SlotNumber) -> Epoch { // find_node_where will give you the node in the fork-tree which is an ancestor // of the `parent_hash` by default. if the last epoch was signalled at the parent_hash, @@ -250,7 +278,7 @@ impl EpochChanges where if parent_number == Zero::zero() { // need to insert the genesis epoch. let genesis_epoch = make_genesis(slot_number); - return Ok(Some(ViableEpoch::Genesis(UnimportedGenesis(genesis_epoch)))); + return Ok(Some(ViableEpoch::Genesis(UnimportedGenesisEpoch(genesis_epoch)))); } // We want to find the deepest node in the tree which is an ancestor @@ -258,11 +286,11 @@ impl EpochChanges where // slot of our block. The genesis special-case doesn't need to look // at epoch_1 -- all we're doing here is figuring out which node // we need. - let predicate = |epoch: &PersistedEpoch| match *epoch { + let predicate = |epoch: &PersistedEpoch| match *epoch { PersistedEpoch::Genesis(ref epoch_0, _) => - epoch_0.start_slot <= slot_number, + epoch_0.start_slot() <= slot_number, PersistedEpoch::Regular(ref epoch_n) => - epoch_n.start_slot <= slot_number, + epoch_n.start_slot() <= slot_number, }; self.inner.find_node_where( @@ -276,7 +304,7 @@ impl EpochChanges where // and here we figure out which of the internal epochs // of a genesis node to use based on their start slot. PersistedEpoch::Genesis(ref epoch_0, ref epoch_1) => - if epoch_1.start_slot <= slot_number { + if epoch_1.start_slot() <= slot_number { epoch_1.clone() } else { epoch_0.clone() @@ -296,7 +324,7 @@ impl EpochChanges where hash: Hash, number: Number, parent_hash: Hash, - epoch: IncrementedEpoch, + epoch: IncrementedEpoch, ) -> Result<(), fork_tree::Error> { let is_descendent_of = descendent_of_builder .build_is_descendent_of(Some((hash, parent_hash))); @@ -314,47 +342,22 @@ impl EpochChanges where } } - /// Return the inner fork tree, useful for testing purposes. - #[cfg(test)] - pub fn tree(&self) -> &ForkTree { + /// Return the inner fork tree. + pub fn tree(&self) -> &ForkTree> { &self.inner } } /// Type alias to produce the epoch-changes tree from a block type. -pub type EpochChangesFor = EpochChanges<::Hash, NumberFor>; +pub type EpochChangesFor = EpochChanges<::Hash, NumberFor, Epoch>; /// A shared epoch changes tree. -#[derive(Clone)] -pub struct SharedEpochChanges { - inner: Arc>>, -} - -impl SharedEpochChanges { - /// Create a new instance of the `SharedEpochChanges`. - pub fn new() -> Self { - SharedEpochChanges { - inner: Arc::new(Mutex::new(EpochChanges::<_, _>::new())) - } - } - - /// Lock the shared epoch changes, - pub fn lock(&self) -> MutexGuard> { - self.inner.lock() - } -} - -impl From> for SharedEpochChanges { - fn from(epoch_changes: EpochChangesFor) -> Self { - SharedEpochChanges { - inner: Arc::new(Mutex::new(epoch_changes)) - } - } -} +pub type SharedEpochChanges = Arc>>; #[cfg(test)] mod tests { use super::*; + use super::Epoch as EpochT; #[derive(Debug, PartialEq)] pub struct TestError; @@ -396,6 +399,33 @@ mod tests { } type Hash = [u8; 1]; + type SlotNumber = u64; + + #[derive(Debug, Clone, Eq, PartialEq)] + struct Epoch { + start_slot: SlotNumber, + duration: SlotNumber, + } + + impl EpochT for Epoch { + type NextEpochDescriptor = (); + type SlotNumber = SlotNumber; + + fn increment(&self, _: ()) -> Self { + Epoch { + start_slot: self.start_slot + self.duration, + duration: self.duration, + } + } + + fn end_slot(&self) -> SlotNumber { + self.start_slot + self.duration + } + + fn start_slot(&self) -> SlotNumber { + self.start_slot + } + } #[test] fn genesis_epoch_is_created_but_not_imported() { @@ -414,11 +444,8 @@ mod tests { }; let make_genesis = |slot| Epoch { - epoch_index: 0, start_slot: slot, duration: 100, - authorities: Vec::new(), - randomness: [0; 32], }; let epoch_changes = EpochChanges::new(); @@ -468,11 +495,8 @@ mod tests { }; let make_genesis = |slot| Epoch { - epoch_index: 0, start_slot: slot, duration: 100, - authorities: Vec::new(), - randomness: [0; 32], }; let mut epoch_changes = EpochChanges::new(); @@ -486,10 +510,7 @@ mod tests { assert_eq!(genesis_epoch.as_ref(), &make_genesis(100)); - let import_epoch_1 = genesis_epoch.increment(NextEpochDescriptor { - authorities: Vec::new(), - randomness: [1; 32], - }); + let import_epoch_1 = genesis_epoch.increment(()); let epoch_1 = import_epoch_1.as_ref().clone(); epoch_changes.import( @@ -566,18 +587,12 @@ mod tests { let duration = 100; let make_genesis = |slot| Epoch { - epoch_index: 0, start_slot: slot, duration, - authorities: Vec::new(), - randomness: [0; 32], }; let mut epoch_changes = EpochChanges::new(); - let next_descriptor = NextEpochDescriptor { - authorities: Vec::new(), - randomness: [0; 32], - }; + let next_descriptor = (); // insert genesis epoch for A { diff --git a/client/consensus/manual-seal/Cargo.toml b/client/consensus/manual-seal/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..71c1f6e5583a27edb96b6d7710a31112ce6f169d --- /dev/null +++ b/client/consensus/manual-seal/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "sc-consensus-manual-seal" +version = "0.8.0" +authors = ["Parity Technologies "] +description = "Manual sealing engine for Substrate" +edition = "2018" +license = "GPL-3.0" + +[dependencies] +derive_more = "0.99.2" +futures = "0.3.1" +jsonrpc-core = "14.0.5" +jsonrpc-core-client = "14.0.5" +jsonrpc-derive = "14.0.5" +log = "0.4.8" +parking_lot = "0.10.0" +serde = { version = "1.0", features=["derive"] } + +sc-client = { path = "../../../client" } +sc-client-api = { path = "../../../client/api" } +sc-transaction-pool = { path = "../../transaction-pool" } +sp-blockchain = { path = "../../../primitives/blockchain" } +sp-consensus = { package = "sp-consensus", path = "../../../primitives/consensus/common" } +sp-inherents = { path = "../../../primitives/inherents" } +sp-runtime = { path = "../../../primitives/runtime" } +sp-transaction-pool = { path = "../../../primitives/transaction-pool" } + +[dev-dependencies] +sc-basic-authorship = { path = "../../basic-authorship" } +substrate-test-runtime-client = { path = "../../../test-utils/runtime/client" } +substrate-test-runtime-transaction-pool = { path = "../../../test-utils/runtime/transaction-pool" } +tokio = { version = "0.2", features = ["rt-core", "macros"] } +env_logger = "0.7.0" +tempfile = "3.1.0" diff --git a/client/consensus/manual-seal/src/error.rs b/client/consensus/manual-seal/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..d6ee9f176772128a8f597e91b944cbca3e978a0c --- /dev/null +++ b/client/consensus/manual-seal/src/error.rs @@ -0,0 +1,98 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A manual sealing engine: the engine listens for rpc calls to seal blocks and create forks. +//! This is suitable for a testing environment. +use sp_consensus::{Error as ConsensusError, ImportResult}; +use sp_blockchain::Error as BlockchainError; +use sp_inherents::Error as InherentsError; +use futures::channel::{oneshot, mpsc::SendError}; + +/// Error code for rpc +mod codes { + pub const SERVER_SHUTTING_DOWN: i64 = 10_000; + pub const BLOCK_IMPORT_FAILED: i64 = 11_000; + pub const EMPTY_TRANSACTION_POOL: i64 = 12_000; + pub const BLOCK_NOT_FOUND: i64 = 13_000; + pub const CONSENSUS_ERROR: i64 = 14_000; + pub const INHERENTS_ERROR: i64 = 15_000; + pub const BLOCKCHAIN_ERROR: i64 = 16_000; + pub const UNKNOWN_ERROR: i64 = 20_000; +} + +/// errors encountered by background block authorship task +#[derive(Debug, derive_more::Display, derive_more::From)] +pub enum Error { + /// An error occurred while importing the block + #[display(fmt = "Block import failed: {:?}", _0)] + BlockImportError(ImportResult), + /// Transaction pool is empty, cannot create a block + #[display(fmt = "Transaction pool is empty, set create_empty to true,\ + if you want to create empty blocks")] + EmptyTransactionPool, + /// encountered during creation of Proposer. + #[display(fmt = "Consensus Error: {}", _0)] + ConsensusError(ConsensusError), + /// Failed to create Inherents data + #[display(fmt = "Inherents Error: {}", _0)] + InherentError(InherentsError), + /// error encountered during finalization + #[display(fmt = "Finalization Error: {}", _0)] + BlockchainError(BlockchainError), + /// Supplied parent_hash doesn't exist in chain + #[display(fmt = "Supplied parent_hash: {} doesn't exist in chain", _0)] + #[from(ignore)] + BlockNotFound(String), + /// Some string error + #[display(fmt = "{}", _0)] + #[from(ignore)] + StringError(String), + ///send error + #[display(fmt = "Consensus process is terminating")] + Canceled(oneshot::Canceled), + ///send error + #[display(fmt = "Consensus process is terminating")] + SendError(SendError), + /// Some other error. + #[display(fmt="Other error: {}", _0)] + Other(Box), +} + +impl Error { + fn to_code(&self) -> i64 { + use Error::*; + match self { + BlockImportError(_) => codes::BLOCK_IMPORT_FAILED, + BlockNotFound(_) => codes::BLOCK_NOT_FOUND, + EmptyTransactionPool => codes::EMPTY_TRANSACTION_POOL, + ConsensusError(_) => codes::CONSENSUS_ERROR, + InherentError(_) => codes::INHERENTS_ERROR, + BlockchainError(_) => codes::BLOCKCHAIN_ERROR, + SendError(_) | Canceled(_) => codes::SERVER_SHUTTING_DOWN, + _ => codes::UNKNOWN_ERROR + } + } +} + +impl std::convert::From for jsonrpc_core::Error { + fn from(error: Error) -> Self { + jsonrpc_core::Error { + code: jsonrpc_core::ErrorCode::ServerError(error.to_code()), + message: format!("{}", error), + data: None + } + } +} diff --git a/client/consensus/manual-seal/src/finalize_block.rs b/client/consensus/manual-seal/src/finalize_block.rs new file mode 100644 index 0000000000000000000000000000000000000000..419159709f2db84ae51cba0fb861458123b63332 --- /dev/null +++ b/client/consensus/manual-seal/src/finalize_block.rs @@ -0,0 +1,64 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Block finalization utilities + +use crate::rpc; +use sp_runtime::{ + Justification, + traits::Block as BlockT, + generic::BlockId, +}; +use std::sync::Arc; +use sc_client_api::backend::Backend as ClientBackend; + +/// params for block finalization. +pub struct FinalizeBlockParams { + /// hash of the block + pub hash: ::Hash, + /// sender to report errors/success to the rpc. + pub sender: rpc::Sender<()>, + /// finalization justification + pub justification: Option, + /// client backend + pub backend: Arc, +} + +/// finalizes a block in the backend with the given params. +pub async fn finalize_block(params: FinalizeBlockParams) + where + B: BlockT, + CB: ClientBackend, +{ + let FinalizeBlockParams { + hash, + mut sender, + justification, + backend: back_end, + .. + } = params; + + match back_end.finalize_block(BlockId::Hash(hash), justification) { + Err(e) => { + log::warn!("Failed to finalize block {:?}", e); + rpc::send_result(&mut sender, Err(e.into())) + } + Ok(()) => { + log::info!("Successfully finalized block: {}", hash); + rpc::send_result(&mut sender, Ok(())) + } + } +} \ No newline at end of file diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..029f746e625e4172bf6f7e8621856602de03afc7 --- /dev/null +++ b/client/consensus/manual-seal/src/lib.rs @@ -0,0 +1,467 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A manual sealing engine: the engine listens for rpc calls to seal blocks and create forks. +//! This is suitable for a testing environment. + +use sp_consensus::{ + self, BlockImport, Environment, Proposer, BlockCheckParams, + ForkChoiceStrategy, BlockImportParams, BlockOrigin, + ImportResult, SelectChain, + import_queue::{ + BasicQueue, + CacheKeyId, + Verifier, + BoxBlockImport, + }, +}; +use sp_inherents::InherentDataProviders; +use sp_runtime::{traits::Block as BlockT, Justification}; +use sc_client_api::backend::Backend as ClientBackend; +use futures::prelude::*; +use sc_transaction_pool::txpool; +use std::collections::HashMap; +use std::sync::Arc; + +pub mod rpc; +mod error; +mod finalize_block; +mod seal_new_block; +use finalize_block::{finalize_block, FinalizeBlockParams}; +use seal_new_block::{seal_new_block, SealBlockParams}; +pub use error::Error; +pub use rpc::{EngineCommand, CreatedBlock}; + +/// The synchronous block-import worker of the engine. +pub struct ManualSealBlockImport { + inner: I, +} + +impl From for ManualSealBlockImport { + fn from(i: I) -> Self { + ManualSealBlockImport { inner: i } + } +} + +impl BlockImport for ManualSealBlockImport + where + B: BlockT, + I: BlockImport, +{ + type Error = I::Error; + type Transaction = (); + + fn check_block(&mut self, block: BlockCheckParams) -> Result + { + self.inner.check_block(block) + } + + fn import_block( + &mut self, + block: BlockImportParams, + cache: HashMap>, + ) -> Result { + self.inner.import_block(block, cache) + } +} + +/// The verifier for the manual seal engine; instantly finalizes. +struct ManualSealVerifier; + +impl Verifier for ManualSealVerifier { + fn verify( + &mut self, + origin: BlockOrigin, + header: B::Header, + justification: Option, + body: Option>, + ) -> Result<(BlockImportParams, Option)>>), String> { + let import_params = BlockImportParams { + origin, + header, + justification, + post_digests: Vec::new(), + body, + storage_changes: None, + finalized: true, + auxiliary: Vec::new(), + intermediates: HashMap::new(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), + allow_missing_state: false, + import_existing: false, + }; + + Ok((import_params, None)) + } +} + +/// Instantiate the import queue for the manual seal consensus engine. +pub fn import_queue(block_import: BoxBlockImport) -> BasicQueue +{ + BasicQueue::new( + ManualSealVerifier, + block_import, + None, + None, + ) +} + +/// Creates the background authorship task for the manual seal engine. +pub async fn run_manual_seal( + mut block_import: BoxBlockImport, + mut env: E, + backend: Arc, + pool: Arc>, + mut seal_block_channel: S, + select_chain: C, + inherent_data_providers: InherentDataProviders, +) + where + B: BlockT + 'static, + CB: ClientBackend + 'static, + E: Environment + 'static, + E::Error: std::fmt::Display, + >::Error: std::fmt::Display, + A: txpool::ChainApi::Hash> + 'static, + S: Stream::Hash>> + Unpin + 'static, + C: SelectChain + 'static, +{ + while let Some(command) = seal_block_channel.next().await { + match command { + EngineCommand::SealNewBlock { + create_empty, + finalize, + parent_hash, + sender, + } => { + seal_new_block( + SealBlockParams { + sender, + parent_hash, + finalize, + create_empty, + env: &mut env, + select_chain: &select_chain, + block_import: &mut block_import, + inherent_data_provider: &inherent_data_providers, + pool: pool.clone(), + backend: backend.clone(), + } + ).await; + } + EngineCommand::FinalizeBlock { hash, sender, justification } => { + finalize_block( + FinalizeBlockParams { + hash, + sender, + justification, + backend: backend.clone(), + } + ).await + } + } + } +} + +/// runs the background authorship task for the instant seal engine. +/// instant-seal creates a new block for every transaction imported into +/// the transaction pool. +pub async fn run_instant_seal( + block_import: BoxBlockImport, + env: E, + backend: Arc, + pool: Arc>, + select_chain: C, + inherent_data_providers: InherentDataProviders, +) + where + A: txpool::ChainApi::Hash> + 'static, + B: BlockT + 'static, + CB: ClientBackend + 'static, + E: Environment + 'static, + E::Error: std::fmt::Display, + >::Error: std::fmt::Display, + C: SelectChain + 'static +{ + // instant-seal creates blocks as soon as transactions are imported + // into the transaction pool. + let seal_block_channel = pool.import_notification_stream() + .map(|_| { + EngineCommand::SealNewBlock { + create_empty: false, + finalize: false, + parent_hash: None, + sender: None, + } + }); + + run_manual_seal( + block_import, + env, + backend, + pool, + seal_block_channel, + select_chain, + inherent_data_providers, + ).await +} + +#[cfg(test)] +mod tests { + use super::*; + use substrate_test_runtime_client::{ + DefaultTestClientBuilderExt, + TestClientBuilderExt, + AccountKeyring::*, + TestClientBuilder, + }; + use sc_transaction_pool::{ + BasicPool, + txpool::Options, + }; + use substrate_test_runtime_transaction_pool::{TestApi, uxt}; + use sp_transaction_pool::TransactionPool; + use sp_runtime::generic::BlockId; + use sp_blockchain::HeaderBackend; + use sp_consensus::ImportedAux; + use sc_client::LongestChain; + use sp_inherents::InherentDataProviders; + use sc_basic_authorship::ProposerFactory; + + fn api() -> Arc { + Arc::new(TestApi::empty()) + } + + #[tokio::test] + async fn instant_seal() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + let select_chain = LongestChain::new(backend.clone()); + let inherent_data_providers = InherentDataProviders::new(); + let pool = Arc::new(BasicPool::new(Options::default(), api())); + let env = ProposerFactory { + transaction_pool: pool.clone(), + client: client.clone(), + }; + // this test checks that blocks are created as soon as transactions are imported into the pool. + let (sender, receiver) = futures::channel::oneshot::channel(); + let mut sender = Arc::new(Some(sender)); + let stream = pool.pool().import_notification_stream() + .map(move |_| { + // we're only going to submit one tx so this fn will only be called once. + let mut_sender = Arc::get_mut(&mut sender).unwrap(); + let sender = std::mem::replace(mut_sender, None); + EngineCommand::SealNewBlock { + create_empty: false, + finalize: true, + parent_hash: None, + sender + } + }); + let future = run_manual_seal( + Box::new(client.clone()), + env, + backend.clone(), + pool.pool().clone(), + stream, + select_chain, + inherent_data_providers, + ); + std::thread::spawn(|| { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + // spawn the background authorship task + rt.block_on(future); + }); + // submit a transaction to pool. + let result = pool.submit_one(&BlockId::Number(0), uxt(Alice, 0)).await; + // assert that it was successfully imported + assert!(result.is_ok()); + // assert that the background task returns ok + let created_block = receiver.await.unwrap().unwrap(); + assert_eq!( + created_block, + CreatedBlock { + hash: created_block.hash.clone(), + aux: ImportedAux { + header_only: false, + clear_justification_requests: false, + needs_justification: false, + bad_justification: false, + needs_finality_proof: false, + is_new_best: true, + } + } + ); + // assert that there's a new block in the db. + assert!(backend.blockchain().header(BlockId::Number(1)).unwrap().is_some()) + } + + #[tokio::test] + async fn manual_seal_and_finalization() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + let select_chain = LongestChain::new(backend.clone()); + let inherent_data_providers = InherentDataProviders::new(); + let pool = Arc::new(BasicPool::new(Options::default(), api())); + let env = ProposerFactory { + transaction_pool: pool.clone(), + client: client.clone(), + }; + // this test checks that blocks are created as soon as an engine command is sent over the stream. + let (mut sink, stream) = futures::channel::mpsc::channel(1024); + let future = run_manual_seal( + Box::new(client.clone()), + env, + backend.clone(), + pool.pool().clone(), + stream, + select_chain, + inherent_data_providers, + ); + std::thread::spawn(|| { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + // spawn the background authorship task + rt.block_on(future); + }); + // submit a transaction to pool. + let result = pool.submit_one(&BlockId::Number(0), uxt(Alice, 0)).await; + // assert that it was successfully imported + assert!(result.is_ok()); + let (tx, rx) = futures::channel::oneshot::channel(); + sink.send(EngineCommand::SealNewBlock { + parent_hash: None, + sender: Some(tx), + create_empty: false, + finalize: false, + }).await.unwrap(); + let created_block = rx.await.unwrap().unwrap(); + + // assert that the background task returns ok + assert_eq!( + created_block, + CreatedBlock { + hash: created_block.hash.clone(), + aux: ImportedAux { + header_only: false, + clear_justification_requests: false, + needs_justification: false, + bad_justification: false, + needs_finality_proof: false, + is_new_best: true, + } + } + ); + // assert that there's a new block in the db. + let header = backend.blockchain().header(BlockId::Number(1)).unwrap().unwrap(); + let (tx, rx) = futures::channel::oneshot::channel(); + sink.send(EngineCommand::FinalizeBlock { + sender: Some(tx), + hash: header.hash(), + justification: None + }).await.unwrap(); + // assert that the background task returns ok + assert_eq!(rx.await.unwrap().unwrap(), ()); + } + + #[tokio::test] + async fn manual_seal_fork_blocks() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + let select_chain = LongestChain::new(backend.clone()); + let inherent_data_providers = InherentDataProviders::new(); + let pool_api = api(); + let pool = Arc::new(BasicPool::new(Options::default(), pool_api.clone())); + let env = ProposerFactory { + transaction_pool: pool.clone(), + client: client.clone(), + }; + // this test checks that blocks are created as soon as an engine command is sent over the stream. + let (mut sink, stream) = futures::channel::mpsc::channel(1024); + let future = run_manual_seal( + Box::new(client.clone()), + env, + backend.clone(), + pool.pool().clone(), + stream, + select_chain, + inherent_data_providers, + ); + std::thread::spawn(|| { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + // spawn the background authorship task + rt.block_on(future); + }); + // submit a transaction to pool. + let result = pool.submit_one(&BlockId::Number(0), uxt(Alice, 0)).await; + // assert that it was successfully imported + assert!(result.is_ok()); + + let (tx, rx) = futures::channel::oneshot::channel(); + sink.send(EngineCommand::SealNewBlock { + parent_hash: None, + sender: Some(tx), + create_empty: false, + finalize: false, + }).await.unwrap(); + let created_block = rx.await.unwrap().unwrap(); + pool_api.increment_nonce(Alice.into()); + + // assert that the background task returns ok + assert_eq!( + created_block, + CreatedBlock { + hash: created_block.hash.clone(), + aux: ImportedAux { + header_only: false, + clear_justification_requests: false, + needs_justification: false, + bad_justification: false, + needs_finality_proof: false, + is_new_best: true + } + } + ); + // assert that there's a new block in the db. + assert!(backend.blockchain().header(BlockId::Number(0)).unwrap().is_some()); + assert!(pool.submit_one(&BlockId::Number(1), uxt(Alice, 1)).await.is_ok()); + + let (tx1, rx1) = futures::channel::oneshot::channel(); + assert!(sink.send(EngineCommand::SealNewBlock { + parent_hash: Some(created_block.hash.clone()), + sender: Some(tx1), + create_empty: false, + finalize: false, + }).await.is_ok()); + assert!(rx1.await.unwrap().is_ok()); + assert!(backend.blockchain().header(BlockId::Number(1)).unwrap().is_some()); + pool_api.increment_nonce(Alice.into()); + + assert!(pool.submit_one(&BlockId::Number(2), uxt(Alice, 2)).await.is_ok()); + let (tx2, rx2) = futures::channel::oneshot::channel(); + assert!(sink.send(EngineCommand::SealNewBlock { + parent_hash: Some(created_block.hash), + sender: Some(tx2), + create_empty: false, + finalize: false, + }).await.is_ok()); + let imported = rx2.await.unwrap().unwrap(); + // assert that fork block is in the db + assert!(backend.blockchain().header(BlockId::Hash(imported.hash)).unwrap().is_some()) + } +} diff --git a/client/consensus/manual-seal/src/rpc.rs b/client/consensus/manual-seal/src/rpc.rs new file mode 100644 index 0000000000000000000000000000000000000000..f3f0fe4a128ed86c0135ccfad5111708d7b37bbf --- /dev/null +++ b/client/consensus/manual-seal/src/rpc.rs @@ -0,0 +1,163 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! RPC interface for the ManualSeal Engine. +use sp_consensus::ImportedAux; +use jsonrpc_core::Error; +use jsonrpc_derive::rpc; +use futures::{ + channel::{mpsc, oneshot}, + TryFutureExt, + FutureExt, + SinkExt +}; +use serde::{Deserialize, Serialize}; +use sp_runtime::Justification; +pub use self::gen_client::Client as ManualSealClient; + +/// Future's type for jsonrpc +type FutureResult = Box + Send>; +/// sender passed to the authorship task to report errors or successes. +pub type Sender = Option>>; + +/// Message sent to the background authorship task, usually by RPC. +pub enum EngineCommand { + /// Tells the engine to propose a new block + /// + /// if create_empty == true, it will create empty blocks if there are no transactions + /// in the transaction pool. + /// + /// if finalize == true, the block will be instantly finalized. + SealNewBlock { + /// if true, empty blocks(without extrinsics) will be created. + /// otherwise, will return Error::EmptyTransactionPool. + create_empty: bool, + /// instantly finalize this block? + finalize: bool, + /// specify the parent hash of the about-to-created block + parent_hash: Option, + /// sender to report errors/success to the rpc. + sender: Sender>, + }, + /// Tells the engine to finalize the block with the supplied hash + FinalizeBlock { + /// hash of the block + hash: Hash, + /// sender to report errors/success to the rpc. + sender: Sender<()>, + /// finalization justification + justification: Option, + } +} + +/// RPC trait that provides methods for interacting with the manual-seal authorship task over rpc. +#[rpc] +pub trait ManualSealApi { + /// Instructs the manual-seal authorship task to create a new block + #[rpc(name = "engine_createBlock")] + fn create_block( + &self, + create_empty: bool, + finalize: bool, + parent_hash: Option + ) -> FutureResult>; + + /// Instructs the manual-seal authorship task to finalize a block + #[rpc(name = "engine_finalizeBlock")] + fn finalize_block( + &self, + hash: Hash, + justification: Option + ) -> FutureResult; +} + +/// A struct that implements the [`ManualSealApi`]. +pub struct ManualSeal { + import_block_channel: mpsc::Sender>, +} + +/// return type of `engine_createBlock` +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +pub struct CreatedBlock { + /// hash of the created block. + pub hash: Hash, + /// some extra details about the import operation + pub aux: ImportedAux +} + +impl ManualSeal { + /// Create new `ManualSeal` with the given reference to the client. + pub fn new(import_block_channel: mpsc::Sender>) -> Self { + Self { import_block_channel } + } +} + +impl ManualSealApi for ManualSeal { + fn create_block( + &self, + create_empty: bool, + finalize: bool, + parent_hash: Option + ) -> FutureResult> { + let mut sink = self.import_block_channel.clone(); + let future = async move { + let (sender, receiver) = oneshot::channel(); + let command = EngineCommand::SealNewBlock { + create_empty, + finalize, + parent_hash, + sender: Some(sender), + }; + sink.send(command).await?; + receiver.await? + }.boxed(); + + Box::new(future.map_err(Error::from).compat()) + } + + fn finalize_block(&self, hash: Hash, justification: Option) -> FutureResult { + let mut sink = self.import_block_channel.clone(); + let future = async move { + let (sender, receiver) = oneshot::channel(); + sink.send( + EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification } + ).await?; + + receiver.await?.map(|_| true) + }; + + Box::new(future.boxed().map_err(Error::from).compat()) + } +} + +/// report any errors or successes encountered by the authorship task back +/// to the rpc +pub fn send_result( + sender: &mut Sender, + result: std::result::Result +) { + if let Some(sender) = sender.take() { + if let Err(err) = sender.send(result) { + log::warn!("Server is shutting down: {:?}", err) + } + } else { + // instant seal doesn't report errors over rpc, simply log them. + match result { + Ok(r) => log::info!("Instant Seal success: {:?}", r), + Err(e) => log::error!("Instant Seal encountered an error: {}", e) + } + } +} diff --git a/client/consensus/manual-seal/src/seal_new_block.rs b/client/consensus/manual-seal/src/seal_new_block.rs new file mode 100644 index 0000000000000000000000000000000000000000..53dc82d353e673a82c6159707e2e2bc3c14a712a --- /dev/null +++ b/client/consensus/manual-seal/src/seal_new_block.rs @@ -0,0 +1,148 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Block sealing utilities + +use crate::{Error, rpc}; +use std::sync::Arc; +use sp_runtime::{ + traits::{Block as BlockT, Header as HeaderT}, + generic::BlockId, +}; +use futures::prelude::*; +use sc_transaction_pool::txpool; +use rpc::CreatedBlock; + +use sp_consensus::{ + self, BlockImport, Environment, Proposer, + ForkChoiceStrategy, BlockImportParams, BlockOrigin, + ImportResult, SelectChain, + import_queue::BoxBlockImport, +}; +use sp_blockchain::HeaderBackend; +use sc_client_api::backend::Backend as ClientBackend; +use std::collections::HashMap; +use std::time::Duration; +use sp_inherents::InherentDataProviders; + +/// max duration for creating a proposal in secs +const MAX_PROPOSAL_DURATION: u64 = 10; + +/// params for sealing a new block +pub struct SealBlockParams<'a, B: BlockT, C, CB, E, T, P: txpool::ChainApi> { + /// if true, empty blocks(without extrinsics) will be created. + /// otherwise, will return Error::EmptyTransactionPool. + pub create_empty: bool, + /// instantly finalize this block? + pub finalize: bool, + /// specify the parent hash of the about-to-created block + pub parent_hash: Option<::Hash>, + /// sender to report errors/success to the rpc. + pub sender: rpc::Sender::Hash>>, + /// transaction pool + pub pool: Arc>, + /// client backend + pub backend: Arc, + /// Environment trait object for creating a proposer + pub env: &'a mut E, + /// SelectChain object + pub select_chain: &'a C, + /// block import object + pub block_import: &'a mut BoxBlockImport, + /// inherent data provider + pub inherent_data_provider: &'a InherentDataProviders, +} + +/// seals a new block with the given params +pub async fn seal_new_block( + SealBlockParams { + create_empty, + finalize, + pool, + parent_hash, + backend: back_end, + select_chain, + block_import, + env, + inherent_data_provider, + mut sender, + .. + }: SealBlockParams<'_, B, SC, CB, E, T, P> +) + where + B: BlockT, + CB: ClientBackend, + E: Environment, + >::Error: std::fmt::Display, + >::Error: std::fmt::Display, + P: txpool::ChainApi::Hash>, + SC: SelectChain, +{ + let future = async { + if pool.status().ready == 0 && !create_empty { + return Err(Error::EmptyTransactionPool) + } + + // get the header to build this new block on. + // use the parent_hash supplied via `EngineCommand` + // or fetch the best_block. + let header = match parent_hash { + Some(hash) => { + match back_end.blockchain().header(BlockId::Hash(hash))? { + Some(header) => header, + None => return Err(Error::BlockNotFound(format!("{}", hash))), + } + } + None => select_chain.best_chain()? + }; + + let mut proposer = env.init(&header) + .map_err(|err| Error::StringError(format!("{}", err))).await?; + let id = inherent_data_provider.create_inherent_data()?; + let inherents_len = id.len(); + let proposal = proposer.propose(id, Default::default(), Duration::from_secs(MAX_PROPOSAL_DURATION), false.into()) + .map_err(|err| Error::StringError(format!("{}", err))).await?; + + if proposal.block.extrinsics().len() == inherents_len && !create_empty { + return Err(Error::EmptyTransactionPool) + } + + let (header, body) = proposal.block.deconstruct(); + let params = BlockImportParams { + origin: BlockOrigin::Own, + header: header.clone(), + justification: None, + post_digests: Vec::new(), + body: Some(body), + finalized: finalize, + storage_changes: None, + auxiliary: Vec::new(), + intermediates: HashMap::new(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), + allow_missing_state: false, + import_existing: false, + }; + + match block_import.import_block(params, HashMap::new())? { + ImportResult::Imported(aux) => { + Ok(CreatedBlock { hash: ::Header::hash(&header), aux }) + }, + other => Err(other.into()), + } + }; + + rpc::send_result(&mut sender, future.await) +} diff --git a/client/consensus/pow/Cargo.toml b/client/consensus/pow/Cargo.toml index bf0efa6c08cd80a71727ba93e59dbea06f5ea408..b31d9406e1e852101016f1af51a9ef9c9cafaaf4 100644 --- a/client/consensus/pow/Cargo.toml +++ b/client/consensus/pow/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "PoW consensus algorithm for substrate" edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index 2ea0cbdf0f7ae17062c1040b2bf74ab2159801d1..d656f71a15b19289172571c5000bfb42659deb66 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -30,8 +30,11 @@ //! clients. use std::sync::Arc; +use std::any::Any; +use std::borrow::Cow; use std::thread; use std::collections::HashMap; +use std::marker::PhantomData; use sc_client_api::{BlockOf, backend::AuxStore}; use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId}; use sp_block_builder::BlockBuilder as BlockBuilderApi; @@ -43,7 +46,8 @@ use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID}; use sp_inherents::{InherentDataProviders, InherentData}; use sp_consensus::{ BlockImportParams, BlockOrigin, ForkChoiceStrategy, SyncOracle, Environment, Proposer, - SelectChain, Error as ConsensusError, CanAuthorWith, RecordProof, + SelectChain, Error as ConsensusError, CanAuthorWith, RecordProof, BlockImport, + BlockCheckParams, ImportResult, }; use sp_consensus::import_queue::{BoxBlockImport, BasicQueue, Verifier}; use codec::{Encode, Decode}; @@ -59,6 +63,8 @@ pub enum Error { HeaderUnsealed(B::Hash), #[display(fmt = "PoW validation error: invalid seal")] InvalidSeal, + #[display(fmt = "PoW validation error: preliminary verification failed")] + FailedPreliminaryVerify, #[display(fmt = "Rejecting block too far in future")] TooFarInFuture, #[display(fmt = "Fetching best header failed using select chain: {:?}", _0)] @@ -89,6 +95,12 @@ impl std::convert::From> for String { } } +impl std::convert::From> for ConsensusError { + fn from(error: Error) -> ConsensusError { + ConsensusError::ClientImport(error.to_string()) + } +} + /// Auxiliary storage prefix for PoW engine. pub const POW_AUX_PREFIX: [u8; 4] = *b"PoW:"; @@ -97,6 +109,18 @@ fn aux_key>(hash: &T) -> Vec { POW_AUX_PREFIX.iter().chain(hash.as_ref()).copied().collect() } +/// Intermediate value passed to block importer. +#[derive(Encode, Decode, Clone, Debug, Default)] +pub struct PowIntermediate { + /// The header hash with seal, used for auxiliary key. + pub hash: B::Hash, + /// Difficulty of the block, if known. + pub difficulty: Option, +} + +/// Intermediate key for PoW engine. +pub static INTERMEDIATE_KEY: &[u8] = b"pow1"; + /// Auxiliary storage data for PoW. #[derive(Encode, Decode, Clone, Debug, Default)] pub struct PowAux { @@ -126,8 +150,21 @@ pub trait PowAlgorithm { type Difficulty: TotalDifficulty + Default + Encode + Decode + Ord + Clone + Copy; /// Get the next block's difficulty. + /// + /// This function will be called twice during the import process, so the implementation + /// should be properly cached. fn difficulty(&self, parent: &BlockId) -> Result>; - /// Verify proof of work against the given difficulty. + /// Verify that the seal is valid against given pre hash when parent block is not yet imported. + /// + /// None means that preliminary verify is not available for this algorithm. + fn preliminary_verify( + &self, + _pre_hash: &B::Hash, + _seal: &Seal, + ) -> Result, Error> { + Ok(None) + } + /// Verify that the difficulty is valid against given seal. fn verify( &self, parent: &BlockId, @@ -145,59 +182,48 @@ pub trait PowAlgorithm { ) -> Result, Error>; } -/// A verifier for PoW blocks. -pub struct PowVerifier { - client: Arc, +/// A block importer for PoW. +pub struct PowBlockImport { algorithm: Algorithm, - inherent_data_providers: sp_inherents::InherentDataProviders, + inner: I, select_chain: Option, + client: Arc, + inherent_data_providers: sp_inherents::InherentDataProviders, check_inherents_after: <::Header as HeaderT>::Number, } -impl PowVerifier { +impl Clone for PowBlockImport { + fn clone(&self) -> Self { + Self { + algorithm: self.algorithm.clone(), + inner: self.inner.clone(), + select_chain: self.select_chain.clone(), + client: self.client.clone(), + inherent_data_providers: self.inherent_data_providers.clone(), + check_inherents_after: self.check_inherents_after.clone(), + } + } +} + +impl PowBlockImport where + B: BlockT, + I: BlockImport> + Send + Sync, + I::Error: Into, + C: ProvideRuntimeApi + Send + Sync + HeaderBackend + AuxStore + ProvideCache + BlockOf, + C::Api: BlockBuilderApi, + Algorithm: PowAlgorithm, +{ + /// Create a new block import suitable to be used in PoW pub fn new( + inner: I, client: Arc, algorithm: Algorithm, check_inherents_after: <::Header as HeaderT>::Number, select_chain: Option, inherent_data_providers: sp_inherents::InherentDataProviders, ) -> Self { - Self { client, algorithm, inherent_data_providers, select_chain, check_inherents_after } - } - - fn check_header( - &self, - mut header: B::Header, - parent_block_id: BlockId, - ) -> Result<(B::Header, Algorithm::Difficulty, DigestItem), Error> where - Algorithm: PowAlgorithm, - { - let hash = header.hash(); - - let (seal, inner_seal) = match header.digest_mut().pop() { - Some(DigestItem::Seal(id, seal)) => { - if id == POW_ENGINE_ID { - (DigestItem::Seal(id, seal.clone()), seal) - } else { - return Err(Error::WrongEngine(id)) - } - }, - _ => return Err(Error::HeaderUnsealed(hash)), - }; - - let pre_hash = header.hash(); - let difficulty = self.algorithm.difficulty(&parent_block_id)?; - - if !self.algorithm.verify( - &parent_block_id, - &pre_hash, - &inner_seal, - difficulty, - )? { - return Err(Error::InvalidSeal); - } - - Ok((header, difficulty, seal)) + Self { inner, client, algorithm, check_inherents_after, + select_chain, inherent_data_providers } } fn check_inherents( @@ -206,9 +232,7 @@ impl PowVerifier { block_id: BlockId, inherent_data: InherentData, timestamp_now: u64, - ) -> Result<(), Error> where - C: ProvideRuntimeApi, C::Api: BlockBuilderApi - { + ) -> Result<(), Error> { const MAX_TIMESTAMP_DRIFT_SECS: u64 = 60; if *block.header().number() < self.check_inherents_after { @@ -243,55 +267,165 @@ impl PowVerifier { } } -impl Verifier for PowVerifier where +impl BlockImport for PowBlockImport where + B: BlockT, + I: BlockImport> + Send + Sync, + I::Error: Into, + S: SelectChain, C: ProvideRuntimeApi + Send + Sync + HeaderBackend + AuxStore + ProvideCache + BlockOf, C::Api: BlockBuilderApi, - S: SelectChain, - Algorithm: PowAlgorithm + Send + Sync, + Algorithm: PowAlgorithm, + Algorithm::Difficulty: 'static, { - fn verify( + type Error = ConsensusError; + type Transaction = sp_api::TransactionFor; + + fn check_block( &mut self, - origin: BlockOrigin, - header: B::Header, - justification: Option, - mut body: Option>, - ) -> Result<(BlockImportParams, Option)>>), String> { - let inherent_data = self.inherent_data_providers - .create_inherent_data().map_err(|e| e.into_string())?; - let timestamp_now = inherent_data.timestamp_inherent_data().map_err(|e| e.into_string())?; + block: BlockCheckParams, + ) -> Result { + self.inner.check_block(block).map_err(Into::into) + } + fn import_block( + &mut self, + mut block: BlockImportParams, + new_cache: HashMap>, + ) -> Result { let best_hash = match self.select_chain.as_ref() { Some(select_chain) => select_chain.best_chain() .map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))? .hash(), None => self.client.info().best_hash, }; - let hash = header.hash(); - let parent_hash = *header.parent_hash(); + + let parent_hash = *block.header.parent_hash(); let best_aux = PowAux::read::<_, B>(self.client.as_ref(), &best_hash)?; let mut aux = PowAux::read::<_, B>(self.client.as_ref(), &parent_hash)?; - let (checked_header, difficulty, seal) = self.check_header( - header, - BlockId::Hash(parent_hash), - )?; - aux.difficulty = difficulty; - aux.total_difficulty.increment(difficulty); + if let Some(inner_body) = block.body.take() { + let inherent_data = self.inherent_data_providers + .create_inherent_data().map_err(|e| e.into_string())?; + let timestamp_now = inherent_data.timestamp_inherent_data().map_err(|e| e.into_string())?; - if let Some(inner_body) = body.take() { - let block = B::new(checked_header.clone(), inner_body); + let check_block = B::new(block.header.clone(), inner_body); self.check_inherents( - block.clone(), + check_block.clone(), BlockId::Hash(parent_hash), inherent_data, timestamp_now )?; - body = Some(block.deconstruct().1); + block.body = Some(check_block.deconstruct().1); } - let key = aux_key(&hash); + let inner_seal = match block.post_digests.last() { + Some(DigestItem::Seal(id, seal)) => { + if id == &POW_ENGINE_ID { + seal.clone() + } else { + return Err(Error::::WrongEngine(*id).into()) + } + }, + _ => return Err(Error::::HeaderUnsealed(block.header.hash()).into()), + }; + + let intermediate = block.take_intermediate::>( + INTERMEDIATE_KEY + )?; + + let difficulty = match intermediate.difficulty { + Some(difficulty) => difficulty, + None => self.algorithm.difficulty(&BlockId::hash(parent_hash))?, + }; + + let pre_hash = block.header.hash(); + if !self.algorithm.verify( + &BlockId::hash(parent_hash), + &pre_hash, + &inner_seal, + difficulty, + )? { + return Err(Error::::InvalidSeal.into()) + } + + aux.difficulty = difficulty; + aux.total_difficulty.increment(difficulty); + + let key = aux_key(&intermediate.hash); + block.auxiliary.push((key, Some(aux.encode()))); + if block.fork_choice.is_none() { + block.fork_choice = Some(ForkChoiceStrategy::Custom( + aux.total_difficulty > best_aux.total_difficulty + )); + } + + self.inner.import_block(block, new_cache).map_err(Into::into) + } +} + +/// A verifier for PoW blocks. +pub struct PowVerifier { + algorithm: Algorithm, + _marker: PhantomData, +} + +impl PowVerifier { + pub fn new( + algorithm: Algorithm, + ) -> Self { + Self { algorithm, _marker: PhantomData } + } + + fn check_header( + &self, + mut header: B::Header, + ) -> Result<(B::Header, DigestItem), Error> where + Algorithm: PowAlgorithm, + { + let hash = header.hash(); + + let (seal, inner_seal) = match header.digest_mut().pop() { + Some(DigestItem::Seal(id, seal)) => { + if id == POW_ENGINE_ID { + (DigestItem::Seal(id, seal.clone()), seal) + } else { + return Err(Error::WrongEngine(id)) + } + }, + _ => return Err(Error::HeaderUnsealed(hash)), + }; + + let pre_hash = header.hash(); + + if !self.algorithm.preliminary_verify(&pre_hash, &inner_seal)?.unwrap_or(true) { + return Err(Error::FailedPreliminaryVerify); + } + + Ok((header, seal)) + } +} + +impl Verifier for PowVerifier where + Algorithm: PowAlgorithm + Send + Sync, + Algorithm::Difficulty: 'static, +{ + fn verify( + &mut self, + origin: BlockOrigin, + header: B::Header, + justification: Option, + body: Option>, + ) -> Result<(BlockImportParams, Option)>>), String> { + let hash = header.hash(); + let (checked_header, seal) = self.check_header(header)?; + + let intermediate = PowIntermediate:: { + hash: hash, + difficulty: None, + }; + let import_block = BlockImportParams { origin, header: checked_header, @@ -300,10 +434,13 @@ impl Verifier for PowVerifier storage_changes: None, finalized: false, justification, - auxiliary: vec![(key, Some(aux.encode()))], - fork_choice: ForkChoiceStrategy::Custom( - aux.total_difficulty > best_aux.total_difficulty - ), + intermediates: { + let mut ret = HashMap::new(); + ret.insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box); + ret + }, + auxiliary: vec![], + fork_choice: None, allow_missing_state: false, import_existing: false, }; @@ -330,33 +467,21 @@ pub fn register_pow_inherent_data_provider( pub type PowImportQueue = BasicQueue; /// Import queue for PoW engine. -pub fn import_queue( - block_import: BoxBlockImport>, - client: Arc, +pub fn import_queue( + block_import: BoxBlockImport, algorithm: Algorithm, - check_inherents_after: <::Header as HeaderT>::Number, - select_chain: Option, inherent_data_providers: InherentDataProviders, ) -> Result< - PowImportQueue>, + PowImportQueue, sp_consensus::Error > where B: BlockT, - C: ProvideRuntimeApi + HeaderBackend + BlockOf + ProvideCache + AuxStore, - C: Send + Sync + AuxStore + 'static, - C::Api: BlockBuilderApi, - Algorithm: PowAlgorithm + Send + Sync + 'static, - S: SelectChain + 'static, + Transaction: Send + Sync + 'static, + Algorithm: PowAlgorithm + Clone + Send + Sync + 'static, { register_pow_inherent_data_provider(&inherent_data_providers)?; - let verifier = PowVerifier::new( - client.clone(), - algorithm, - check_inherents_after, - select_chain, - inherent_data_providers, - ); + let verifier = PowVerifier::new(algorithm); Ok(BasicQueue::new( verifier, @@ -443,6 +568,7 @@ fn mine_loop( ) -> Result<(), Error> where C: HeaderBackend + AuxStore + ProvideRuntimeApi, Algorithm: PowAlgorithm, + Algorithm::Difficulty: 'static, E: Environment, E::Proposer: Proposer>, E::Error: std::fmt::Debug, @@ -485,7 +611,6 @@ fn mine_loop( continue 'outer } - let mut aux = PowAux::read(client, &best_hash)?; let mut proposer = futures::executor::block_on(env.init(&best_header)) .map_err(|e| Error::Environment(format!("{:?}", e)))?; @@ -526,38 +651,36 @@ fn mine_loop( } }; - aux.difficulty = difficulty; - aux.total_difficulty.increment(difficulty); - let hash = { + let (hash, seal) = { + let seal = DigestItem::Seal(POW_ENGINE_ID, seal); let mut header = header.clone(); - header.digest_mut().push(DigestItem::Seal(POW_ENGINE_ID, seal.clone())); - header.hash() + header.digest_mut().push(seal); + let hash = header.hash(); + let seal = header.digest_mut().pop() + .expect("Pushed one seal above; length greater than zero; qed"); + (hash, seal) }; - let key = aux_key(&hash); - let best_hash = match select_chain { - Some(select_chain) => select_chain.best_chain() - .map_err(Error::BestHashSelectChain)? - .hash(), - None => client.info().best_hash, + let intermediate = PowIntermediate:: { + hash, + difficulty: Some(difficulty), }; - let best_aux = PowAux::::read(client, &best_hash)?; - - // if the best block has changed in the meantime drop our proposal - if best_aux.total_difficulty > aux.total_difficulty { - continue 'outer - } let import_block = BlockImportParams { origin: BlockOrigin::Own, header, justification: None, - post_digests: vec![DigestItem::Seal(POW_ENGINE_ID, seal)], + post_digests: vec![seal], body: Some(body), storage_changes: Some(proposal.storage_changes), + intermediates: { + let mut ret = HashMap::new(); + ret.insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box); + ret + }, finalized: false, - auxiliary: vec![(key, Some(aux.encode()))], - fork_choice: ForkChoiceStrategy::Custom(true), + auxiliary: vec![], + fork_choice: None, allow_missing_state: false, import_existing: false, }; diff --git a/client/consensus/slots/Cargo.toml b/client/consensus/slots/Cargo.toml index 89ee14b8fa4637b95294ed9e3c12f3a037a18bd7..67de0a54ed7b657418a7abb9710d0672e042733a 100644 --- a/client/consensus/slots/Cargo.toml +++ b/client/consensus/slots/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Parity Technologies "] description = "Generic slots-based utilities for consensus" edition = "2018" build = "build.rs" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } @@ -18,8 +19,8 @@ sc-telemetry = { version = "2.0.0", path = "../../telemetry" } sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" } sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } futures = "0.3.1" -futures-timer = "2.0" -parking_lot = "0.9.0" +futures-timer = "3.0.1" +parking_lot = "0.10.0" log = "0.4.8" [dev-dependencies] diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index c6185d5d307b9c87394e117c94d7ac23c458136d..8bc2547a49e39ea96a0856f36bc878e421f648ab 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -81,7 +81,7 @@ pub trait SimpleSlotWorker { type Claim: Send + 'static; /// Epoch data necessary for authoring. - type EpochData; + type EpochData: Send + 'static; /// The logging target to use when logging messages. fn logging_target(&self) -> &'static str; @@ -119,6 +119,7 @@ pub trait SimpleSlotWorker { Vec, StorageChanges<>::Transaction, B>, Self::Claim, + Self::EpochData, ) -> sp_consensus::BlockImportParams< B, >::Transaction @@ -280,6 +281,7 @@ pub trait SimpleSlotWorker { body, proposal.storage_changes, claim, + epoch_data, ); info!( diff --git a/client/consensus/uncles/Cargo.toml b/client/consensus/uncles/Cargo.toml index 54789c57944a1075caeff2e5a00283ba5bb6b826..f336564c4ec14db9fca99f0e50c2155d81dc35ac 100644 --- a/client/consensus/uncles/Cargo.toml +++ b/client/consensus/uncles/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Generic uncle inclusion utilities for consensus" edition = "2018" +license = "GPL-3.0" [dependencies] sc-client-api = { version = "2.0.0", path = "../../api" } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index dcc7df29ca11c64470d6fa0da2cc3e4158bdefd1..ef418d43efc183facef019a1e9e57912c3951140 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -3,16 +3,18 @@ name = "sc-client-db" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] -parking_lot = "0.9.0" +parking_lot = "0.10.0" log = "0.4.8" -kvdb = "0.3.0" -kvdb-rocksdb = { version = "0.4", optional = true } -kvdb-memorydb = "0.3.0" +rand = "0.7" +kvdb = "0.4.0" +kvdb-rocksdb = { version = "0.5", optional = true } +kvdb-memorydb = "0.4.0" linked-hash-map = "0.5.2" hash-db = "0.15.2" -parity-util-mem = { version = "0.4", default-features = false, features = ["std"] } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["std"] } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } sc-client-api = { version = "2.0.0", path = "../api" } @@ -31,8 +33,8 @@ sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } env_logger = "0.7.0" quickcheck = "0.9" -kvdb-rocksdb = "0.4" -tempdir = "0.3" +kvdb-rocksdb = "0.5" +tempfile = "3" [features] default = [] diff --git a/client/db/src/bench.rs b/client/db/src/bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..9858a5c148bfaa13f34af57ce4091540e1b39928 --- /dev/null +++ b/client/db/src/bench.rs @@ -0,0 +1,286 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! State backend that's useful for benchmarking + +use std::sync::Arc; +use std::path::PathBuf; +use std::cell::{Cell, RefCell}; +use rand::Rng; + +use hash_db::{Prefix, Hasher}; +use sp_trie::{MemoryDB, prefixed_key}; +use sp_core::storage::ChildInfo; +use sp_runtime::traits::{Block as BlockT, HasherFor}; +use sp_runtime::Storage; +use sp_state_machine::{DBValue, backend::Backend as StateBackend}; +use kvdb::{KeyValueDB, DBTransaction}; +use kvdb_rocksdb::{Database, DatabaseConfig}; + +type DbState = sp_state_machine::TrieBackend< + Arc>>, HasherFor +>; + +struct StorageDb { + db: Arc, + _block: std::marker::PhantomData, +} + +impl sp_state_machine::Storage> for StorageDb { + fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result, String> { + let key = prefixed_key::>(key, prefix); + self.db.get(0, &key) + .map_err(|e| format!("Database backend error: {:?}", e)) + } +} + +/// State that manages the backend database reference. Allows runtime to control the database. +pub struct BenchmarkingState { + path: PathBuf, + root: Cell, + state: RefCell>>, + db: Cell>>, + genesis: as StateBackend>>::Transaction, +} + +impl BenchmarkingState { + /// Create a new instance that creates a database in a temporary dir. + pub fn new(genesis: Storage) -> Result { + let temp_dir = PathBuf::from(std::env::temp_dir()); + let name: String = rand::thread_rng().sample_iter(&rand::distributions::Alphanumeric).take(10).collect(); + let path = temp_dir.join(&name); + + let mut root = B::Hash::default(); + let mut mdb = MemoryDB::>::default(); + sp_state_machine::TrieDBMut::>::new(&mut mdb, &mut root); + + std::fs::create_dir(&path).map_err(|_| String::from("Error creating temp dir"))?; + let mut state = BenchmarkingState { + state: RefCell::new(None), + db: Cell::new(None), + path, + root: Cell::new(root), + genesis: Default::default(), + }; + + state.reopen()?; + let child_delta = genesis.children.into_iter().map(|(storage_key, child_content)| ( + storage_key, + child_content.data.into_iter().map(|(k, v)| (k, Some(v))), + child_content.child_info + )); + let (root, transaction) = state.state.borrow_mut().as_mut().unwrap().full_storage_root( + genesis.top.into_iter().map(|(k, v)| (k, Some(v))), + child_delta, + ); + state.genesis = transaction.clone(); + state.commit(root, transaction)?; + Ok(state) + } + + fn reopen(&self) -> Result<(), String> { + *self.state.borrow_mut() = None; + self.db.set(None); + let db_config = DatabaseConfig::with_columns(1); + let path = self.path.to_str() + .ok_or_else(|| String::from("Invalid database path"))?; + let db = Arc::new(Database::open(&db_config, &path).map_err(|e| format!("Error opening database: {:?}", e))?); + self.db.set(Some(db.clone())); + let storage_db = Arc::new(StorageDb:: { db, _block: Default::default() }); + *self.state.borrow_mut() = Some(DbState::::new(storage_db, self.root.get())); + Ok(()) + } + + fn kill(&self) -> Result<(), String> { + self.db.set(None); + *self.state.borrow_mut() = None; + let mut root = B::Hash::default(); + let mut mdb = MemoryDB::>::default(); + sp_state_machine::TrieDBMut::>::new(&mut mdb, &mut root); + self.root.set(root); + + std::fs::remove_dir_all(&self.path).map_err(|_| "Error removing database dir".into()) + } +} + +impl Drop for BenchmarkingState { + fn drop(&mut self) { + self.kill().ok(); + } +} + +fn state_err() -> String { + "State is not open".into() +} + +impl StateBackend> for BenchmarkingState { + type Error = as StateBackend>>::Error; + type Transaction = as StateBackend>>::Transaction; + type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; + + fn storage(&self, key: &[u8]) -> Result>, Self::Error> { + self.state.borrow().as_ref().ok_or_else(state_err)?.storage(key) + } + + fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { + self.state.borrow().as_ref().ok_or_else(state_err)?.storage_hash(key) + } + + fn child_storage( + &self, + storage_key: &[u8], + child_info: ChildInfo, + key: &[u8], + ) -> Result>, Self::Error> { + self.state.borrow().as_ref().ok_or_else(state_err)?.child_storage(storage_key, child_info, key) + } + + fn exists_storage(&self, key: &[u8]) -> Result { + self.state.borrow().as_ref().ok_or_else(state_err)?.exists_storage(key) + } + + fn exists_child_storage( + &self, + storage_key: &[u8], + child_info: ChildInfo, + key: &[u8], + ) -> Result { + self.state.borrow().as_ref().ok_or_else(state_err)?.exists_child_storage(storage_key, child_info, key) + } + + fn next_storage_key(&self, key: &[u8]) -> Result>, Self::Error> { + self.state.borrow().as_ref().ok_or_else(state_err)?.next_storage_key(key) + } + + fn next_child_storage_key( + &self, + storage_key: &[u8], + child_info: ChildInfo, + key: &[u8], + ) -> Result>, Self::Error> { + self.state.borrow().as_ref().ok_or_else(state_err)?.next_child_storage_key(storage_key, child_info, key) + } + + fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { + if let Some(ref state) = *self.state.borrow() { + state.for_keys_with_prefix(prefix, f) + } + } + + fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { + if let Some(ref state) = *self.state.borrow() { + state.for_key_values_with_prefix(prefix, f) + } + } + + fn for_keys_in_child_storage( + &self, + storage_key: &[u8], + child_info: ChildInfo, + f: F, + ) { + if let Some(ref state) = *self.state.borrow() { + state.for_keys_in_child_storage(storage_key, child_info, f) + } + } + + fn for_child_keys_with_prefix( + &self, + storage_key: &[u8], + child_info: ChildInfo, + prefix: &[u8], + f: F, + ) { + if let Some(ref state) = *self.state.borrow() { + state.for_child_keys_with_prefix(storage_key, child_info, prefix, f) + } + } + + fn storage_root(&self, delta: I) -> (B::Hash, Self::Transaction) where + I: IntoIterator, Option>)> + { + self.state.borrow().as_ref().map_or(Default::default(), |s| s.storage_root(delta)) + } + + fn child_storage_root( + &self, + storage_key: &[u8], + child_info: ChildInfo, + delta: I, + ) -> (B::Hash, bool, Self::Transaction) where + I: IntoIterator, Option>)>, + { + self.state.borrow().as_ref().map_or(Default::default(), |s| s.child_storage_root(storage_key, child_info, delta)) + } + + fn pairs(&self) -> Vec<(Vec, Vec)> { + self.state.borrow().as_ref().map_or(Default::default(), |s| s.pairs()) + } + + fn keys(&self, prefix: &[u8]) -> Vec> { + self.state.borrow().as_ref().map_or(Default::default(), |s| s.keys(prefix)) + } + + fn child_keys( + &self, + storage_key: &[u8], + child_info: ChildInfo, + prefix: &[u8], + ) -> Vec> { + self.state.borrow().as_ref().map_or(Default::default(), |s| s.child_keys(storage_key, child_info, prefix)) + } + + fn as_trie_backend(&mut self) + -> Option<&sp_state_machine::TrieBackend>> + { + None + } + + fn commit(&self, storage_root: as Hasher>::Out, mut transaction: Self::Transaction) + -> Result<(), Self::Error> + { + if let Some(db) = self.db.take() { + let mut db_transaction = DBTransaction::new(); + + for (key, (val, rc)) in transaction.drain() { + if rc > 0 { + db_transaction.put(0, &key, &val); + } else if rc < 0 { + db_transaction.delete(0, &key); + } + } + db.write(db_transaction).map_err(|_| String::from("Error committing transaction"))?; + self.root.set(storage_root); + } else { + return Err("Trying to commit to a closed db".into()) + } + self.reopen() + } + + fn wipe(&self) -> Result<(), Self::Error> { + self.kill()?; + self.reopen()?; + self.commit(self.root.get(), self.genesis.clone())?; + Ok(()) + } +} + +impl std::fmt::Debug for BenchmarkingState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "DB at {:?}", self.path) + } +} + diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index be569194972cc80a1f26b9505a9d256f02381769..b64123428160af1de166a12e868695ac77e0cfc7 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -29,6 +29,9 @@ pub mod light; pub mod offchain; +#[cfg(any(feature = "kvdb-rocksdb", test))] +pub mod bench; + mod children; mod cache; mod changes_tries_storage; @@ -80,6 +83,9 @@ use crate::stats::StateUsageStats; use log::{trace, debug, warn}; pub use sc_state_db::PruningMode; +#[cfg(any(feature = "kvdb-rocksdb", test))] +pub use bench::BenchmarkingState; + #[cfg(feature = "test-helpers")] use sc_client::in_mem::Backend as InMemoryBackend; @@ -699,7 +705,7 @@ impl sp_state_machine::Storage> for DbGenesisSto /// Used as inner structure under lock in `FrozenForDuration`. struct Frozen { at: std::time::Instant, - value: T, + value: Option, } /// Some value frozen for period of time. @@ -709,26 +715,26 @@ struct Frozen { /// a new value which will be again frozen for `duration`. pub(crate) struct FrozenForDuration { duration: std::time::Duration, - value: RwLock>, + value: parking_lot::Mutex>, } impl FrozenForDuration { - fn new(duration: std::time::Duration, initial: T) -> Self { + fn new(duration: std::time::Duration) -> Self { Self { duration, - value: Frozen { at: std::time::Instant::now(), value: initial }.into(), + value: Frozen { at: std::time::Instant::now(), value: None }.into(), } } fn take_or_else(&self, f: F) -> T where F: FnOnce() -> T { - if self.value.read().at.elapsed() > self.duration { - let mut write_lock = self.value.write(); + let mut lock = self.value.lock(); + if lock.at.elapsed() > self.duration || lock.value.is_none() { let new_value = f(); - write_lock.at = std::time::Instant::now(); - write_lock.value = new_value.clone(); + lock.at = std::time::Instant::now(); + lock.value = Some(new_value.clone()); new_value } else { - self.value.read().value.clone() + lock.value.as_ref().expect("checked with lock above").clone() } } } @@ -818,7 +824,7 @@ impl Backend { ), import_lock: Default::default(), is_archive: is_archive_pruning, - io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1), (kvdb::IoStats::empty(), StateUsageInfo::empty())), + io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)), state_usage: StateUsageStats::new(), }) } @@ -1466,6 +1472,7 @@ impl sc_client_api::backend::Backend for Backend { average_transaction_size: io_stats.avg_transaction_size() as u64, state_reads: state_stats.reads.ops, state_reads_cache: state_stats.cache_reads.ops, + state_writes: state_stats.writes.ops, }, }) } diff --git a/client/db/src/light.rs b/client/db/src/light.rs index e663fc569949091fbc8be0bb5163294d610909aa..bea08d64676eeba2758157c107e4ec8ec29e5009 100644 --- a/client/db/src/light.rs +++ b/client/db/src/light.rs @@ -102,7 +102,7 @@ impl LightStorage { cache: Arc::new(DbCacheSync(RwLock::new(cache))), header_metadata_cache: HeaderMetadataCache::default(), #[cfg(not(target_os = "unknown"))] - io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1), kvdb::IoStats::empty()), + io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)), }) } @@ -592,6 +592,7 @@ impl LightBlockchainStorage for LightStorage // Light client does not track those state_reads: 0, state_reads_cache: 0, + state_writes: 0, } }) } diff --git a/client/db/src/upgrade.rs b/client/db/src/upgrade.rs index ab2d4bbf799b25cbf276db4b2b9a4faeb908d948..69230e78519258f0fa55424b72a31b6c045627ce 100644 --- a/client/db/src/upgrade.rs +++ b/client/db/src/upgrade.rs @@ -172,14 +172,14 @@ mod tests { #[test] fn downgrade_never_happens() { - let db_dir = tempdir::TempDir::new("").unwrap(); + let db_dir = tempfile::TempDir::new().unwrap(); create_db(db_dir.path(), Some(CURRENT_VERSION + 1)); assert!(open_database(db_dir.path()).is_err()); } #[test] fn open_empty_database_works() { - let db_dir = tempdir::TempDir::new("").unwrap(); + let db_dir = tempfile::TempDir::new().unwrap(); open_database(db_dir.path()).unwrap(); open_database(db_dir.path()).unwrap(); assert_eq!(current_version(db_dir.path()).unwrap(), CURRENT_VERSION); @@ -188,7 +188,7 @@ mod tests { #[test] fn upgrade_from_0_to_1_works() { for version_from_file in &[None, Some(0)] { - let db_dir = tempdir::TempDir::new("").unwrap(); + let db_dir = tempfile::TempDir::new().unwrap(); let db_path = db_dir.path(); create_db(db_path, *version_from_file); open_database(db_path).unwrap(); diff --git a/client/db/src/utils.rs b/client/db/src/utils.rs index f7f51d7f6de5d8dda4e2306c9adae590feede603..534cbb2197ebce2c55006b10bea4529227418969 100644 --- a/client/db/src/utils.rs +++ b/client/db/src/utils.rs @@ -320,7 +320,9 @@ pub fn require_header( id: BlockId, ) -> sp_blockchain::Result { read_header(db, col_index, col, id) - .and_then(|header| header.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("{}", id)))) + .and_then(|header| header.ok_or_else(|| + sp_blockchain::Error::UnknownBlock(format!("Require header: {}", id)) + )) } /// Read meta from the database. diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index aa8c46999d98d8973d18824126ea35c300fa5f34..c8774470febf3ea3ed82e380b8f0775dd7cbd55c 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-executor" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] derive_more = "0.99.2" @@ -22,7 +23,7 @@ sp-externalities = { version = "0.8.0", path = "../../primitives/externalities" sc-executor-common = { version = "0.8", path = "common" } sc-executor-wasmi = { version = "0.8", path = "wasmi" } sc-executor-wasmtime = { version = "0.8", path = "wasmtime", optional = true } -parking_lot = "0.9.0" +parking_lot = "0.10.0" log = "0.4.8" libsecp256k1 = "0.3.4" diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index cae08762179930cde74fcd7016140ed65a0e8256..e10afe3448b7e960571c7cb1135f97d72a7cf066 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-executor-common" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4.8" @@ -10,6 +11,7 @@ derive_more = "0.99.2" codec = { package = "parity-scale-codec", version = "1.0.0" } wasmi = "0.6.2" sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } sp-serializer = { version = "2.0.0", path = "../../../primitives/serializer" } diff --git a/client/executor/common/src/allocator.rs b/client/executor/common/src/allocator.rs deleted file mode 100644 index feab044f69c32f282de96172d2ac5df36f93270c..0000000000000000000000000000000000000000 --- a/client/executor/common/src/allocator.rs +++ /dev/null @@ -1,557 +0,0 @@ -// Copyright 2017-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! This module implements a freeing-bump allocator. -//! -//! The algorithm is as follows: -//! We store `N` linked list heads, where `N` is the total number of sizes -//! of allocations to support. A simple set is powers of two from 8 bytes -//! to 16,777,216 bytes (2^3 - 2^24 inclusive), resulting in `N = 22`: -//! -//! ```ignore -//! let mut heads [u64; N] = [0; N]; -//! fn size(n: u64) -> u64 { 8 << n } -//! let mut bumper = 0; -//! fn bump(n: u64) -> u64 { let res = bumper; bumper += n; res } -//! ``` -//! -//! We assume there is a slab of heap to be allocated: -//! -//! ```ignore -//! let mut heap = [0u8; HEAP_SIZE]; -//! ``` -//! -//! Whenever we allocate, we select the lowest linked list item size that -//! will fit the allocation (i.e. the next highest power of two). -//! We then check to see if the linked list is empty. If empty, we use -//! the bump allocator to get the allocation with an extra 8 bytes -//! preceding it. We initialise those preceding 8 bytes to identify the -//! list to which it belongs. If it is not empty, we unlink the first item from -//! the linked list and then reset the 8 preceding bytes so they now record -//! the identity of the linked list. -//! -//! To deallocate we use the preceding 8 bytes of the allocation to knit -//! back the allocation into the linked list from the head. - -use crate::error::{Error, Result}; -use log::trace; -use std::convert::{TryFrom, TryInto}; -use std::ops::Range; -use sp_wasm_interface::{Pointer, WordSize}; - -// The pointers need to be aligned to 8 bytes. This is because the -// maximum value type handled by wasm32 is u64. -const ALIGNMENT: u32 = 8; - -// The pointer returned by `allocate()` needs to fulfill the alignment -// requirement. In our case a pointer will always be a multiple of -// 8, as long as the first pointer is aligned to 8 bytes. -// This is because all pointers will contain a 8 byte prefix (the list -// index) and then a subsequent item of 2^x bytes, where x = [3..24]. -const N: usize = 22; -const MAX_POSSIBLE_ALLOCATION: u32 = 16777216; // 2^24 bytes -const MIN_POSSIBLE_ALLOCATION: u32 = 8; - -// Each pointer is prefixed with 8 bytes, which identify the list index -// to which it belongs. -const PREFIX_SIZE: u32 = 8; - -/// An implementation of freeing bump allocator. -/// -/// Refer to the module-level documentation for further details. -pub struct FreeingBumpHeapAllocator { - bumper: u32, - heads: [u32; N], - ptr_offset: u32, - total_size: u32, -} - -/// Create an allocator error. -fn error(msg: &'static str) -> Error { - Error::Allocator(msg) -} - -impl FreeingBumpHeapAllocator { - /// Creates a new allocation heap which follows a freeing-bump strategy. - /// The maximum size which can be allocated at once is 16 MiB. - /// - /// # Arguments - /// - /// - `heap_base` - the offset from the beginning of the linear memory where the heap starts. - pub fn new(heap_base: u32) -> Self { - // ptr_offset is the next alignment boundary on or after heap_base. - let ptr_offset = (heap_base + ALIGNMENT - 1) / ALIGNMENT * ALIGNMENT; - - FreeingBumpHeapAllocator { - bumper: 0, - heads: [0; N], - ptr_offset, - total_size: 0, - } - } - - /// Gets requested number of bytes to allocate and returns a pointer. - /// The maximum size which can be allocated at once is 16 MiB. - /// There is no minimum size, but whatever size is passed into - /// this function is rounded to the next power of two. If the requested - /// size is below 8 bytes it will be rounded up to 8 bytes. - /// - /// # Arguments - /// - /// - `mem` - a slice representing the linear memory on which this allocator operates. - /// - `size` - size in bytes of the allocation request - pub fn allocate(&mut self, mem: &mut [u8], size: WordSize) -> Result> { - let mem_size = u32::try_from(mem.len()) - .expect("size of Wasm linear memory is <2^32"); - let max_heap_size = mem_size - self.ptr_offset; - - if size > MAX_POSSIBLE_ALLOCATION { - return Err(Error::RequestedAllocationTooLarge); - } - - let size = size.max(MIN_POSSIBLE_ALLOCATION); - let item_size = size.next_power_of_two(); - if item_size + PREFIX_SIZE + self.total_size > max_heap_size { - return Err(Error::AllocatorOutOfSpace); - } - - let list_index = (item_size.trailing_zeros() - 3) as usize; - let ptr: u32 = if self.heads[list_index] != 0 { - // Something from the free list - let item = self.heads[list_index]; - let ptr = item + PREFIX_SIZE; - assert!( - ptr + item_size <= max_heap_size, - "Pointer is looked up in list of free entries, into which - only valid values are inserted; qed" - ); - - self.heads[list_index] = self.get_heap_u64(mem, item)? - .try_into() - .map_err(|_| error("read invalid free list pointer"))?; - ptr - } else { - // Nothing to be freed. Bump. - self.bump(item_size, max_heap_size)? + PREFIX_SIZE - }; - - self.set_heap_u64(mem, ptr - PREFIX_SIZE, list_index as u64)?; - - self.total_size = self.total_size + item_size + PREFIX_SIZE; - trace!(target: "wasm-heap", "Heap size is {} bytes after allocation", self.total_size); - - Ok(Pointer::new(self.ptr_offset + ptr)) - } - - /// Deallocates the space which was allocated for a pointer. - /// - /// # Arguments - /// - /// - `mem` - a slice representing the linear memory on which this allocator operates. - /// - `ptr` - pointer to the allocated chunk - pub fn deallocate(&mut self, mem: &mut [u8], ptr: Pointer) -> Result<()> { - let ptr = u32::from(ptr) - self.ptr_offset; - if ptr < PREFIX_SIZE { - return Err(error("Invalid pointer for deallocation")); - } - - let list_index: usize = self.get_heap_u64(mem, ptr - PREFIX_SIZE)? - .try_into() - .map_err(|_| error("read invalid list index"))?; - if list_index > self.heads.len() { - return Err(error("read invalid list index")); - } - self.set_heap_u64(mem, ptr - PREFIX_SIZE, self.heads[list_index] as u64)?; - self.heads[list_index] = ptr - PREFIX_SIZE; - - let item_size = Self::get_item_size_from_index(list_index); - self.total_size = self.total_size.checked_sub(item_size as u32 + PREFIX_SIZE) - .ok_or_else(|| error("Unable to subtract from total heap size without overflow"))?; - trace!(target: "wasm-heap", "Heap size is {} bytes after deallocation", self.total_size); - - Ok(()) - } - - /// Increases the `bumper` by `item_size + PREFIX_SIZE`. - /// - /// Returns the `bumper` from before the increase. - /// Returns an `Error::AllocatorOutOfSpace` if the operation - /// would exhaust the heap. - fn bump(&mut self, item_size: u32, max_heap_size: u32) -> Result { - if self.bumper + PREFIX_SIZE + item_size > max_heap_size { - return Err(Error::AllocatorOutOfSpace); - } - - let res = self.bumper; - self.bumper += item_size + PREFIX_SIZE; - Ok(res) - } - - fn get_item_size_from_index(index: usize) -> usize { - // we shift 1 by three places, since the first possible item size is 8 - 1 << 3 << index - } - - // Read a u64 from the heap in LE form. Used to read heap allocation prefixes. - fn get_heap_u64(&self, heap: &[u8], offset: u32) -> Result { - let range = self.heap_range(offset, 8, heap.len()) - .ok_or_else(|| error("read out of heap bounds"))?; - let bytes = heap[range].try_into() - .expect("[u8] slice of length 8 must be convertible to [u8; 8]"); - Ok(u64::from_le_bytes(bytes)) - } - - // Write a u64 to the heap in LE form. Used to write heap allocation prefixes. - fn set_heap_u64(&self, heap: &mut [u8], offset: u32, val: u64) -> Result<()> { - let range = self.heap_range(offset, 8, heap.len()) - .ok_or_else(|| error("write out of heap bounds"))?; - let bytes = val.to_le_bytes(); - &mut heap[range].copy_from_slice(&bytes[..]); - Ok(()) - } - - fn heap_range(&self, offset: u32, length: u32, heap_len: usize) -> Option> { - let start = offset - .checked_add(self.ptr_offset)? - as usize; - let end = offset - .checked_add(self.ptr_offset)? - .checked_add(length)? - as usize; - if end <= heap_len { - Some(start..end) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const PAGE_SIZE: u32 = 65536; - - /// Makes a pointer out of the given address. - fn to_pointer(address: u32) -> Pointer { - Pointer::new(address) - } - - #[test] - fn should_allocate_properly() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - - // when - let ptr = heap.allocate(&mut mem[..], 1).unwrap(); - - // then - // returned pointer must start right after `PREFIX_SIZE` - assert_eq!(ptr, to_pointer(PREFIX_SIZE)); - } - - #[test] - fn should_always_align_pointers_to_multiples_of_8() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(13); - - // when - let ptr = heap.allocate(&mut mem[..], 1).unwrap(); - - // then - // the pointer must start at the next multiple of 8 from 13 - // + the prefix of 8 bytes. - assert_eq!(ptr, to_pointer(24)); - } - - #[test] - fn should_increment_pointers_properly() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - - // when - let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); - let ptr2 = heap.allocate(&mut mem[..], 9).unwrap(); - let ptr3 = heap.allocate(&mut mem[..], 1).unwrap(); - - // then - // a prefix of 8 bytes is prepended to each pointer - assert_eq!(ptr1, to_pointer(PREFIX_SIZE)); - - // the prefix of 8 bytes + the content of ptr1 padded to the lowest possible - // item size of 8 bytes + the prefix of ptr1 - assert_eq!(ptr2, to_pointer(24)); - - // ptr2 + its content of 16 bytes + the prefix of 8 bytes - assert_eq!(ptr3, to_pointer(24 + 16 + PREFIX_SIZE)); - } - - #[test] - fn should_free_properly() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); - // the prefix of 8 bytes is prepended to the pointer - assert_eq!(ptr1, to_pointer(PREFIX_SIZE)); - - let ptr2 = heap.allocate(&mut mem[..], 1).unwrap(); - // the prefix of 8 bytes + the content of ptr 1 is prepended to the pointer - assert_eq!(ptr2, to_pointer(24)); - - // when - heap.deallocate(&mut mem[..], ptr2).unwrap(); - - // then - // then the heads table should contain a pointer to the - // prefix of ptr2 in the leftmost entry - assert_eq!(heap.heads[0], u32::from(ptr2) - PREFIX_SIZE); - } - - #[test] - fn should_deallocate_and_reallocate_properly() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let padded_offset = 16; - let mut heap = FreeingBumpHeapAllocator::new(13); - - let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); - // the prefix of 8 bytes is prepended to the pointer - assert_eq!(ptr1, to_pointer(padded_offset + PREFIX_SIZE)); - - let ptr2 = heap.allocate(&mut mem[..], 9).unwrap(); - // the padded_offset + the previously allocated ptr (8 bytes prefix + - // 8 bytes content) + the prefix of 8 bytes which is prepended to the - // current pointer - assert_eq!(ptr2, to_pointer(padded_offset + 16 + PREFIX_SIZE)); - - // when - heap.deallocate(&mut mem[..], ptr2).unwrap(); - let ptr3 = heap.allocate(&mut mem[..], 9).unwrap(); - - // then - // should have re-allocated - assert_eq!(ptr3, to_pointer(padded_offset + 16 + PREFIX_SIZE)); - assert_eq!(heap.heads, [0; N]); - } - - #[test] - fn should_build_linked_list_of_free_areas_properly() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - - let ptr1 = heap.allocate(&mut mem[..], 8).unwrap(); - let ptr2 = heap.allocate(&mut mem[..], 8).unwrap(); - let ptr3 = heap.allocate(&mut mem[..], 8).unwrap(); - - // when - heap.deallocate(&mut mem[..], ptr1).unwrap(); - heap.deallocate(&mut mem[..], ptr2).unwrap(); - heap.deallocate(&mut mem[..], ptr3).unwrap(); - - // then - assert_eq!(heap.heads[0], u32::from(ptr3) - PREFIX_SIZE); - - let ptr4 = heap.allocate(&mut mem[..], 8).unwrap(); - assert_eq!(ptr4, ptr3); - - assert_eq!(heap.heads[0], u32::from(ptr2) - PREFIX_SIZE); - } - - #[test] - fn should_not_allocate_if_too_large() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(13); - - // when - let ptr = heap.allocate(&mut mem[..], PAGE_SIZE - 13); - - // then - match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {}, - e => panic!("Expected allocator out of space error, got: {:?}", e), - } - } - - #[test] - fn should_not_allocate_if_full() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - let ptr1 = heap.allocate(&mut mem[..], (PAGE_SIZE / 2) - PREFIX_SIZE).unwrap(); - assert_eq!(ptr1, to_pointer(PREFIX_SIZE)); - - // when - let ptr2 = heap.allocate(&mut mem[..], PAGE_SIZE / 2); - - // then - // there is no room for another half page incl. its 8 byte prefix - match ptr2.unwrap_err() { - Error::AllocatorOutOfSpace => {}, - e => panic!("Expected allocator out of space error, got: {:?}", e), - } - } - - #[test] - fn should_allocate_max_possible_allocation_size() { - // given - let mut mem = vec![0u8; (MAX_POSSIBLE_ALLOCATION + PAGE_SIZE) as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - - // when - let ptr = heap.allocate(&mut mem[..], MAX_POSSIBLE_ALLOCATION).unwrap(); - - // then - assert_eq!(ptr, to_pointer(PREFIX_SIZE)); - } - - #[test] - fn should_not_allocate_if_requested_size_too_large() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(0); - - // when - let ptr = heap.allocate(&mut mem[..], MAX_POSSIBLE_ALLOCATION + 1); - - // then - match ptr.unwrap_err() { - Error::RequestedAllocationTooLarge => {}, - e => panic!("Expected allocation size too large error, got: {:?}", e), - } - } - - #[test] - fn should_return_error_when_bumper_greater_than_heap_size() { - // given - let mut mem = [0u8; 64]; - let mut heap = FreeingBumpHeapAllocator::new(0); - - let ptr1 = heap.allocate(&mut mem[..], 32).unwrap(); - assert_eq!(ptr1, to_pointer(PREFIX_SIZE)); - heap.deallocate(&mut mem[..], ptr1).expect("failed freeing ptr1"); - assert_eq!(heap.total_size, 0); - assert_eq!(heap.bumper, 40); - - let ptr2 = heap.allocate(&mut mem[..], 16).unwrap(); - assert_eq!(ptr2, to_pointer(48)); - heap.deallocate(&mut mem[..], ptr2).expect("failed freeing ptr2"); - assert_eq!(heap.total_size, 0); - assert_eq!(heap.bumper, 64); - - // when - // the `bumper` value is equal to `max_heap_size` here and any - // further allocation which would increment the bumper must fail. - // we try to allocate 8 bytes here, which will increment the - // bumper since no 8 byte item has been allocated+freed before. - let ptr = heap.allocate(&mut mem[..], 8); - - // then - match ptr.unwrap_err() { - Error::AllocatorOutOfSpace => {}, - e => panic!("Expected allocator out of space error, got: {:?}", e), - } - } - - #[test] - fn should_include_prefixes_in_total_heap_size() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(1); - - // when - // an item size of 16 must be used then - heap.allocate(&mut mem[..], 9).unwrap(); - - // then - assert_eq!(heap.total_size, PREFIX_SIZE + 16); - } - - #[test] - fn should_calculate_total_heap_size_to_zero() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(13); - - // when - let ptr = heap.allocate(&mut mem[..], 42).unwrap(); - assert_eq!(ptr, to_pointer(16 + PREFIX_SIZE)); - heap.deallocate(&mut mem[..], ptr).unwrap(); - - // then - assert_eq!(heap.total_size, 0); - } - - #[test] - fn should_calculate_total_size_of_zero() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let mut heap = FreeingBumpHeapAllocator::new(19); - - // when - for _ in 1..10 { - let ptr = heap.allocate(&mut mem[..], 42).unwrap(); - heap.deallocate(&mut mem[..], ptr).unwrap(); - } - - // then - assert_eq!(heap.total_size, 0); - } - - #[test] - fn should_read_and_write_u64_correctly() { - // given - let mut mem = [0u8; PAGE_SIZE as usize]; - let heap = FreeingBumpHeapAllocator::new(16); - - // when - heap.set_heap_u64(&mut mem[..], 40, 4480113).unwrap(); - - // then - let value = heap.get_heap_u64(&mut mem[..], 40).unwrap(); - assert_eq!(value, 4480113); - } - - #[test] - fn should_get_item_size_from_index() { - // given - let index = 0; - - // when - let item_size = FreeingBumpHeapAllocator::get_item_size_from_index(index); - - // then - assert_eq!(item_size, 8); - } - - #[test] - fn should_get_max_item_size_from_index() { - // given - let index = 21; - - // when - let item_size = FreeingBumpHeapAllocator::get_item_size_from_index(index); - - // then - assert_eq!(item_size as u32, MAX_POSSIBLE_ALLOCATION); - } - -} diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index f7de0af9685d78a80d36f4fc7152bb5279d7d0cd..b8d40d4d91f15c8556953555e4b0f365d89c949b 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -72,17 +72,10 @@ pub enum Error { #[display(fmt="The runtime has the `start` function")] RuntimeHasStartFn, /// Some other error occurred - #[from(ignore)] Other(String), /// Some error occurred in the allocator #[display(fmt="Error in allocator: {}", _0)] - Allocator(&'static str), - /// The allocator ran out of space. - #[display(fmt="Allocator ran out of space")] - AllocatorOutOfSpace, - /// Someone tried to allocate more memory than the allowed maximum per allocation. - #[display(fmt="Requested allocation size is too large")] - RequestedAllocationTooLarge, + Allocator(sp_allocator::Error), /// Execution of a host function failed. #[display(fmt="Host function {} execution failed with: {}", _0, _1)] FunctionExecution(String, String), @@ -101,9 +94,9 @@ impl std::error::Error for Error { impl wasmi::HostError for Error {} -impl From for Error { - fn from(err: String) -> Error { - Error::Other(err) +impl From<&'static str> for Error { + fn from(err: &'static str) -> Error { + Error::Other(err.into()) } } diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs index 69f6a710b316c2cb1839874ce4ab59af19f01477..cc515dcf9dab9a8f852f2d9b2dc1f3e2af40773f 100644 --- a/client/executor/common/src/lib.rs +++ b/client/executor/common/src/lib.rs @@ -19,6 +19,5 @@ #![warn(missing_docs)] pub mod sandbox; -pub mod allocator; pub mod error; pub mod wasm_runtime; diff --git a/client/executor/common/src/sandbox.rs b/client/executor/common/src/sandbox.rs index e48afd48917216d8359a3776f18c30fbe7f8fc0d..f920a47ca7655c2b3a71a8575f65136d9af272fa 100644 --- a/client/executor/common/src/sandbox.rs +++ b/client/executor/common/src/sandbox.rs @@ -27,7 +27,7 @@ use wasmi::{ Externals, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind, memory_units::Pages, }; -use sp_wasm_interface::{Pointer, WordSize}; +use sp_wasm_interface::{FunctionContext, Pointer, WordSize}; /// Index of a function inside the supervisor. /// @@ -144,48 +144,10 @@ impl ImportResolver for Imports { /// This trait encapsulates sandboxing capabilities. /// /// Note that this functions are only called in the `supervisor` context. -pub trait SandboxCapabilities { +pub trait SandboxCapabilities: FunctionContext { /// Represents a function reference into the supervisor environment. type SupervisorFuncRef; - /// Returns a reference to an associated sandbox `Store`. - fn store(&self) -> &Store; - - /// Returns a mutable reference to an associated sandbox `Store`. - fn store_mut(&mut self) -> &mut Store; - - /// Allocate space of the specified length in the supervisor memory. - /// - /// # Errors - /// - /// Returns `Err` if allocation not possible or errors during heap management. - /// - /// Returns pointer to the allocated block. - fn allocate(&mut self, len: WordSize) -> Result>; - - /// Deallocate space specified by the pointer that was previously returned by [`allocate`]. - /// - /// # Errors - /// - /// Returns `Err` if deallocation not possible or because of errors in heap management. - /// - /// [`allocate`]: #tymethod.allocate - fn deallocate(&mut self, ptr: Pointer) -> Result<()>; - - /// Write `data` into the supervisor memory at offset specified by `ptr`. - /// - /// # Errors - /// - /// Returns `Err` if `ptr + data.len()` is out of bounds. - fn write_memory(&mut self, ptr: Pointer, data: &[u8]) -> Result<()>; - - /// Read `len` bytes from the supervisor memory. - /// - /// # Errors - /// - /// Returns `Err` if `ptr + len` is out of bounds. - fn read_memory(&self, ptr: Pointer, len: WordSize) -> Result>; - /// Invoke a function in the supervisor environment. /// /// This first invokes the dispatch_thunk function, passing in the function index of the @@ -224,7 +186,8 @@ fn trap(msg: &'static str) -> Trap { } fn deserialize_result(serialized_result: &[u8]) -> std::result::Result, Trap> { - use self::sandbox_primitives::{HostError, ReturnValue}; + use self::sandbox_primitives::HostError; + use sp_wasm_interface::ReturnValue; let result_val = std::result::Result::::decode(&mut &serialized_result[..]) .map_err(|_| trap("Decoding Result failed!"))?; @@ -260,7 +223,7 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> { let invoke_args_data: Vec = args.as_ref() .iter() .cloned() - .map(sandbox_primitives::TypedValue::from) + .map(sp_wasm_interface::Value::from) .collect::>() .encode(); @@ -269,8 +232,14 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> { // Move serialized arguments inside the memory and invoke dispatch thunk and // then free allocated memory. let invoke_args_len = invoke_args_data.len() as WordSize; - let invoke_args_ptr = self.supervisor_externals.allocate(invoke_args_len)?; - self.supervisor_externals.write_memory(invoke_args_ptr, &invoke_args_data)?; + let invoke_args_ptr = self + .supervisor_externals + .allocate_memory(invoke_args_len) + .map_err(|_| trap("Can't allocate memory in supervisor for the arguments"))?; + self + .supervisor_externals + .write_memory(invoke_args_ptr, &invoke_args_data) + .map_err(|_| trap("Can't write invoke args into memory"))?; let result = self.supervisor_externals.invoke( &self.sandbox_instance.dispatch_thunk, invoke_args_ptr, @@ -278,7 +247,10 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> { state, func_idx, )?; - self.supervisor_externals.deallocate(invoke_args_ptr)?; + self + .supervisor_externals + .deallocate_memory(invoke_args_ptr) + .map_err(|_| trap("Can't deallocate memory for dispatch thunk's invoke arguments"))?; // dispatch_thunk returns pointer to serialized arguments. // Unpack pointer and len of the serialized result data. @@ -291,9 +263,11 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> { }; let serialized_result_val = self.supervisor_externals - .read_memory(serialized_result_val_ptr, serialized_result_val_len)?; + .read_memory(serialized_result_val_ptr, serialized_result_val_len) + .map_err(|_| trap("Can't read the serialized result from dispatch thunk"))?; self.supervisor_externals - .deallocate(serialized_result_val_ptr)?; + .deallocate_memory(serialized_result_val_ptr) + .map_err(|_| trap("Can't deallocate memory for dispatch thunk's result"))?; deserialize_result(&serialized_result_val) } @@ -362,6 +336,18 @@ impl SandboxInstance { }, ) } + + /// Get the value from a global with the given `name`. + /// + /// Returns `Some(_)` if the global could be found. + pub fn get_global_val(&self, name: &str) -> Option { + let global = self.instance + .export_by_name(name)? + .as_global()? + .get(); + + Some(global.into()) + } } /// Error occurred during instantiation of a sandboxed module. @@ -420,6 +406,46 @@ fn decode_environment_definition( )) } +/// An environment in which the guest module is instantiated. +pub struct GuestEnvironment { + imports: Imports, + guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping, +} + +impl GuestEnvironment { + /// Decodes an environment definition from the given raw bytes. + /// + /// Returns `Err` if the definition cannot be decoded. + pub fn decode( + store: &Store, + raw_env_def: &[u8], + ) -> std::result::Result { + let (imports, guest_to_supervisor_mapping) = + decode_environment_definition(raw_env_def, &store.memories)?; + Ok(Self { + imports, + guest_to_supervisor_mapping, + }) + } +} + +/// An unregistered sandboxed instance. +/// +/// To finish off the instantiation the user must call `register`. +#[must_use] +pub struct UnregisteredInstance { + sandbox_instance: Rc>, +} + +impl UnregisteredInstance { + /// Finalizes instantiation of this module. + pub fn register(self, store: &mut Store) -> u32 { + // At last, register the instance. + let instance_idx = store.register_sandbox_instance(self.sandbox_instance); + instance_idx + } +} + /// Instantiate a guest module and return it's index in the store. /// /// The guest module's code is specified in `wasm`. Environment that will be available to @@ -434,18 +460,16 @@ fn decode_environment_definition( /// - Module in `wasm` is invalid or couldn't be instantiated. /// /// [`EnvironmentDefinition`]: ../sandbox/struct.EnvironmentDefinition.html -pub fn instantiate( +pub fn instantiate<'a, FE: SandboxCapabilities>( supervisor_externals: &mut FE, dispatch_thunk: FE::SupervisorFuncRef, wasm: &[u8], - raw_env_def: &[u8], + host_env: GuestEnvironment, state: u32, -) -> std::result::Result { - let (imports, guest_to_supervisor_mapping) = - decode_environment_definition(raw_env_def, &supervisor_externals.store().memories)?; - +) -> std::result::Result, InstantiationError> { let module = Module::from_buffer(wasm).map_err(|_| InstantiationError::ModuleDecoding)?; - let instance = ModuleInstance::new(&module, &imports).map_err(|_| InstantiationError::Instantiation)?; + let instance = ModuleInstance::new(&module, &host_env.imports) + .map_err(|_| InstantiationError::Instantiation)?; let sandbox_instance = Rc::new(SandboxInstance { // In general, it's not a very good idea to use `.not_started_instance()` for anything @@ -453,7 +477,7 @@ pub fn instantiate( // for the purpose of running `start` function which should be ok. instance: instance.not_started_instance().clone(), dispatch_thunk, - guest_to_supervisor_mapping, + guest_to_supervisor_mapping: host_env.guest_to_supervisor_mapping, }); with_guest_externals( @@ -467,11 +491,7 @@ pub fn instantiate( }, )?; - // At last, register the instance. - let instance_idx = supervisor_externals - .store_mut() - .register_sandbox_instance(sandbox_instance); - Ok(instance_idx) + Ok(UnregisteredInstance { sandbox_instance }) } /// This struct keeps track of all sandboxed components. diff --git a/client/executor/common/src/wasm_runtime.rs b/client/executor/common/src/wasm_runtime.rs index 0733350f4cd6a45a1a788502bf5a21b904464ed3..7af6c2bd53c0ec1f9de45356d459a100e2cf50e8 100644 --- a/client/executor/common/src/wasm_runtime.rs +++ b/client/executor/common/src/wasm_runtime.rs @@ -17,21 +17,18 @@ //! Definitions for a wasm runtime. use crate::error::Error; -use sp_wasm_interface::Function; +use sp_wasm_interface::{Function, Value}; /// A trait that defines an abstract wasm runtime. /// /// This can be implemented by an execution engine. pub trait WasmRuntime { - /// Attempt to update the number of heap pages available during execution. - /// - /// Returns false if the update cannot be applied. The function is guaranteed to return true if - /// the heap pages would not change from its current value. - fn update_heap_pages(&mut self, heap_pages: u64) -> bool; - /// Return the host functions that are registered for this Wasm runtime. fn host_functions(&self) -> &[&'static dyn Function]; /// Call a method in the Substrate runtime by name. Returns the encoded result on success. fn call(&mut self, method: &str, data: &[u8]) -> Result, Error>; + + /// Get the value from a global with the given `name`. + fn get_global_val(&self, name: &str) -> Result, Error>; } diff --git a/client/executor/runtime-test/Cargo.toml b/client/executor/runtime-test/Cargo.toml index 835021fec2a1151eec152af740ac186513fb5ff3..3cb0c03a87c8eea3832ef3993fc6711d58deee86 100644 --- a/client/executor/runtime-test/Cargo.toml +++ b/client/executor/runtime-test/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" +license = "GPL-3.0" [dependencies] sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } @@ -11,10 +12,16 @@ sp-io = { version = "2.0.0", default-features = false, path = "../../../primitiv sp-sandbox = { version = "0.8.0", default-features = false, path = "../../../primitives/sandbox" } sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-allocator = { version = "2.0.0", default-features = false, path = "../../../primitives/allocator" } [build-dependencies] wasm-builder-runner = { version = "1.0.4", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } [features] default = [ "std" ] -std = ["sp-io/std", "sp-sandbox/std", "sp-std/std"] +std = [ + "sp-io/std", + "sp-sandbox/std", + "sp-std/std", + "sp-allocator/std", +] diff --git a/client/executor/runtime-test/build.rs b/client/executor/runtime-test/build.rs index 9c81ea6f38b8df4812805e75501414c862e50d11..647b4768141d2203155bf5325f9ff87f7d9c2446 100644 --- a/client/executor/runtime-test/build.rs +++ b/client/executor/runtime-test/build.rs @@ -14,17 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; +use wasm_builder_runner::WasmBuilder; fn main() { - build_current_project_with_rustflags( - "wasm_binary.rs", - WasmBuilderSource::CratesOrPath { - path: "../../../utils/wasm-builder", - version: "1.0.9", - }, - // This instructs LLD to export __heap_base as a global variable, which is used by the - // external memory allocator. - "-Clink-arg=--export=__heap_base", - ); + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_from_crates_or_path("1.0.9", "../../../utils/wasm-builder") + .export_heap_base() + .import_memory() + .build() } diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index a8d329cdd9607a05a0d00cc3da2685970a0115f5..38a16ae39ea012806d12d90f1a920e622eec4ad0 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -17,6 +17,8 @@ use sp_io::{ use sp_runtime::{print, traits::{BlakeTwo256, Hash}}; #[cfg(not(feature = "std"))] use sp_core::{ed25519, sr25519}; +#[cfg(not(feature = "std"))] +use sp_sandbox::Value; extern "C" { #[allow(dead_code)] @@ -26,6 +28,11 @@ extern "C" { fn yet_another_missing_external(); } +#[cfg(not(feature = "std"))] +/// Mutable static variables should be always observed to have +/// the initialized value at the start of a runtime call. +static mut MUTABLE_STATIC: u64 = 32; + sp_core::wasm_export_functions! { fn test_calling_missing_external() { unsafe { missing_external() } @@ -128,8 +135,8 @@ sp_core::wasm_export_functions! { execute_sandboxed( &code, &[ - sp_sandbox::TypedValue::I32(0x12345678), - sp_sandbox::TypedValue::I64(0x1234567887654321), + Value::I32(0x12345678), + Value::I64(0x1234567887654321), ], ).is_ok() } @@ -138,10 +145,10 @@ sp_core::wasm_export_functions! { let ok = match execute_sandboxed( &code, &[ - sp_sandbox::TypedValue::I32(0x1336), + Value::I32(0x1336), ] ) { - Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(0x1337))) => true, + Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, _ => false, }; @@ -160,6 +167,22 @@ sp_core::wasm_export_functions! { code } + + fn test_sandbox_get_global_val(code: Vec) -> i64 { + let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new(); + let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) { + i + } else { + return 20; + }; + + match instance.get_global_val("test_global") { + Some(sp_sandbox::Value::I64(val)) => val, + None => 30, + val => 40, + } + } + fn test_offchain_local_storage() -> bool { let kind = sp_core::offchain::StorageKind::PERSISTENT; assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None); @@ -212,12 +235,66 @@ sp_core::wasm_export_functions! { run().is_some() } + + // Just some test to make sure that `sp-allocator` compiles on `no_std`. + fn test_sp_allocator_compiles() { + sp_allocator::FreeingBumpHeapAllocator::new(0); + } + + fn returns_mutable_static() -> u64 { + unsafe { + MUTABLE_STATIC += 1; + MUTABLE_STATIC + } + } + + fn allocates_huge_stack_array(trap: bool) -> Vec { + // Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB). + // This will just decrease (stacks in wasm32-u-u grow downwards) the stack + // pointer. This won't trap on the current compilers. + let mut data = [0u8; 1024 * 768]; + + // Then make sure we actually write something to it. + // + // If: + // 1. the stack area is placed at the beginning of the linear memory space, and + // 2. the stack pointer points to out-of-bounds area, and + // 3. a write is performed around the current stack pointer. + // + // then a trap should happen. + // + for (i, v) in data.iter_mut().enumerate() { + *v = i as u8; // deliberate truncation + } + + if trap { + // There is a small chance of this to be pulled up in theory. In practice + // the probability of that is rather low. + panic!() + } + + data.to_vec() + } + + // Check that the heap at `heap_base + offset` don't contains the test message. + // After the check succeeds the test message is written into the heap. + // + // It is expected that the given pointer is not allocated. + fn check_and_set_in_heap(heap_base: u32, offset: u32) { + let test_message = b"Hello invalid heap memory"; + let ptr = unsafe { (heap_base + offset) as *mut u8 }; + + let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) }; + + assert_ne!(test_message, message_slice); + message_slice.copy_from_slice(test_message); + } } #[cfg(not(feature = "std"))] fn execute_sandboxed( code: &[u8], - args: &[sp_sandbox::TypedValue], + args: &[Value], ) -> Result { struct State { counter: u32, @@ -225,7 +302,7 @@ fn execute_sandboxed( fn env_assert( _e: &mut State, - args: &[sp_sandbox::TypedValue], + args: &[Value], ) -> Result { if args.len() != 1 { return Err(sp_sandbox::HostError); @@ -239,14 +316,14 @@ fn execute_sandboxed( } fn env_inc_counter( e: &mut State, - args: &[sp_sandbox::TypedValue], + args: &[Value], ) -> Result { if args.len() != 1 { return Err(sp_sandbox::HostError); } let inc_by = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?; e.counter += inc_by as u32; - Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(e.counter as i32))) + Ok(sp_sandbox::ReturnValue::Value(Value::I32(e.counter as i32))) } let mut state = State { counter: 0 }; diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 00f4eb33b02bf99bb11c706f161d773e10c9f102..c0516d3ac7dfaa8b3447faf234834791efb56842 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -27,52 +27,11 @@ use sc_runtime_test::WASM_BINARY; use sp_state_machine::TestExternalities as CoreTestExternalities; use test_case::test_case; use sp_trie::{TrieConfiguration, trie_types::Layout}; +use sp_wasm_interface::HostFunctions as _; use crate::WasmExecutionMethod; pub type TestExternalities = CoreTestExternalities; - -#[cfg(feature = "wasmtime")] -mod wasmtime_missing_externals { - use sp_wasm_interface::{Function, FunctionContext, HostFunctions, Result, Signature, Value}; - - pub struct WasmtimeHostFunctions; - - impl HostFunctions for WasmtimeHostFunctions { - fn host_functions() -> Vec<&'static dyn Function> { - vec![MISSING_EXTERNAL_FUNCTION, YET_ANOTHER_MISSING_EXTERNAL_FUNCTION] - } - } - - struct MissingExternalFunction(&'static str); - - impl Function for MissingExternalFunction { - fn name(&self) -> &str { self.0 } - - fn signature(&self) -> Signature { - Signature::new(vec![], None) - } - - fn execute( - &self, - _context: &mut dyn FunctionContext, - _args: &mut dyn Iterator, - ) -> Result> { - panic!("should not be called"); - } - } - - static MISSING_EXTERNAL_FUNCTION: &'static MissingExternalFunction = - &MissingExternalFunction("missing_external"); - static YET_ANOTHER_MISSING_EXTERNAL_FUNCTION: &'static MissingExternalFunction = - &MissingExternalFunction("yet_another_missing_external"); -} - -#[cfg(feature = "wasmtime")] -type HostFunctions = - (wasmtime_missing_externals::WasmtimeHostFunctions, sp_io::SubstrateHostFunctions); - -#[cfg(not(feature = "wasmtime"))] type HostFunctions = sp_io::SubstrateHostFunctions; fn call_in_wasm( @@ -80,16 +39,14 @@ fn call_in_wasm( call_data: &[u8], execution_method: WasmExecutionMethod, ext: &mut E, - code: &[u8], - heap_pages: u64, ) -> crate::error::Result> { crate::call_in_wasm::( function, call_data, execution_method, ext, - code, - heap_pages, + &WASM_BINARY[..], + 1024, true, ) } @@ -99,55 +56,72 @@ fn call_in_wasm( fn returning_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let output = call_in_wasm( "test_empty_return", &[], wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(); assert_eq!(output, vec![0u8; 0]); } #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] -#[should_panic(expected = "Function `missing_external` is only a stub. Calling a stub is not allowed.")] -#[cfg(not(feature = "wasmtime"))] fn call_not_existing_function(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; - call_in_wasm( + match call_in_wasm( "test_calling_missing_external", &[], wasm_method, &mut ext, - &test_code[..], - 8, - ).unwrap(); + ) { + Ok(_) => panic!("was expected an `Err`"), + Err(e) => { + match wasm_method { + WasmExecutionMethod::Interpreted => assert_eq!( + &format!("{:?}", e), + "Wasmi(Trap(Trap { kind: Host(Other(\"Function `missing_external` is only a stub. Calling a stub is not allowed.\")) }))" + ), + #[cfg(feature = "wasmtime")] + WasmExecutionMethod::Compiled => assert_eq!( + &format!("{:?}", e), + "Other(\"Wasm execution trapped: call to a missing function env:missing_external\")" + ), + } + } + } } #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] -#[should_panic(expected = "Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.")] -#[cfg(not(feature = "wasmtime"))] fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; - call_in_wasm( + match call_in_wasm( "test_calling_yet_another_missing_external", &[], wasm_method, &mut ext, - &test_code[..], - 8, - ).unwrap(); + ) { + Ok(_) => panic!("was expected an `Err`"), + Err(e) => { + match wasm_method { + WasmExecutionMethod::Interpreted => assert_eq!( + &format!("{:?}", e), + "Wasmi(Trap(Trap { kind: Host(Other(\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\")) }))" + ), + #[cfg(feature = "wasmtime")] + WasmExecutionMethod::Compiled => assert_eq!( + &format!("{:?}", e), + "Other(\"Wasm execution trapped: call to a missing function env:yet_another_missing_external\")" + ), + } + } + } } #[test_case(WasmExecutionMethod::Interpreted)] @@ -155,15 +129,12 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) { fn panicking_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let output = call_in_wasm( "test_panic", &[], wasm_method, &mut ext, - &test_code[..], - 8, ); assert!(output.is_err()); @@ -172,8 +143,6 @@ fn panicking_should_work(wasm_method: WasmExecutionMethod) { &[0], wasm_method, &mut ext, - &test_code[..], - 8, ); assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::::new())); @@ -182,8 +151,6 @@ fn panicking_should_work(wasm_method: WasmExecutionMethod) { &vec![2].encode(), wasm_method, &mut ext, - &test_code[..], - 8, ); assert!(output.is_err()); } @@ -196,15 +163,12 @@ fn storage_should_work(wasm_method: WasmExecutionMethod) { { let mut ext = ext.ext(); ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); - let test_code = WASM_BINARY; let output = call_in_wasm( "test_data_in", &b"Hello world".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(); assert_eq!(output, b"all ok!".to_vec().encode()); @@ -232,7 +196,6 @@ fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { ext.set_storage(b"aba".to_vec(), b"3".to_vec()); ext.set_storage(b"abb".to_vec(), b"4".to_vec()); ext.set_storage(b"bbb".to_vec(), b"5".to_vec()); - let test_code = WASM_BINARY; // This will clear all entries which prefix is "ab". let output = call_in_wasm( @@ -240,8 +203,6 @@ fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { &b"ab".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(); assert_eq!(output, b"all ok!".to_vec().encode()); @@ -263,15 +224,12 @@ fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; assert_eq!( call_in_wasm( "test_blake2_256", &[0], wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), blake2_256(&b""[..]).to_vec().encode(), ); @@ -281,8 +239,6 @@ fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { &b"Hello world!".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), blake2_256(&b"Hello world!"[..]).to_vec().encode(), ); @@ -293,15 +249,12 @@ fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; assert_eq!( call_in_wasm( "test_blake2_128", &[0], wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), blake2_128(&b""[..]).to_vec().encode(), ); @@ -311,8 +264,6 @@ fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { &b"Hello world!".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), blake2_128(&b"Hello world!"[..]).to_vec().encode(), ); @@ -323,15 +274,12 @@ fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { fn sha2_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; assert_eq!( call_in_wasm( "test_sha2_256", &[0], wasm_method, &mut ext, - &test_code[..], - 8, ) .unwrap(), hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") @@ -344,8 +292,6 @@ fn sha2_256_should_work(wasm_method: WasmExecutionMethod) { &b"Hello world!".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ) .unwrap(), hex!("c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a") @@ -359,15 +305,12 @@ fn sha2_256_should_work(wasm_method: WasmExecutionMethod) { fn twox_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; assert_eq!( call_in_wasm( "test_twox_256", &[0], wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), hex!( "99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a" @@ -379,8 +322,6 @@ fn twox_256_should_work(wasm_method: WasmExecutionMethod) { &b"Hello world!".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), hex!( "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" @@ -393,15 +334,12 @@ fn twox_256_should_work(wasm_method: WasmExecutionMethod) { fn twox_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; assert_eq!( call_in_wasm( "test_twox_128", &[0], wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(), ); @@ -411,8 +349,6 @@ fn twox_128_should_work(wasm_method: WasmExecutionMethod) { &b"Hello world!".to_vec().encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(), ); @@ -423,7 +359,6 @@ fn twox_128_should_work(wasm_method: WasmExecutionMethod) { fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let key = ed25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; @@ -436,8 +371,6 @@ fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { &calldata.encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), true.encode(), ); @@ -453,8 +386,6 @@ fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { &calldata.encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), false.encode(), ); @@ -465,7 +396,6 @@ fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) { fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let key = sr25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; @@ -478,8 +408,6 @@ fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { &calldata.encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), true.encode(), ); @@ -495,8 +423,6 @@ fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { &calldata.encode(), wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), false.encode(), ); @@ -506,17 +432,13 @@ fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) { #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; - let test_code = WASM_BINARY; assert_eq!( call_in_wasm( "test_ordered_trie_root", &[0], wasm_method, - &mut ext, - &test_code[..], - 8, + &mut ext.ext(), ).unwrap(), Layout::::ordered_trie_root(trie_input.iter()).as_bytes().encode(), ); @@ -530,16 +452,12 @@ fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let (offchain, state) = testing::TestOffchainExt::new(); ext.register_extension(OffchainExt::new(offchain)); - let test_code = WASM_BINARY; - let mut ext = ext.ext(); assert_eq!( call_in_wasm( "test_offchain_local_storage", &[0], wasm_method, - &mut ext, - &test_code[..], - 8, + &mut ext.ext(), ).unwrap(), true.encode(), ); @@ -566,17 +484,107 @@ fn offchain_http_should_work(wasm_method: WasmExecutionMethod) { }, ); - let test_code = WASM_BINARY; - let mut ext = ext.ext(); assert_eq!( call_in_wasm( "test_offchain_http", &[0], wasm_method, - &mut ext, - &test_code[..], - 8, + &mut ext.ext(), ).unwrap(), true.encode(), ); } + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +#[should_panic(expected = "Allocator ran out of space")] +fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + + crate::call_in_wasm::( + "test_exhaust_heap", + &[0], + wasm_method, + &mut ext.ext(), + &WASM_BINARY[..], + // `17` is the initial number of pages compiled into the binary. + 17, + true, + ).unwrap(); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn returns_mutable_static(wasm_method: WasmExecutionMethod) { + let mut instance = crate::wasm_runtime::create_wasm_runtime_with_code( + wasm_method, + 1024, + &WASM_BINARY[..], + HostFunctions::host_functions(), + true, + ).expect("Creates instance"); + + let res = instance.call("returns_mutable_static", &[0]).unwrap(); + assert_eq!(33, u64::decode(&mut &res[..]).unwrap()); + + // We expect that every invocation will need to return the initial + // value plus one. If the value increases more than that then it is + // a sign that the wasm runtime preserves the memory content. + let res = instance.call("returns_mutable_static", &[0]).unwrap(); + assert_eq!(33, u64::decode(&mut &res[..]).unwrap()); +} + +// If we didn't restore the wasm instance properly, on a trap the stack pointer would not be +// returned to its initial value and thus the stack space is going to be leaked. +// +// See https://github.com/paritytech/substrate/issues/2967 for details +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn restoration_of_globals(wasm_method: WasmExecutionMethod) { + // Allocate 32 pages (of 65536 bytes) which gives the runtime 2048KB of heap to operate on + // (plus some additional space unused from the initial pages requested by the wasm runtime + // module). + // + // The fixture performs 2 allocations of 768KB and this theoretically gives 1536KB, however, due + // to our allocator algorithm there are inefficiencies. + const REQUIRED_MEMORY_PAGES: u64 = 32; + + let mut instance = crate::wasm_runtime::create_wasm_runtime_with_code( + wasm_method, + REQUIRED_MEMORY_PAGES, + &WASM_BINARY[..], + HostFunctions::host_functions(), + true, + ).expect("Creates instance"); + + // On the first invocation we allocate approx. 768KB (75%) of stack and then trap. + let res = instance.call("allocates_huge_stack_array", &true.encode()); + assert!(res.is_err()); + + // On the second invocation we allocate yet another 768KB (75%) of stack + let res = instance.call("allocates_huge_stack_array", &false.encode()); + assert!(res.is_ok()); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) { + let mut instance = crate::wasm_runtime::create_wasm_runtime_with_code( + wasm_method, + 1024, + &WASM_BINARY[..], + HostFunctions::host_functions(), + true, + ).expect("Creates instance"); + + let heap_base = instance.get_global_val("__heap_base") + .expect("`__heap_base` is valid") + .expect("`__heap_base` exists") + .as_i32() + .expect("`__heap_base` is an `i32`"); + + let params = (heap_base as u32, 512u32 * 64 * 1024).encode(); + instance.call("check_and_set_in_heap", ¶ms).unwrap(); + + // Cal it a second time to check that the heap was freed. + instance.call("check_and_set_in_heap", ¶ms).unwrap(); +} diff --git a/client/executor/src/integration_tests/sandbox.rs b/client/executor/src/integration_tests/sandbox.rs index 9a9b33608d7021b78993d18c375025cabea4314c..8e8b7896cf90b5630486fa780af0a3cc94d9b8dd 100644 --- a/client/executor/src/integration_tests/sandbox.rs +++ b/client/executor/src/integration_tests/sandbox.rs @@ -18,7 +18,6 @@ use super::{TestExternalities, call_in_wasm}; use crate::WasmExecutionMethod; use codec::Encode; -use sc_runtime_test::WASM_BINARY; use test_case::test_case; use wabt; @@ -27,7 +26,6 @@ use wabt; fn sandbox_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -56,8 +54,6 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), true.encode(), ); @@ -68,7 +64,6 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) { fn sandbox_trap(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -86,47 +81,16 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), vec![0], ); } -#[test_case(WasmExecutionMethod::Interpreted)] -#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] -#[should_panic(expected = "Allocator ran out of space")] -fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (func (export "call") - i32.const 0 - call $assert - ) - ) - "#).unwrap().encode(); - - call_in_wasm( - "test_exhaust_heap", - &code, - wasm_method, - &mut ext, - &test_code[..], - 8, - ).unwrap(); -} - #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn start_called(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -161,8 +125,6 @@ fn start_called(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), true.encode(), ); @@ -173,7 +135,6 @@ fn start_called(wasm_method: WasmExecutionMethod) { fn invoke_args(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -204,8 +165,6 @@ fn invoke_args(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), true.encode(), ); @@ -216,7 +175,6 @@ fn invoke_args(wasm_method: WasmExecutionMethod) { fn return_val(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -235,8 +193,6 @@ fn return_val(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), true.encode(), ); @@ -247,7 +203,6 @@ fn return_val(wasm_method: WasmExecutionMethod) { fn unlinkable_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -264,8 +219,6 @@ fn unlinkable_module(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), 1u8.encode(), ); @@ -276,7 +229,6 @@ fn unlinkable_module(wasm_method: WasmExecutionMethod) { fn corrupted_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; // Corrupted wasm file let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); @@ -287,8 +239,6 @@ fn corrupted_module(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), 1u8.encode(), ); @@ -299,7 +249,6 @@ fn corrupted_module(wasm_method: WasmExecutionMethod) { fn start_fn_ok(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -319,8 +268,6 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), 0u8.encode(), ); @@ -331,7 +278,6 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) { fn start_fn_traps(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let test_code = WASM_BINARY; let code = wabt::wat2wasm(r#" (module @@ -352,9 +298,30 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) { &code, wasm_method, &mut ext, - &test_code[..], - 8, ).unwrap(), 2u8.encode(), ); } + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn get_global_val_works(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + let code = wabt::wat2wasm(r#" + (module + (global (export "test_global") i64 (i64.const 500)) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_get_global_val", + &code, + wasm_method, + &mut ext, + ).unwrap(), + 500i64.encode(), + ); +} diff --git a/client/executor/src/lib.rs b/client/executor/src/lib.rs index 4f97efa9b661e408d7b5620d4c9667e45d7eaed0..152e3a498485dfb310efcc25abd8a0b2eb163e02 100644 --- a/client/executor/src/lib.rs +++ b/client/executor/src/lib.rs @@ -48,7 +48,7 @@ pub use sp_core::traits::Externalities; pub use sp_wasm_interface; pub use wasm_runtime::WasmExecutionMethod; -pub use sc_executor_common::{error, allocator, sandbox}; +pub use sc_executor_common::{error, sandbox}; /// Call the given `function` in the given wasm `code`. /// @@ -68,7 +68,7 @@ pub fn call_in_wasm( ext: &mut dyn Externalities, code: &[u8], heap_pages: u64, - allow_missing_imports: bool, + allow_missing_func_imports: bool, ) -> error::Result> { call_in_wasm_with_host_functions( function, @@ -78,7 +78,7 @@ pub fn call_in_wasm( code, heap_pages, HF::host_functions(), - allow_missing_imports, + allow_missing_func_imports, ) } @@ -92,14 +92,14 @@ pub fn call_in_wasm_with_host_functions( code: &[u8], heap_pages: u64, host_functions: Vec<&'static dyn sp_wasm_interface::Function>, - allow_missing_imports: bool, + allow_missing_func_imports: bool, ) -> error::Result> { let instance = wasm_runtime::create_wasm_runtime_with_code( execution_method, heap_pages, code, host_functions, - allow_missing_imports, + allow_missing_func_imports, )?; // It is safe, as we delete the instance afterwards. diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 036b28f764003cd60a6c86921bc10a205d8f3282..9d54246ee07638562fd7ae95ab2cbd0e9efb808a 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -42,6 +42,8 @@ pub enum WasmExecutionMethod { /// A Wasm runtime object along with its cached runtime version. struct VersionedRuntime { runtime: Box, + /// The number of WebAssembly heap pages this instance was created with. + heap_pages: u64, /// Runtime version according to `Core_version`. version: RuntimeVersion, } @@ -122,7 +124,7 @@ impl RuntimesCache { Entry::Occupied(o) => { let result = o.into_mut(); if let Ok(ref mut cached_runtime) = result { - let heap_pages_changed = !cached_runtime.runtime.update_heap_pages(heap_pages); + let heap_pages_changed = cached_runtime.heap_pages != heap_pages; let host_functions_changed = cached_runtime.runtime.host_functions() != host_functions; if heap_pages_changed || host_functions_changed { @@ -191,15 +193,15 @@ pub fn create_wasm_runtime_with_code( heap_pages: u64, code: &[u8], host_functions: Vec<&'static dyn Function>, - allow_missing_imports: bool, + allow_missing_func_imports: bool, ) -> Result, WasmError> { match wasm_method { WasmExecutionMethod::Interpreted => - sc_executor_wasmi::create_instance(code, heap_pages, host_functions, allow_missing_imports) + sc_executor_wasmi::create_instance(code, heap_pages, host_functions, allow_missing_func_imports) .map(|runtime| -> Box { Box::new(runtime) }), #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => - sc_executor_wasmtime::create_instance(code, heap_pages, host_functions) + sc_executor_wasmtime::create_instance(code, heap_pages, host_functions, allow_missing_func_imports) .map(|runtime| -> Box { Box::new(runtime) }), } } @@ -236,6 +238,7 @@ fn create_versioned_wasm_runtime( Ok(VersionedRuntime { runtime, version, + heap_pages, }) } diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml index dbfdc505c647a9db8820c24888209ec9cf6d0f20..9e968fdc685f0896abb3b7593fc425e2e5af6c17 100644 --- a/client/executor/wasmi/Cargo.toml +++ b/client/executor/wasmi/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-executor-wasmi" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4.8" @@ -13,3 +14,4 @@ sc-executor-common = { version = "0.8", path = "../common" } sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index c10698fea46adc84ba91d5fa92a5b47cfa9b3a47..b90c0f05f5d9e53b1abfd29e416fd575c50e6880 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -16,11 +16,7 @@ //! This crate provides an implementation of `WasmRuntime` that is baked by wasmi. -use sc_executor_common::{ - error::{Error, WasmError}, - sandbox, - allocator, -}; +use sc_executor_common::{error::{Error, WasmError}, sandbox}; use std::{str, mem, cell::RefCell}; use wasmi::{ Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef, @@ -28,7 +24,7 @@ use wasmi::{ }; use codec::{Encode, Decode}; use sp_core::sandbox as sandbox_primitives; -use log::{error, trace}; +use log::{error, trace, debug}; use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; use sp_wasm_interface::{ FunctionContext, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, Function, @@ -38,11 +34,11 @@ use sc_executor_common::wasm_runtime::WasmRuntime; struct FunctionExecutor<'a> { sandbox_store: sandbox::Store, - heap: allocator::FreeingBumpHeapAllocator, + heap: sp_allocator::FreeingBumpHeapAllocator, memory: MemoryRef, table: Option, host_functions: &'a [&'static dyn Function], - allow_missing_imports: bool, + allow_missing_func_imports: bool, missing_functions: &'a [String], } @@ -52,16 +48,16 @@ impl<'a> FunctionExecutor<'a> { heap_base: u32, t: Option, host_functions: &'a [&'static dyn Function], - allow_missing_imports: bool, + allow_missing_func_imports: bool, missing_functions: &'a [String], ) -> Result { Ok(FunctionExecutor { sandbox_store: sandbox::Store::new(), - heap: allocator::FreeingBumpHeapAllocator::new(heap_base), + heap: sp_allocator::FreeingBumpHeapAllocator::new(heap_base), memory: m, table: t, host_functions, - allow_missing_imports, + allow_missing_func_imports, missing_functions, }) } @@ -70,31 +66,6 @@ impl<'a> FunctionExecutor<'a> { impl<'a> sandbox::SandboxCapabilities for FunctionExecutor<'a> { type SupervisorFuncRef = wasmi::FuncRef; - fn store(&self) -> &sandbox::Store { - &self.sandbox_store - } - fn store_mut(&mut self) -> &mut sandbox::Store { - &mut self.sandbox_store - } - fn allocate(&mut self, len: WordSize) -> Result, Error> { - let heap = &mut self.heap; - self.memory.with_direct_access_mut(|mem| { - heap.allocate(mem, len) - }) - } - fn deallocate(&mut self, ptr: Pointer) -> Result<(), Error> { - let heap = &mut self.heap; - self.memory.with_direct_access_mut(|mem| { - heap.deallocate(mem, ptr) - }) - } - fn write_memory(&mut self, ptr: Pointer, data: &[u8]) -> Result<(), Error> { - self.memory.set(ptr.into(), data).map_err(Into::into) - } - fn read_memory(&self, ptr: Pointer, len: WordSize) -> Result, Error> { - self.memory.get(ptr.into(), len as usize).map_err(Into::into) - } - fn invoke( &mut self, dispatch_thunk: &Self::SupervisorFuncRef, @@ -102,8 +73,7 @@ impl<'a> sandbox::SandboxCapabilities for FunctionExecutor<'a> { invoke_args_len: WordSize, state: u32, func_idx: sandbox::SupervisorFuncIndex, - ) -> Result - { + ) -> Result { let result = wasmi::FuncInstance::invoke( dispatch_thunk, &[ @@ -217,7 +187,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> { trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id); // Deserialize arguments and convert them into wasmi types. - let args = Vec::::decode(&mut &args[..]) + let args = Vec::::decode(&mut &args[..]) .map_err(|_| "Can't decode serialized arguments for the invocation")? .into_iter() .map(Into::into) @@ -230,7 +200,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> { Ok(None) => Ok(sandbox_primitives::ERR_OK), Ok(Some(val)) => { // Serialize return value and write it back into the memory. - sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| { + sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| { if val.len() > return_val_len as usize { Err("Return value buffer is too small")?; } @@ -263,8 +233,15 @@ impl<'a> Sandbox for FunctionExecutor<'a> { .clone() }; + let guest_env = match sandbox::GuestEnvironment::decode(&self.sandbox_store, raw_env_def) { + Ok(guest_env) => guest_env, + Err(_) => return Ok(sandbox_primitives::ERR_MODULE as u32), + }; + let instance_idx_or_err_code = - match sandbox::instantiate(self, dispatch_thunk, wasm, raw_env_def, state) { + match sandbox::instantiate(self, dispatch_thunk, wasm, guest_env, state) + .map(|i| i.register(&mut self.sandbox_store)) + { Ok(instance_idx) => instance_idx, Err(sandbox::InstantiationError::StartTrapped) => sandbox_primitives::ERR_EXECUTION, @@ -273,20 +250,49 @@ impl<'a> Sandbox for FunctionExecutor<'a> { Ok(instance_idx_or_err_code as u32) } + + fn get_global_val( + &self, + instance_idx: u32, + name: &str, + ) -> WResult> { + self.sandbox_store + .instance(instance_idx) + .map(|i| i.get_global_val(name)) + .map_err(|e| e.to_string()) + } } +/// Will be used on initialization of a module to resolve function and memory imports. struct Resolver<'a> { - host_functions: &'a[&'static dyn Function], - allow_missing_imports: bool, + /// All the hot functions that we export for the WASM blob. + host_functions: &'a [&'static dyn Function], + /// Should we allow missing function imports? + /// + /// If `true`, we return a stub that will return an error when being called. + allow_missing_func_imports: bool, + /// All the names of functions for that we did not provide a host function. missing_functions: RefCell>, + /// Will be used as initial and maximum size of the imported memory. + heap_pages: usize, + /// By default, runtimes should import memory and this is `Some(_)` after + /// reolving. However, to be backwards compatible, we also support memory + /// exported by the WASM blob (this will be `None` after resolving). + import_memory: RefCell>, } impl<'a> Resolver<'a> { - fn new(host_functions: &'a[&'static dyn Function], allow_missing_imports: bool) -> Resolver<'a> { + fn new( + host_functions: &'a[&'static dyn Function], + allow_missing_func_imports: bool, + heap_pages: usize, + ) -> Resolver<'a> { Resolver { host_functions, - allow_missing_imports, + allow_missing_func_imports, missing_functions: RefCell::new(Vec::new()), + heap_pages, + import_memory: Default::default(), } } } @@ -315,7 +321,7 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { } } - if self.allow_missing_imports { + if self.allow_missing_func_imports { trace!(target: "wasm-executor", "Could not find function `{}`, a stub will be provided instead.", name); let id = self.missing_functions.borrow().len() + self.host_functions.len(); self.missing_functions.borrow_mut().push(name.to_string()); @@ -327,6 +333,48 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { )) } } + + fn resolve_memory( + &self, + field_name: &str, + memory_type: &wasmi::MemoryDescriptor, + ) -> Result { + if field_name == "memory" { + match &mut *self.import_memory.borrow_mut() { + Some(_) => Err(wasmi::Error::Instantiation( + "Memory can not be imported twice!".into(), + )), + memory_ref @ None => { + if memory_type + .maximum() + .map(|m| m.saturating_sub(memory_type.initial())) + .map(|m| self.heap_pages > m as usize) + .unwrap_or(false) + { + Err(wasmi::Error::Instantiation(format!( + "Heap pages ({}) is greater than imported memory maximum ({}).", + self.heap_pages, + memory_type + .maximum() + .map(|m| m.saturating_sub(memory_type.initial())) + .expect("Maximum is set, checked above; qed"), + ))) + } else { + let memory = MemoryInstance::alloc( + Pages(memory_type.initial() as usize + self.heap_pages), + Some(Pages(memory_type.initial() as usize + self.heap_pages)), + )?; + *memory_ref = Some(memory.clone()); + Ok(memory) + } + } + } + } else { + Err(wasmi::Error::Instantiation( + format!("Unknown memory reference with name: {}", field_name), + )) + } + } } impl<'a> wasmi::Externals for FunctionExecutor<'a> { @@ -340,7 +388,7 @@ impl<'a> wasmi::Externals for FunctionExecutor<'a> { .map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg)) .map_err(wasmi::Trap::from) .map(|v| v.map(Into::into)) - } else if self.allow_missing_imports + } else if self.allow_missing_func_imports && index >= self.host_functions.len() && index < self.host_functions.len() + self.missing_functions.len() { @@ -382,15 +430,14 @@ fn get_heap_base(module: &ModuleRef) -> Result { /// Call a given method in the given wasm-module runtime. fn call_in_wasm_module( module_instance: &ModuleRef, + memory: &MemoryRef, method: &str, data: &[u8], host_functions: &[&'static dyn Function], - allow_missing_imports: bool, + allow_missing_func_imports: bool, missing_functions: &Vec, ) -> Result, Error> { - // extract a reference to a linear memory, optional reference to a table - // and then initialize FunctionExecutor. - let memory = get_mem_instance(module_instance)?; + // Initialize FunctionExecutor. let table: Option = module_instance .export_by_name("__indirect_function_table") .and_then(|e| e.as_table().cloned()); @@ -401,7 +448,7 @@ fn call_in_wasm_module( heap_base, table, host_functions, - allow_missing_imports, + allow_missing_func_imports, missing_functions, )?; @@ -437,9 +484,9 @@ fn instantiate_module( heap_pages: usize, module: &Module, host_functions: &[&'static dyn Function], - allow_missing_imports: bool, -) -> Result<(ModuleRef, Vec), Error> { - let resolver = Resolver::new(host_functions, allow_missing_imports); + allow_missing_func_imports: bool, +) -> Result<(ModuleRef, Vec, MemoryRef), Error> { + let resolver = Resolver::new(host_functions, allow_missing_func_imports, heap_pages); // start module instantiation. Don't run 'start' function yet. let intermediate_instance = ModuleInstance::new( module, @@ -449,15 +496,33 @@ fn instantiate_module( // Verify that the module has the heap base global variable. let _ = get_heap_base(intermediate_instance.not_started_instance())?; - // Extract a reference to a linear memory. - let memory = get_mem_instance(intermediate_instance.not_started_instance())?; - memory.grow(Pages(heap_pages)).map_err(|_| Error::Runtime)?; + + // Get the memory reference. Runtimes should import memory, but to be backwards + // compatible we also support exported memory. + let memory = match resolver.import_memory.into_inner() { + Some(memory) => memory, + None => { + debug!( + target: "wasm-executor", + "WASM blob does not imports memory, falling back to exported memory", + ); + + let memory = get_mem_instance(intermediate_instance.not_started_instance())?; + memory.grow(Pages(heap_pages)).map_err(|_| Error::Runtime)?; + + memory + } + }; if intermediate_instance.has_start() { // Runtime is not allowed to have the `start` function. Err(Error::RuntimeHasStartFn) } else { - Ok((intermediate_instance.assert_no_start(), resolver.missing_functions.into_inner())) + Ok(( + intermediate_instance.assert_no_start(), + resolver.missing_functions.into_inner(), + memory, + )) } } @@ -470,7 +535,6 @@ struct StateSnapshot { data_segments: Vec<(u32, Vec)>, /// The list of all global mutable variables of the module in their sequential order. global_mut_values: Vec, - heap_pages: u64, } impl StateSnapshot { @@ -478,7 +542,6 @@ impl StateSnapshot { fn take( module_instance: &ModuleRef, data_segments: Vec, - heap_pages: u64, ) -> Option { let prepared_segments = data_segments .into_iter() @@ -524,7 +587,6 @@ impl StateSnapshot { Some(Self { data_segments: prepared_segments, global_mut_values, - heap_pages, }) } @@ -532,14 +594,7 @@ impl StateSnapshot { /// the preserved memory and globals. /// /// Returns `Err` if applying the snapshot is failed. - fn apply(&self, instance: &ModuleRef) -> Result<(), WasmError> { - let memory = instance - .export_by_name("memory") - .ok_or(WasmError::ApplySnapshotFailed)? - .as_memory() - .cloned() - .ok_or(WasmError::ApplySnapshotFailed)?; - + fn apply(&self, instance: &ModuleRef, memory: &MemoryRef) -> Result<(), WasmError> { // First, erase the memory and copy the data segments into it. memory .erase() @@ -573,22 +628,20 @@ impl StateSnapshot { pub struct WasmiRuntime { /// A wasm module instance. instance: ModuleRef, + /// The memory instance of used by the wasm module. + memory: MemoryRef, /// The snapshot of the instance's state taken just after the instantiation. state_snapshot: StateSnapshot, /// The host functions registered for this instance. host_functions: Vec<&'static dyn Function>, /// Enable stub generation for functions that are not available in `host_functions`. /// These stubs will error when the wasm blob tries to call them. - allow_missing_imports: bool, + allow_missing_func_imports: bool, /// List of missing functions detected during function resolution missing_functions: Vec, } impl WasmRuntime for WasmiRuntime { - fn update_heap_pages(&mut self, heap_pages: u64) -> bool { - self.state_snapshot.heap_pages == heap_pages - } - fn host_functions(&self) -> &[&'static dyn Function] { &self.host_functions } @@ -598,7 +651,7 @@ impl WasmRuntime for WasmiRuntime { method: &str, data: &[u8], ) -> Result, Error> { - self.state_snapshot.apply(&self.instance) + self.state_snapshot.apply(&self.instance, &self.memory) .map_err(|e| { // Snapshot restoration failed. This is pretty unexpected since this can happen // if some invariant is broken or if the system is under extreme memory pressure @@ -608,20 +661,34 @@ impl WasmRuntime for WasmiRuntime { })?; call_in_wasm_module( &self.instance, + &self.memory, method, data, &self.host_functions, - self.allow_missing_imports, + self.allow_missing_func_imports, &self.missing_functions, ) } + + fn get_global_val(&self, name: &str) -> Result, Error> { + match self.instance.export_by_name(name) { + Some(global) => Ok(Some( + global + .as_global() + .ok_or_else(|| format!("`{}` is not a global", name))? + .get() + .into() + )), + None => Ok(None), + } + } } pub fn create_instance( code: &[u8], heap_pages: u64, host_functions: Vec<&'static dyn Function>, - allow_missing_imports: bool, + allow_missing_func_imports: bool, ) -> Result { let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?; @@ -632,15 +699,15 @@ pub fn create_instance( let data_segments = extract_data_segments(&code)?; // Instantiate this module. - let (instance, missing_functions) = instantiate_module( + let (instance, missing_functions, memory) = instantiate_module( heap_pages as usize, &module, &host_functions, - allow_missing_imports, + allow_missing_func_imports, ).map_err(|e| WasmError::Instantiation(e.to_string()))?; // Take state snapshot before executing anything. - let state_snapshot = StateSnapshot::take(&instance, data_segments, heap_pages) + let state_snapshot = StateSnapshot::take(&instance, data_segments) .expect( "`take` returns `Err` if the module is not valid; we already loaded module above, thus the `Module` is proven to be valid at this point; @@ -650,9 +717,10 @@ pub fn create_instance( Ok(WasmiRuntime { instance, + memory, state_snapshot, host_functions, - allow_missing_imports, + allow_missing_func_imports, missing_functions, }) } diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 44912086eaaed0f06a8bf751b08b71078fd97643..eb41adb2714e03b8b99291c70a6dbabb668020ee 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-executor-wasmtime" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4.8" @@ -13,15 +14,9 @@ sc-executor-common = { version = "0.8", path = "../common" } sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } -cranelift-codegen = "0.50" -cranelift-entity = "0.50" -cranelift-frontend = "0.50" -cranelift-native = "0.50" -cranelift-wasm = "0.50" -wasmtime-environ = "0.8" -wasmtime-jit = "0.8" -wasmtime-runtime = "0.8" +wasmtime = "0.11" [dev-dependencies] assert_matches = "1.3.0" diff --git a/client/executor/wasmtime/src/function_executor.rs b/client/executor/wasmtime/src/function_executor.rs deleted file mode 100644 index b398ea8476d8b8a99b3b8cba49386a2e54d19bee..0000000000000000000000000000000000000000 --- a/client/executor/wasmtime/src/function_executor.rs +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use sc_executor_common::allocator::FreeingBumpHeapAllocator; -use sc_executor_common::error::{Error, Result}; -use sc_executor_common::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; -use crate::util::{ - checked_range, cranelift_ir_signature, read_memory_into, write_memory_from, -}; - -use codec::{Decode, Encode}; -use cranelift_codegen::ir; -use cranelift_codegen::isa::TargetFrontendConfig; -use log::trace; -use sp_core::sandbox as sandbox_primitives; -use std::{cmp, mem, ptr}; -use wasmtime_environ::translate_signature; -use wasmtime_jit::{ActionError, Compiler}; -use wasmtime_runtime::{Export, VMCallerCheckedAnyfunc, VMContext, wasmtime_call_trampoline}; -use sp_wasm_interface::{ - FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, Signature, Value, ValueType, - WordSize, -}; - -/// Wrapper type for pointer to a Wasm table entry. -/// -/// The wrapper type is used to ensure that the function reference is valid as it must be unsafely -/// dereferenced from within the safe method `::invoke`. -#[derive(Clone, Copy)] -pub struct SupervisorFuncRef(*const VMCallerCheckedAnyfunc); - -/// The state required to construct a FunctionExecutor context. The context only lasts for one host -/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make -/// many different host calls that must share state. -/// -/// This is stored as part of the host state of the "env" Wasmtime instance. -pub struct FunctionExecutorState { - sandbox_store: sandbox::Store, - heap: FreeingBumpHeapAllocator, -} - -impl FunctionExecutorState { - /// Constructs a new `FunctionExecutorState`. - pub fn new(heap_base: u32) -> Self { - FunctionExecutorState { - sandbox_store: sandbox::Store::new(), - heap: FreeingBumpHeapAllocator::new(heap_base), - } - } - - /// Returns a mutable reference to the heap allocator. - pub fn heap(&mut self) -> &mut FreeingBumpHeapAllocator { - &mut self.heap - } -} - -/// A `FunctionExecutor` implements `FunctionContext` for making host calls from a Wasmtime -/// runtime. The `FunctionExecutor` exists only for the lifetime of the call and borrows state from -/// a longer-living `FunctionExecutorState`. -pub struct FunctionExecutor<'a> { - compiler: &'a mut Compiler, - sandbox_store: &'a mut sandbox::Store, - heap: &'a mut FreeingBumpHeapAllocator, - memory: &'a mut [u8], - table: Option<&'a [VMCallerCheckedAnyfunc]>, -} - -impl<'a> FunctionExecutor<'a> { - /// Construct a new `FunctionExecutor`. - /// - /// The vmctx MUST come from a call to a function in the "env" module. - /// The state MUST be looked up from the host state of the "env" module. - pub unsafe fn new( - vmctx: *mut VMContext, - compiler: &'a mut Compiler, - state: &'a mut FunctionExecutorState, - ) -> Result - { - let memory = match (*vmctx).lookup_global_export("memory") { - Some(Export::Memory { definition, vmctx: _, memory: _ }) => - std::slice::from_raw_parts_mut( - (*definition).base, - (*definition).current_length, - ), - _ => return Err(Error::InvalidMemoryReference), - }; - let table = match (*vmctx).lookup_global_export("__indirect_function_table") { - Some(Export::Table { definition, vmctx: _, table: _ }) => - Some(std::slice::from_raw_parts( - (*definition).base as *const VMCallerCheckedAnyfunc, - (*definition).current_elements as usize, - )), - _ => None, - }; - Ok(FunctionExecutor { - compiler, - sandbox_store: &mut state.sandbox_store, - heap: &mut state.heap, - memory, - table, - }) - } -} - -impl<'a> SandboxCapabilities for FunctionExecutor<'a> { - type SupervisorFuncRef = SupervisorFuncRef; - - fn store(&self) -> &sandbox::Store { - &self.sandbox_store - } - - fn store_mut(&mut self) -> &mut sandbox::Store { - &mut self.sandbox_store - } - - fn allocate(&mut self, len: WordSize) -> Result> { - self.heap.allocate(self.memory, len) - } - - fn deallocate(&mut self, ptr: Pointer) -> Result<()> { - self.heap.deallocate(self.memory, ptr) - } - - fn write_memory(&mut self, ptr: Pointer, data: &[u8]) -> Result<()> { - write_memory_from(self.memory, ptr, data) - } - - fn read_memory(&self, ptr: Pointer, len: WordSize) -> Result> { - let mut output = vec![0; len as usize]; - read_memory_into(self.memory, ptr, output.as_mut())?; - Ok(output) - } - - fn invoke( - &mut self, - dispatch_thunk: &Self::SupervisorFuncRef, - invoke_args_ptr: Pointer, - invoke_args_len: WordSize, - state: u32, - func_idx: SupervisorFuncIndex, - ) -> Result - { - let func_ptr = unsafe { (*dispatch_thunk.0).func_ptr }; - let vmctx = unsafe { (*dispatch_thunk.0).vmctx }; - - // The following code is based on the wasmtime_jit::Context::invoke. - let value_size = mem::size_of::(); - let (signature, mut values_vec) = generate_signature_and_args( - &[ - Value::I32(u32::from(invoke_args_ptr) as i32), - Value::I32(invoke_args_len as i32), - Value::I32(state as i32), - Value::I32(usize::from(func_idx) as i32), - ], - Some(ValueType::I64), - self.compiler.frontend_config(), - ); - - // Get the trampoline to call for this function. - let exec_code_buf = self.compiler - .get_published_trampoline(func_ptr, &signature, value_size) - .map_err(ActionError::Setup) - .map_err(|e| Error::Other(e.to_string()))?; - - // Call the trampoline. - if let Err(message) = unsafe { - wasmtime_call_trampoline( - vmctx, - exec_code_buf, - values_vec.as_mut_ptr() as *mut u8, - ) - } { - return Err(Error::Other(message)); - } - - // Load the return value out of `values_vec`. - Ok(unsafe { ptr::read(values_vec.as_ptr() as *const i64) }) - } -} - -impl<'a> FunctionContext for FunctionExecutor<'a> { - fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { - read_memory_into(self.memory, address, dest).map_err(|e| e.to_string()) - } - - fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { - write_memory_from(self.memory, address, data).map_err(|e| e.to_string()) - } - - fn allocate_memory(&mut self, size: WordSize) -> WResult> { - self.heap.allocate(self.memory, size).map_err(|e| e.to_string()) - } - - fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { - self.heap.deallocate(self.memory, ptr).map_err(|e| e.to_string()) - } - - fn sandbox(&mut self) -> &mut dyn Sandbox { - self - } -} - -impl<'a> Sandbox for FunctionExecutor<'a> { - fn memory_get( - &mut self, - memory_id: MemoryId, - offset: WordSize, - buf_ptr: Pointer, - buf_len: WordSize, - ) -> WResult - { - let sandboxed_memory = self.sandbox_store.memory(memory_id) - .map_err(|e| e.to_string())?; - sandboxed_memory.with_direct_access(|memory| { - let len = buf_len as usize; - let src_range = match checked_range(offset as usize, len, memory.len()) { - Some(range) => range, - None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - }; - let dst_range = match checked_range(buf_ptr.into(), len, self.memory.len()) { - Some(range) => range, - None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - }; - &mut self.memory[dst_range].copy_from_slice(&memory[src_range]); - Ok(sandbox_primitives::ERR_OK) - }) - } - - fn memory_set( - &mut self, - memory_id: MemoryId, - offset: WordSize, - val_ptr: Pointer, - val_len: WordSize, - ) -> WResult - { - let sandboxed_memory = self.sandbox_store.memory(memory_id) - .map_err(|e| e.to_string())?; - sandboxed_memory.with_direct_access_mut(|memory| { - let len = val_len as usize; - let src_range = match checked_range(val_ptr.into(), len, self.memory.len()) { - Some(range) => range, - None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - }; - let dst_range = match checked_range(offset as usize, len, memory.len()) { - Some(range) => range, - None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - }; - &mut memory[dst_range].copy_from_slice(&self.memory[src_range]); - Ok(sandbox_primitives::ERR_OK) - }) - } - - fn memory_teardown(&mut self, memory_id: MemoryId) - -> WResult<()> - { - self.sandbox_store.memory_teardown(memory_id).map_err(|e| e.to_string()) - } - - fn memory_new(&mut self, initial: u32, maximum: MemoryId) -> WResult { - self.sandbox_store.new_memory(initial, maximum).map_err(|e| e.to_string()) - } - - fn invoke( - &mut self, - instance_id: u32, - export_name: &str, - args: &[u8], - return_val: Pointer, - return_val_len: u32, - state: u32, - ) -> WResult { - trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id); - - // Deserialize arguments and convert them into wasmi types. - let args = Vec::::decode(&mut &args[..]) - .map_err(|_| "Can't decode serialized arguments for the invocation")? - .into_iter() - .map(Into::into) - .collect::>(); - - let instance = self.sandbox_store.instance(instance_id).map_err(|e| e.to_string())?; - let result = instance.invoke(export_name, &args, self, state); - - match result { - Ok(None) => Ok(sandbox_primitives::ERR_OK), - Ok(Some(val)) => { - // Serialize return value and write it back into the memory. - sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| { - if val.len() > return_val_len as usize { - Err("Return value buffer is too small")?; - } - FunctionContext::write_memory(self, return_val, val)?; - Ok(sandbox_primitives::ERR_OK) - }) - } - Err(_) => Ok(sandbox_primitives::ERR_EXECUTION), - } - } - - fn instance_teardown(&mut self, instance_id: u32) -> WResult<()> { - self.sandbox_store.instance_teardown(instance_id).map_err(|e| e.to_string()) - } - - fn instance_new(&mut self, dispatch_thunk_id: u32, wasm: &[u8], raw_env_def: &[u8], state: u32) - -> WResult - { - // Extract a dispatch thunk from instance's table by the specified index. - let dispatch_thunk = { - let table = self.table.as_ref() - .ok_or_else(|| "Runtime doesn't have a table; sandbox is unavailable")?; - let func_ref = table.get(dispatch_thunk_id as usize) - .ok_or_else(|| "dispatch_thunk_idx is out of the table bounds")?; - SupervisorFuncRef(func_ref) - }; - - let instance_idx_or_err_code = - match sandbox::instantiate(self, dispatch_thunk, wasm, raw_env_def, state) { - Ok(instance_idx) => instance_idx, - Err(sandbox::InstantiationError::StartTrapped) => - sandbox_primitives::ERR_EXECUTION, - Err(_) => sandbox_primitives::ERR_MODULE, - }; - - Ok(instance_idx_or_err_code as u32) - } -} - -// The storage for a Wasmtime invocation argument. -#[derive(Debug, Default, Copy, Clone)] -#[repr(C, align(8))] -struct VMInvokeArgument([u8; 8]); - -fn generate_signature_and_args( - args: &[Value], - result_type: Option, - frontend_config: TargetFrontendConfig, -) -> (ir::Signature, Vec) -{ - // This code is based on the wasmtime_jit::Context::invoke. - - let param_types = args.iter() - .map(|arg| arg.value_type()) - .collect::>(); - let signature = translate_signature( - cranelift_ir_signature( - Signature::new(param_types, result_type), - &frontend_config.default_call_conv - ), - frontend_config.pointer_type() - ); - - let mut values_vec = vec![ - VMInvokeArgument::default(); - cmp::max(args.len(), result_type.iter().len()) - ]; - - // Store the argument values into `values_vec`. - for (index, arg) in args.iter().enumerate() { - unsafe { - let ptr = values_vec.as_mut_ptr().add(index); - - match arg { - Value::I32(x) => ptr::write(ptr as *mut i32, *x), - Value::I64(x) => ptr::write(ptr as *mut i64, *x), - Value::F32(x) => ptr::write(ptr as *mut u32, *x), - Value::F64(x) => ptr::write(ptr as *mut u64, *x), - } - } - } - - (signature, values_vec) -} - diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs new file mode 100644 index 0000000000000000000000000000000000000000..e0cc6ecc9ae7a1dd738352a0d287ff9d5ad7ff5b --- /dev/null +++ b/client/executor/wasmtime/src/host.rs @@ -0,0 +1,349 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! This module defines `HostState` and `HostContext` structs which provide logic and state +//! required for execution of host. + +use crate::instance_wrapper::InstanceWrapper; +use crate::util; +use std::cell::RefCell; +use log::trace; +use codec::{Encode, Decode}; +use sp_allocator::FreeingBumpHeapAllocator; +use sc_executor_common::error::Result; +use sc_executor_common::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; +use sp_core::sandbox as sandbox_primitives; +use sp_wasm_interface::{FunctionContext, MemoryId, Pointer, Sandbox, WordSize}; +use wasmtime::{Func, Val}; + +/// Wrapper type for pointer to a Wasm table entry. +/// +/// The wrapper type is used to ensure that the function reference is valid as it must be unsafely +/// dereferenced from within the safe method `::invoke`. +#[derive(Clone)] +pub struct SupervisorFuncRef(Func); + +/// The state required to construct a HostContext context. The context only lasts for one host +/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make +/// many different host calls that must share state. +pub struct HostState { + // We need some interior mutability here since the host state is shared between all host + // function handlers and the wasmtime backend's `impl WasmRuntime`. + // + // Furthermore, because of recursive calls (e.g. runtime can create and call an sandboxed + // instance which in turn can call the runtime back) we have to be very careful with borrowing + // those. + // + // Basically, most of the interactions should do temporary borrow immediately releasing the + // borrow after performing necessary queries/changes. + sandbox_store: RefCell>, + allocator: RefCell, + instance: InstanceWrapper, +} + +impl HostState { + /// Constructs a new `HostState`. + pub fn new(allocator: FreeingBumpHeapAllocator, instance: InstanceWrapper) -> Self { + HostState { + sandbox_store: RefCell::new(sandbox::Store::new()), + allocator: RefCell::new(allocator), + instance, + } + } + + /// Destruct the host state and extract the `InstanceWrapper` passed at the creation. + pub fn into_instance(self) -> InstanceWrapper { + self.instance + } + + /// Materialize `HostContext` that can be used to invoke a substrate host `dyn Function`. + pub fn materialize<'a>(&'a self) -> HostContext<'a> { + HostContext(self) + } +} + +/// A `HostContext` implements `FunctionContext` for making host calls from a Wasmtime +/// runtime. The `HostContext` exists only for the lifetime of the call and borrows state from +/// a longer-living `HostState`. +pub struct HostContext<'a>(&'a HostState); + +impl<'a> std::ops::Deref for HostContext<'a> { + type Target = HostState; + fn deref(&self) -> &HostState { + self.0 + } +} + +impl<'a> SandboxCapabilities for HostContext<'a> { + type SupervisorFuncRef = SupervisorFuncRef; + + fn invoke( + &mut self, + dispatch_thunk: &Self::SupervisorFuncRef, + invoke_args_ptr: Pointer, + invoke_args_len: WordSize, + state: u32, + func_idx: SupervisorFuncIndex, + ) -> Result { + let result = dispatch_thunk.0.call(&[ + Val::I32(u32::from(invoke_args_ptr) as i32), + Val::I32(invoke_args_len as i32), + Val::I32(state as i32), + Val::I32(usize::from(func_idx) as i32), + ]); + match result { + Ok(ret_vals) => { + let ret_val = if ret_vals.len() != 1 { + return Err(format!( + "Supervisor function returned {} results, expected 1", + ret_vals.len() + ) + .into()); + } else { + &ret_vals[0] + }; + + if let Some(ret_val) = ret_val.i64() { + Ok(ret_val) + } else { + return Err("Supervisor function returned unexpected result!".into()); + } + } + Err(err) => Err(err.message().to_string().into()), + } + } +} + +impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> { + fn read_memory_into( + &self, + address: Pointer, + dest: &mut [u8], + ) -> sp_wasm_interface::Result<()> { + self.instance + .read_memory_into(address, dest) + .map_err(|e| e.to_string()) + } + + fn write_memory(&mut self, address: Pointer, data: &[u8]) -> sp_wasm_interface::Result<()> { + self.instance + .write_memory_from(address, data) + .map_err(|e| e.to_string()) + } + + fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result> { + self.instance + .allocate(&mut *self.allocator.borrow_mut(), size) + .map_err(|e| e.to_string()) + } + + fn deallocate_memory(&mut self, ptr: Pointer) -> sp_wasm_interface::Result<()> { + self.instance + .deallocate(&mut *self.allocator.borrow_mut(), ptr) + .map_err(|e| e.to_string()) + } + + fn sandbox(&mut self) -> &mut dyn Sandbox { + self + } +} + +impl<'a> Sandbox for HostContext<'a> { + fn memory_get( + &mut self, + memory_id: MemoryId, + offset: WordSize, + buf_ptr: Pointer, + buf_len: WordSize, + ) -> sp_wasm_interface::Result { + let sandboxed_memory = self + .sandbox_store + .borrow() + .memory(memory_id) + .map_err(|e| e.to_string())?; + sandboxed_memory.with_direct_access(|sandboxed_memory| { + let len = buf_len as usize; + let src_range = match util::checked_range(offset as usize, len, sandboxed_memory.len()) + { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + let supervisor_mem_size = self.instance.memory_size() as usize; + let dst_range = match util::checked_range(buf_ptr.into(), len, supervisor_mem_size) { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + self.instance + .write_memory_from( + Pointer::new(dst_range.start as u32), + &sandboxed_memory[src_range], + ) + .expect("ranges are checked above; write can't fail; qed"); + Ok(sandbox_primitives::ERR_OK) + }) + } + + fn memory_set( + &mut self, + memory_id: MemoryId, + offset: WordSize, + val_ptr: Pointer, + val_len: WordSize, + ) -> sp_wasm_interface::Result { + let sandboxed_memory = self + .sandbox_store + .borrow() + .memory(memory_id) + .map_err(|e| e.to_string())?; + sandboxed_memory.with_direct_access_mut(|sandboxed_memory| { + let len = val_len as usize; + let supervisor_mem_size = self.instance.memory_size() as usize; + let src_range = match util::checked_range(val_ptr.into(), len, supervisor_mem_size) { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + let dst_range = match util::checked_range(offset as usize, len, sandboxed_memory.len()) + { + Some(range) => range, + None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + }; + self.instance + .read_memory_into( + Pointer::new(src_range.start as u32), + &mut sandboxed_memory[dst_range], + ) + .expect("ranges are checked above; read can't fail; qed"); + Ok(sandbox_primitives::ERR_OK) + }) + } + + fn memory_teardown(&mut self, memory_id: MemoryId) -> sp_wasm_interface::Result<()> { + self.sandbox_store + .borrow_mut() + .memory_teardown(memory_id) + .map_err(|e| e.to_string()) + } + + fn memory_new(&mut self, initial: u32, maximum: MemoryId) -> sp_wasm_interface::Result { + self.sandbox_store + .borrow_mut() + .new_memory(initial, maximum) + .map_err(|e| e.to_string()) + } + + fn invoke( + &mut self, + instance_id: u32, + export_name: &str, + args: &[u8], + return_val: Pointer, + return_val_len: u32, + state: u32, + ) -> sp_wasm_interface::Result { + trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id); + + // Deserialize arguments and convert them into wasmi types. + let args = Vec::::decode(&mut &args[..]) + .map_err(|_| "Can't decode serialized arguments for the invocation")? + .into_iter() + .map(Into::into) + .collect::>(); + + let instance = self + .sandbox_store + .borrow() + .instance(instance_id) + .map_err(|e| e.to_string())?; + let result = instance.invoke(export_name, &args, self, state); + + match result { + Ok(None) => Ok(sandbox_primitives::ERR_OK), + Ok(Some(val)) => { + // Serialize return value and write it back into the memory. + sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| { + if val.len() > return_val_len as usize { + Err("Return value buffer is too small")?; + } + ::write_memory(self, return_val, val) + .map_err(|_| "can't write return value")?; + Ok(sandbox_primitives::ERR_OK) + }) + } + Err(_) => Ok(sandbox_primitives::ERR_EXECUTION), + } + } + + fn instance_teardown(&mut self, instance_id: u32) -> sp_wasm_interface::Result<()> { + self.sandbox_store + .borrow_mut() + .instance_teardown(instance_id) + .map_err(|e| e.to_string()) + } + + fn instance_new( + &mut self, + dispatch_thunk_id: u32, + wasm: &[u8], + raw_env_def: &[u8], + state: u32, + ) -> sp_wasm_interface::Result { + // Extract a dispatch thunk from the instance's table by the specified index. + let dispatch_thunk = { + let table_item = self + .instance + .table() + .as_ref() + .ok_or_else(|| "Runtime doesn't have a table; sandbox is unavailable")? + .get(dispatch_thunk_id); + + let func_ref = table_item + .ok_or_else(|| "dispatch_thunk_id is out of bounds")? + .funcref() + .ok_or_else(|| "dispatch_thunk_idx should be a funcref")? + .clone(); + SupervisorFuncRef(func_ref) + }; + + let guest_env = + match sandbox::GuestEnvironment::decode(&*self.sandbox_store.borrow(), raw_env_def) { + Ok(guest_env) => guest_env, + Err(_) => return Ok(sandbox_primitives::ERR_MODULE as u32), + }; + + let instance_idx_or_err_code = + match sandbox::instantiate(self, dispatch_thunk, wasm, guest_env, state) + .map(|i| i.register(&mut *self.sandbox_store.borrow_mut())) + { + Ok(instance_idx) => instance_idx, + Err(sandbox::InstantiationError::StartTrapped) => sandbox_primitives::ERR_EXECUTION, + Err(_) => sandbox_primitives::ERR_MODULE, + }; + + Ok(instance_idx_or_err_code as u32) + } + + fn get_global_val( + &self, + instance_idx: u32, + name: &str, + ) -> sp_wasm_interface::Result> { + self.sandbox_store + .borrow() + .instance(instance_idx) + .map(|i| i.get_global_val(name)) + .map_err(|e| e.to_string()) + } +} diff --git a/client/executor/wasmtime/src/imports.rs b/client/executor/wasmtime/src/imports.rs new file mode 100644 index 0000000000000000000000000000000000000000..349f84a0d74d27031ea3d3cba613cb3f36f02fbf --- /dev/null +++ b/client/executor/wasmtime/src/imports.rs @@ -0,0 +1,333 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::state_holder::StateHolder; +use sc_executor_common::error::WasmError; +use sp_wasm_interface::{Function, Value, ValueType}; +use std::any::Any; +use std::rc::Rc; +use wasmtime::{ + Callable, Extern, ExternType, Func, FuncType, ImportType, Limits, Memory, MemoryType, Module, + Trap, Val, +}; + +pub struct Imports { + /// Contains the index into `externs` where the memory import is stored if any. `None` if there + /// is none. + pub memory_import_index: Option, + pub externs: Vec, +} + +/// Goes over all imports of a module and prepares a vector of `Extern`s that can be used for +/// instantiation of the module. Returns an error if there are imports that cannot be satisfied. +pub fn resolve_imports( + state_holder: &StateHolder, + module: &Module, + host_functions: &[&'static dyn Function], + heap_pages: u32, + allow_missing_func_imports: bool, +) -> Result { + let mut externs = vec![]; + let mut memory_import_index = None; + for import_ty in module.imports() { + if import_ty.module() != "env" { + return Err(WasmError::Other(format!( + "host doesn't provide any imports from non-env module: {}:{}", + import_ty.module(), + import_ty.name() + ))); + } + + let resolved = match import_ty.name() { + "memory" => { + memory_import_index = Some(externs.len()); + resolve_memory_import(module, import_ty, heap_pages)? + } + _ => resolve_func_import( + module, + state_holder, + import_ty, + host_functions, + allow_missing_func_imports, + )?, + }; + externs.push(resolved); + } + Ok(Imports { + memory_import_index, + externs, + }) +} + +fn resolve_memory_import( + module: &Module, + import_ty: &ImportType, + heap_pages: u32, +) -> Result { + let requested_memory_ty = match import_ty.ty() { + ExternType::Memory(memory_ty) => memory_ty, + _ => { + return Err(WasmError::Other(format!( + "this import must be of memory type: {}:{}", + import_ty.module(), + import_ty.name() + ))) + } + }; + + // Increment the min (a.k.a initial) number of pages by `heap_pages` and check if it exceeds the + // maximum specified by the import. + let initial = requested_memory_ty + .limits() + .min() + .saturating_add(heap_pages); + if let Some(max) = requested_memory_ty.limits().max() { + if initial > max { + return Err(WasmError::Other(format!( + "incremented number of pages by heap_pages (total={}) is more than maximum requested\ + by the runtime wasm module {}", + initial, + max, + ))); + } + } + + let memory_ty = MemoryType::new(Limits::new(initial, requested_memory_ty.limits().max())); + let memory = Memory::new(module.store(), memory_ty); + Ok(Extern::Memory(memory)) +} + +fn resolve_func_import( + module: &Module, + state_holder: &StateHolder, + import_ty: &ImportType, + host_functions: &[&'static dyn Function], + allow_missing_func_imports: bool, +) -> Result { + let func_ty = match import_ty.ty() { + ExternType::Func(func_ty) => func_ty, + _ => { + return Err(WasmError::Other(format!( + "host doesn't provide any non function imports besides 'memory': {}:{}", + import_ty.module(), + import_ty.name() + ))); + } + }; + + let host_func = match host_functions + .iter() + .find(|host_func| host_func.name() == import_ty.name()) + { + Some(host_func) => host_func, + None if allow_missing_func_imports => { + return Ok(MissingHostFuncHandler::new(import_ty).into_extern(module, func_ty)); + } + None => { + return Err(WasmError::Other(format!( + "host doesn't provide such function: {}:{}", + import_ty.module(), + import_ty.name() + ))); + } + }; + if !signature_matches(&func_ty, &wasmtime_func_sig(*host_func)) { + return Err(WasmError::Other(format!( + "signature mismatch for: {}:{}", + import_ty.module(), + import_ty.name() + ))); + } + + Ok(HostFuncHandler::new(&state_holder, *host_func).into_extern(module)) +} + +/// Returns `true` if `lhs` and `rhs` represent the same signature. +fn signature_matches(lhs: &wasmtime::FuncType, rhs: &wasmtime::FuncType) -> bool { + lhs.params() == rhs.params() && lhs.results() == rhs.results() +} + +/// This structure implements `Callable` and acts as a bridge between wasmtime and +/// substrate host functions. +struct HostFuncHandler { + state_holder: StateHolder, + host_func: &'static dyn Function, +} + +impl HostFuncHandler { + fn new(state_holder: &StateHolder, host_func: &'static dyn Function) -> Self { + Self { + state_holder: state_holder.clone(), + host_func, + } + } + + fn into_extern(self, module: &Module) -> Extern { + let func_ty = wasmtime_func_sig(self.host_func); + let func = Func::new(module.store(), func_ty, Rc::new(self)); + Extern::Func(func) + } +} + +impl Callable for HostFuncHandler { + fn call( + &self, + wasmtime_params: &[Val], + wasmtime_results: &mut [Val], + ) -> Result<(), wasmtime::Trap> { + let unwind_result = self.state_holder.with_context(|host_ctx| { + let mut host_ctx = host_ctx.expect( + "host functions can be called only from wasm instance; + wasm instance is always called initializing context; + therefore host_ctx cannot be None; + qed + ", + ); + // `into_value` panics if it encounters a value that doesn't fit into the values + // available in substrate. + // + // This, however, cannot happen since the signature of this function is created from + // a `dyn Function` signature of which cannot have a non substrate value by definition. + let mut params = wasmtime_params.iter().cloned().map(into_value); + + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + self.host_func.execute(&mut host_ctx, &mut params) + })) + }); + + let execution_result = match unwind_result { + Ok(execution_result) => execution_result, + Err(err) => return Err(Trap::new(stringify_panic_payload(err))), + }; + + match execution_result { + Ok(Some(ret_val)) => { + debug_assert!( + wasmtime_results.len() == 1, + "wasmtime function signature, therefore the number of results, should always \ + correspond to the number of results returned by the host function", + ); + wasmtime_results[0] = into_wasmtime_val(ret_val); + Ok(()) + } + Ok(None) => { + debug_assert!( + wasmtime_results.len() == 0, + "wasmtime function signature, therefore the number of results, should always \ + correspond to the number of results returned by the host function", + ); + Ok(()) + } + Err(msg) => Err(Trap::new(msg)), + } + } +} + +/// A `Callable` handler for missing functions. +struct MissingHostFuncHandler { + module: String, + name: String, +} + +impl MissingHostFuncHandler { + fn new(import_ty: &ImportType) -> Self { + Self { + module: import_ty.module().to_string(), + name: import_ty.name().to_string(), + } + } + + fn into_extern(self, module: &Module, func_ty: &FuncType) -> Extern { + let func = Func::new(module.store(), func_ty.clone(), Rc::new(self)); + Extern::Func(func) + } +} + +impl Callable for MissingHostFuncHandler { + fn call( + &self, + _wasmtime_params: &[Val], + _wasmtime_results: &mut [Val], + ) -> Result<(), wasmtime::Trap> { + Err(Trap::new(format!( + "call to a missing function {}:{}", + self.module, self.name + ))) + } +} + +fn wasmtime_func_sig(func: &dyn Function) -> wasmtime::FuncType { + let params = func + .signature() + .args + .iter() + .cloned() + .map(into_wasmtime_val_type) + .collect::>() + .into_boxed_slice(); + let results = func + .signature() + .return_value + .iter() + .cloned() + .map(into_wasmtime_val_type) + .collect::>() + .into_boxed_slice(); + wasmtime::FuncType::new(params, results) +} + +fn into_wasmtime_val_type(val_ty: ValueType) -> wasmtime::ValType { + match val_ty { + ValueType::I32 => wasmtime::ValType::I32, + ValueType::I64 => wasmtime::ValType::I64, + ValueType::F32 => wasmtime::ValType::F32, + ValueType::F64 => wasmtime::ValType::F64, + } +} + +/// Converts a `Val` into a substrate runtime interface `Value`. +/// +/// Panics if the given value doesn't have a corresponding variant in `Value`. +fn into_value(val: Val) -> Value { + match val { + Val::I32(v) => Value::I32(v), + Val::I64(v) => Value::I64(v), + Val::F32(f_bits) => Value::F32(f_bits), + Val::F64(f_bits) => Value::F64(f_bits), + _ => panic!("Given value type is unsupported by substrate"), + } +} + +fn into_wasmtime_val(value: Value) -> wasmtime::Val { + match value { + Value::I32(v) => Val::I32(v), + Value::I64(v) => Val::I64(v), + Value::F32(f_bits) => Val::F32(f_bits), + Value::F64(f_bits) => Val::F64(f_bits), + } +} + +/// Attempt to convert a opaque panic payload to a string. +fn stringify_panic_payload(payload: Box) -> String { + match payload.downcast::<&'static str>() { + Ok(msg) => msg.to_string(), + Err(payload) => match payload.downcast::() { + Ok(msg) => *msg, + // At least we tried... + Err(_) => "Box".to_string(), + }, + } +} diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs new file mode 100644 index 0000000000000000000000000000000000000000..013651cd7af885b98f370d9479aab44f7200ae33 --- /dev/null +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -0,0 +1,276 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Defines data and logic needed for interaction with an WebAssembly instance of a substrate +//! runtime module. + +use crate::util; +use crate::imports::Imports; + +use sc_executor_common::error::{Error, Result}; +use sp_wasm_interface::{Pointer, WordSize, Value}; +use std::slice; +use std::marker; +use wasmtime::{Instance, Module, Memory, Table, Val}; + +/// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime. +/// +/// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific +/// routines. +pub struct InstanceWrapper { + instance: Instance, + // The memory instance of the `intance`. + // + // It is important to make sure that we don't make any copies of this to make it easier to proof + // See `memory_as_slice` and `memory_as_slice_mut`. + memory: Memory, + table: Option, + // Make this struct explicitly !Send & !Sync. + _not_send_nor_sync: marker::PhantomData<*const ()>, +} + +impl InstanceWrapper { + /// Create a new instance wrapper from the given wasm module. + pub fn new(module: &Module, imports: &Imports, heap_pages: u32) -> Result { + let instance = Instance::new(module, &imports.externs) + .map_err(|e| Error::from(format!("cannot instantiate: {}", e)))?; + + let memory = match imports.memory_import_index { + Some(memory_idx) => { + imports.externs[memory_idx] + .memory() + .expect("only memory can be at the `memory_idx`; qed") + .clone() + } + None => { + let memory = get_linear_memory(&instance)?; + if !memory.grow(heap_pages).is_ok() { + return Err("failed top increase the linear memory size".into()); + } + memory + }, + }; + + Ok(Self { + table: get_table(&instance), + memory, + instance, + _not_send_nor_sync: marker::PhantomData, + }) + } + + /// Resolves a substrate entrypoint by the given name. + /// + /// An entrypoint must have a signature `(i32, i32) -> i64`, otherwise this function will return + /// an error. + pub fn resolve_entrypoint(&self, name: &str) -> Result { + // Resolve the requested method and verify that it has a proper signature. + let export = self + .instance + .get_export(name) + .ok_or_else(|| Error::from(format!("Exported method {} is not found", name)))?; + let entrypoint = export + .func() + .ok_or_else(|| Error::from(format!("Export {} is not a function", name)))?; + match (entrypoint.ty().params(), entrypoint.ty().results()) { + (&[wasmtime::ValType::I32, wasmtime::ValType::I32], &[wasmtime::ValType::I64]) => {} + _ => { + return Err(Error::from(format!( + "method {} have an unsupported signature", + name + ))) + } + } + Ok(entrypoint.clone()) + } + + /// Returns an indirect function table of this instance. + pub fn table(&self) -> Option<&Table> { + self.table.as_ref() + } + + /// Returns the byte size of the linear memory instance attached to this instance. + pub fn memory_size(&self) -> u32 { + self.memory.data_size() as u32 + } + + /// Reads `__heap_base: i32` global variable and returns it. + /// + /// If it doesn't exist, not a global or of not i32 type returns an error. + pub fn extract_heap_base(&self) -> Result { + let heap_base_export = self + .instance + .get_export("__heap_base") + .ok_or_else(|| Error::from("__heap_base is not found"))?; + + let heap_base_global = heap_base_export + .global() + .ok_or_else(|| Error::from("__heap_base is not a global"))?; + + let heap_base = heap_base_global + .get() + .i32() + .ok_or_else(|| Error::from("__heap_base is not a i32"))?; + + Ok(heap_base as u32) + } + + /// Get the value from a global with the given `name`. + pub fn get_global_val(&self, name: &str) -> Result> { + let global = match self.instance.get_export(name) { + Some(global) => global, + None => return Ok(None), + }; + + let global = global.global().ok_or_else(|| format!("`{}` is not a global", name))?; + + match global.get() { + Val::I32(val) => Ok(Some(Value::I32(val))), + Val::I64(val) => Ok(Some(Value::I64(val))), + Val::F32(val) => Ok(Some(Value::F32(val))), + Val::F64(val) => Ok(Some(Value::F64(val))), + _ => Err("Unknow value type".into()), + } + } +} + +/// Extract linear memory instance from the given instance. +fn get_linear_memory(instance: &Instance) -> Result { + let memory_export = instance + .get_export("memory") + .ok_or_else(|| Error::from("memory is not exported under `memory` name"))?; + + let memory = memory_export + .memory() + .ok_or_else(|| Error::from("the `memory` export should have memory type"))? + .clone(); + + Ok(memory) +} + +/// Extract the table from the given instance if any. +fn get_table(instance: &Instance) -> Option
{ + instance + .get_export("__indirect_function_table") + .and_then(|export| export.table()) + .cloned() +} + +/// Functions realted to memory. +impl InstanceWrapper { + /// Read data from a slice of memory into a destination buffer. + /// + /// Returns an error if the read would go out of the memory bounds. + pub fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> Result<()> { + unsafe { + // This should be safe since we don't grow up memory while caching this reference and + // we give up the reference before returning from this function. + let memory = self.memory_as_slice(); + + let range = util::checked_range(address.into(), dest.len(), memory.len()) + .ok_or_else(|| Error::Other("memory read is out of bounds".into()))?; + dest.copy_from_slice(&memory[range]); + Ok(()) + } + } + + /// Write data to a slice of memory. + /// + /// Returns an error if the write would go out of the memory bounds. + pub fn write_memory_from(&self, address: Pointer, data: &[u8]) -> Result<()> { + unsafe { + // This should be safe since we don't grow up memory while caching this reference and + // we give up the reference before returning from this function. + let memory = self.memory_as_slice_mut(); + + let range = util::checked_range(address.into(), data.len(), memory.len()) + .ok_or_else(|| Error::Other("memory write is out of bounds".into()))?; + &mut memory[range].copy_from_slice(data); + Ok(()) + } + } + + /// Allocate some memory of the given size. Returns pointer to the allocated memory region. + /// + /// Returns `Err` in case memory cannot be allocated. Refer to the allocator documentation + /// to get more details. + pub fn allocate( + &self, + allocator: &mut sp_allocator::FreeingBumpHeapAllocator, + size: WordSize, + ) -> Result> { + unsafe { + // This should be safe since we don't grow up memory while caching this reference and + // we give up the reference before returning from this function. + let memory = self.memory_as_slice_mut(); + + allocator.allocate(memory, size).map_err(Into::into) + } + } + + /// Deallocate the memory pointed by the given pointer. + /// + /// Returns `Err` in case the given memory region cannot be deallocated. + pub fn deallocate( + &self, + allocator: &mut sp_allocator::FreeingBumpHeapAllocator, + ptr: Pointer, + ) -> Result<()> { + unsafe { + // This should be safe since we don't grow up memory while caching this reference and + // we give up the reference before returning from this function. + let memory = self.memory_as_slice_mut(); + + allocator.deallocate(memory, ptr).map_err(Into::into) + } + } + + /// Returns linear memory of the wasm instance as a slice. + /// + /// # Safety + /// + /// Wasmtime doesn't provide comprehensive documentation about the exact behavior of the data + /// pointer. If a dynamic style heap is used the base pointer of the heap can change. Since + /// growing, we cannot guarantee the lifetime of the returned slice reference. + unsafe fn memory_as_slice(&self) -> &[u8] { + let ptr = self.memory.data_ptr() as *const _; + let len = self.memory.data_size(); + + if len == 0 { + &[] + } else { + slice::from_raw_parts(ptr, len) + } + } + + /// Returns linear memory of the wasm instance as a slice. + /// + /// # Safety + /// + /// See `[memory_as_slice]`. In addition to those requirements, since a mutable reference is + /// returned it must be ensured that only one mutable and no shared references to memory exists + /// at the same time. + unsafe fn memory_as_slice_mut(&self) -> &mut [u8] { + let ptr = self.memory.data_ptr(); + let len = self.memory.data_size(); + + if len == 0 { + &mut [] + } else { + slice::from_raw_parts_mut(ptr, len) + } + } +} diff --git a/client/executor/wasmtime/src/lib.rs b/client/executor/wasmtime/src/lib.rs index 244fca8f842a3083cacbca010f70e53a7a0087eb..8f4801e6da1d0de171ff50d2e8b04c0a08d1f00d 100644 --- a/client/executor/wasmtime/src/lib.rs +++ b/client/executor/wasmtime/src/lib.rs @@ -16,10 +16,11 @@ ///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute. -mod function_executor; +mod host; mod runtime; -mod trampoline; +mod state_holder; +mod imports; +mod instance_wrapper; mod util; pub use runtime::create_instance; - diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index f9b1b6209c3b7c7bdfcfb3f8ba2a6329ff8c6387..b99d3347872059d778f50c3c00581252e224a167 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -16,68 +16,51 @@ //! Defines the compiled Wasm runtime that uses Wasmtime internally. -use crate::function_executor::FunctionExecutorState; -use crate::trampoline::{EnvState, make_trampoline}; -use crate::util::{cranelift_ir_signature, read_memory_into, write_memory_from}; +use crate::host::HostState; +use crate::imports::{resolve_imports, Imports}; +use crate::instance_wrapper::InstanceWrapper; +use crate::state_holder::StateHolder; use sc_executor_common::{ error::{Error, Result, WasmError}, wasm_runtime::WasmRuntime, }; -use sp_wasm_interface::{Pointer, WordSize, Function}; +use sp_allocator::FreeingBumpHeapAllocator; use sp_runtime_interface::unpack_ptr_and_len; +use sp_wasm_interface::{Function, Pointer, WordSize, Value}; +use wasmtime::{Config, Engine, Module, Store}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::rc::Rc; - -use cranelift_codegen::ir; -use cranelift_codegen::isa::TargetIsa; -use cranelift_entity::{EntityRef, PrimaryMap}; -use cranelift_frontend::FunctionBuilderContext; -use cranelift_wasm::DefinedFuncIndex; -use wasmtime_environ::{Module, translate_signature}; -use wasmtime_jit::{ - ActionOutcome, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context, RuntimeValue, -}; -use wasmtime_runtime::{Export, Imports, InstanceHandle, VMFunctionBody}; - -/// A `WasmRuntime` implementation using the Wasmtime JIT to compile the runtime module to native +/// A `WasmRuntime` implementation using wasmtime to compile the runtime module to machine code /// and execute the compiled code. pub struct WasmtimeRuntime { - module: CompiledModule, - context: Context, - max_heap_pages: Option, + module: Module, + imports: Imports, + state_holder: StateHolder, heap_pages: u32, - /// The host functions registered for this instance. host_functions: Vec<&'static dyn Function>, } impl WasmRuntime for WasmtimeRuntime { - fn update_heap_pages(&mut self, heap_pages: u64) -> bool { - match heap_pages_valid(heap_pages, self.max_heap_pages) { - Some(heap_pages) => { - self.heap_pages = heap_pages; - true - } - None => false, - } - } - fn host_functions(&self) -> &[&'static dyn Function] { &self.host_functions } fn call(&mut self, method: &str, data: &[u8]) -> Result> { call_method( - &mut self.context, - &mut self.module, + &self.module, + &mut self.imports, + &self.state_holder, method, data, self.heap_pages, ) } + + fn get_global_val(&self, name: &str) -> Result> { + // Yeah, there is no better way currently :( + InstanceWrapper::new(&self.module, &self.imports, self.heap_pages)? + .get_global_val(name) + } } /// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to @@ -86,283 +69,107 @@ pub fn create_instance( code: &[u8], heap_pages: u64, host_functions: Vec<&'static dyn Function>, + allow_missing_func_imports: bool, ) -> std::result::Result { - let (compiled_module, context) = create_compiled_unit(code, &host_functions)?; - - // Inspect the module for the min and max memory sizes. - let (min_memory_size, max_memory_size) = { - let module = compiled_module.module_ref(); - let memory_index = match module.exports.get("memory") { - Some(wasmtime_environ::Export::Memory(memory_index)) => *memory_index, - _ => return Err(WasmError::InvalidMemory), - }; - let memory_plan = module.memory_plans.get(memory_index) - .expect("memory_index is retrieved from the module's exports map; qed"); - (memory_plan.memory.minimum, memory_plan.memory.maximum) - }; - - // Check that heap_pages is within the allowed range. - let max_heap_pages = max_memory_size.map(|max| max.saturating_sub(min_memory_size)); - let heap_pages = heap_pages_valid(heap_pages, max_heap_pages) - .ok_or_else(|| WasmError::InvalidHeapPages)?; + // Create the engine, store and finally the module from the given code. + let mut config = Config::new(); + config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize); + + let engine = Engine::new(&config); + let store = Store::new(&engine); + let module = Module::new(&store, code) + .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; + + let state_holder = StateHolder::empty(); + + // Scan all imports, find the matching host functions, and create stubs that adapt arguments + // and results. + let imports = resolve_imports( + &state_holder, + &module, + &host_functions, + heap_pages as u32, + allow_missing_func_imports, + )?; Ok(WasmtimeRuntime { - module: compiled_module, - context, - max_heap_pages, - heap_pages, + module, + imports, + state_holder, + heap_pages: heap_pages as u32, host_functions, }) } -fn create_compiled_unit( - code: &[u8], - host_functions: &[&'static dyn Function], -) -> std::result::Result<(CompiledModule, Context), WasmError> { - let compilation_strategy = CompilationStrategy::Cranelift; - - let compiler = new_compiler(compilation_strategy)?; - let mut context = Context::new(Box::new(compiler)); - - // Enable/disable producing of debug info. - context.set_debug_info(false); - - // Instantiate and link the env module. - let global_exports = context.get_global_exports(); - let compiler = new_compiler(compilation_strategy)?; - let env_module = instantiate_env_module(global_exports, compiler, host_functions)?; - context.name_instance("env".to_owned(), env_module); - - // Compile the wasm module. - let module = context.compile_module(&code) - .map_err(|e| WasmError::Other(format!("module compile error: {}", e)))?; - - Ok((module, context)) -} - /// Call a function inside a precompiled Wasm module. fn call_method( - context: &mut Context, - module: &mut CompiledModule, + module: &Module, + imports: &mut Imports, + state_holder: &StateHolder, method: &str, data: &[u8], heap_pages: u32, ) -> Result> { - // Old exports get clobbered in `InstanceHandle::new` if we don't explicitly remove them first. - // - // The global exports mechanism is temporary in Wasmtime and expected to be removed. - // https://github.com/CraneStation/wasmtime/issues/332 - clear_globals(&mut *context.get_global_exports().borrow_mut()); - - let mut instance = module.instantiate() - .map_err(|e| Error::Other(e.to_string()))?; - - // Ideally there would be a way to set the heap pages during instantiation rather than - // growing the memory after the fact. Currently this may require an additional mmap and copy. - // However, the wasmtime API doesn't support modifying the size of memory on instantiation - // at this time. - grow_memory(&mut instance, heap_pages)?; - - // Initialize the function executor state. - let heap_base = get_heap_base(&instance)?; - let executor_state = FunctionExecutorState::new(heap_base); - reset_env_state_and_take_trap(context, Some(executor_state))?; - - // Write the input data into guest memory. - let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?; - let args = [RuntimeValue::I32(u32::from(data_ptr) as i32), RuntimeValue::I32(data_len as i32)]; - - // Invoke the function in the runtime. - let outcome = context - .invoke(&mut instance, method, &args[..]) - .map_err(|e| Error::Other(format!("error calling runtime: {}", e)))?; - let trap_error = reset_env_state_and_take_trap(context, None)?; - let (output_ptr, output_len) = match outcome { - ActionOutcome::Returned { values } => match values.as_slice() { - [RuntimeValue::I64(retval)] => unpack_ptr_and_len(*retval as u64), - _ => return Err(Error::InvalidReturn), - } - ActionOutcome::Trapped { message } => return Err(trap_error.unwrap_or_else( - || format!("Wasm execution trapped: {}", message).into() - )), - }; + let instance_wrapper = InstanceWrapper::new(module, imports, heap_pages)?; + let entrypoint = instance_wrapper.resolve_entrypoint(method)?; + let heap_base = instance_wrapper.extract_heap_base()?; + let allocator = FreeingBumpHeapAllocator::new(heap_base); - // Read the output data from guest memory. - let mut output = vec![0; output_len as usize]; - let memory = get_memory_mut(&mut instance)?; - read_memory_into(memory, Pointer::new(output_ptr), &mut output)?; - Ok(output) + perform_call(data, state_holder, instance_wrapper, entrypoint, allocator) } -/// The implementation is based on wasmtime_wasi::instantiate_wasi. -fn instantiate_env_module( - global_exports: Rc>>>, - compiler: Compiler, - host_functions: &[&'static dyn Function], -) -> std::result::Result -{ - let isa = target_isa()?; - let pointer_type = isa.pointer_type(); - let call_conv = isa.default_call_conv(); - - let mut fn_builder_ctx = FunctionBuilderContext::new(); - let mut module = Module::new(); - let mut finished_functions = >::new(); - let mut code_memory = CodeMemory::new(); - - for function in host_functions { - let sig = translate_signature( - cranelift_ir_signature(function.signature(), &call_conv), - pointer_type - ); - let sig_id = module.signatures.push(sig.clone()); - let func_id = module.functions.push(sig_id); - module - .exports - .insert(function.name().to_string(), wasmtime_environ::Export::Function(func_id)); - - let trampoline = make_trampoline( - isa.as_ref(), - &mut code_memory, - &mut fn_builder_ctx, - func_id.index() as u32, - &sig, - )?; - finished_functions.push(trampoline); - } - - code_memory.publish(); - - let imports = Imports::none(); - let data_initializers = Vec::new(); - let signatures = PrimaryMap::new(); - let env_state = EnvState::new(code_memory, compiler, host_functions); - - let result = InstanceHandle::new( - Rc::new(module), - global_exports, - finished_functions.into_boxed_slice(), - imports, - &data_initializers, - signatures.into_boxed_slice(), - None, - Box::new(env_state), - ); - result.map_err(|e| WasmError::Other(format!("cannot instantiate env: {}", e))) -} - -/// Build a new TargetIsa for the host machine. -fn target_isa() -> std::result::Result, WasmError> { - let isa_builder = cranelift_native::builder() - .map_err(|e| WasmError::Other(format!("missing compiler support: {}", e)))?; - let flag_builder = cranelift_codegen::settings::builder(); - Ok(isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))) -} - -fn new_compiler(strategy: CompilationStrategy) -> std::result::Result { - let isa = target_isa()?; - Ok(Compiler::new(isa, strategy)) -} - -fn clear_globals(global_exports: &mut HashMap>) { - global_exports.remove("memory"); - global_exports.remove("__heap_base"); - global_exports.remove("__indirect_function_table"); -} - -fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> { - // This is safe to wrap in an unsafe block as: - // - The result of the `lookup_immutable` call is not mutated - // - The definition pointer is returned by a lookup on a valid instance - let memory_index = unsafe { - match instance.lookup_immutable("memory") { - Some(Export::Memory { definition, vmctx: _, memory: _ }) => - instance.memory_index(&*definition), - _ => return Err(Error::InvalidMemoryReference), +fn perform_call( + data: &[u8], + state_holder: &StateHolder, + instance_wrapper: InstanceWrapper, + entrypoint: wasmtime::Func, + mut allocator: FreeingBumpHeapAllocator, +) -> Result> { + let (data_ptr, data_len) = inject_input_data(&instance_wrapper, &mut allocator, data)?; + + let host_state = HostState::new(allocator, instance_wrapper); + let (ret, host_state) = state_holder.with_initialized_state(host_state, || { + match entrypoint.call(&[ + wasmtime::Val::I32(u32::from(data_ptr) as i32), + wasmtime::Val::I32(u32::from(data_len) as i32), + ]) { + Ok(results) => { + let retval = results[0].unwrap_i64() as u64; + Ok(unpack_ptr_and_len(retval)) + } + Err(trap) => { + return Err(Error::from(format!( + "Wasm execution trapped: {}", + trap.message() + ))); + } } - }; - instance.memory_grow(memory_index, pages) - .map(|_| ()) - .ok_or_else(|| "requested heap_pages would exceed maximum memory size".into()) -} + }); + let (output_ptr, output_len) = ret?; -fn get_env_state(context: &mut Context) -> Result<&mut EnvState> { - let env_instance = context.get_instance("env") - .map_err(|err| format!("cannot find \"env\" module: {}", err))?; - env_instance - .host_state() - .downcast_mut::() - .ok_or_else(|| "cannot get \"env\" module host state".into()) -} + let instance = host_state.into_instance(); + let output = extract_output_data(&instance, output_ptr, output_len)?; -fn reset_env_state_and_take_trap( - context: &mut Context, - executor_state: Option, -) -> Result> -{ - let env_state = get_env_state(context)?; - env_state.executor_state = executor_state; - Ok(env_state.take_trap()) + Ok(output) } fn inject_input_data( - context: &mut Context, - instance: &mut InstanceHandle, + instance: &InstanceWrapper, + allocator: &mut FreeingBumpHeapAllocator, data: &[u8], ) -> Result<(Pointer, WordSize)> { - let env_state = get_env_state(context)?; - let executor_state = env_state.executor_state - .as_mut() - .ok_or_else(|| "cannot get \"env\" module executor state")?; - - let memory = get_memory_mut(instance)?; - let data_len = data.len() as WordSize; - let data_ptr = executor_state.heap().allocate(memory, data_len)?; - write_memory_from(memory, data_ptr, data)?; + let data_ptr = instance.allocate(allocator, data_len)?; + instance.write_memory_from(data_ptr, data)?; Ok((data_ptr, data_len)) } -fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> { - match instance.lookup("memory") { - // This is safe to wrap in an unsafe block as: - // - The definition pointer is returned by a lookup on a valid instance and thus points to - // a valid memory definition - Some(Export::Memory { definition, vmctx: _, memory: _ }) => unsafe { - Ok(std::slice::from_raw_parts_mut( - (*definition).base, - (*definition).current_length, - )) - }, - _ => Err(Error::InvalidMemoryReference), - } -} - -fn get_heap_base(instance: &InstanceHandle) -> Result { - // This is safe to wrap in an unsafe block as: - // - The result of the `lookup_immutable` call is not mutated - // - The definition pointer is returned by a lookup on a valid instance - // - The defined value is checked to be an I32, which can be read safely as a u32 - unsafe { - match instance.lookup_immutable("__heap_base") { - Some(Export::Global { definition, vmctx: _, global }) - if global.ty == ir::types::I32 => - Ok(*(*definition).as_u32()), - _ => return Err(Error::HeapBaseNotFoundOrInvalid), - } - } -} - -/// Checks whether the heap_pages parameter is within the valid range and converts it to a u32. -/// Returns None if heaps_pages in not in range. -fn heap_pages_valid(heap_pages: u64, max_heap_pages: Option) - -> Option -{ - let heap_pages = u32::try_from(heap_pages).ok()?; - if let Some(max_heap_pages) = max_heap_pages { - if heap_pages > max_heap_pages { - return None; - } - } - Some(heap_pages) +fn extract_output_data( + instance: &InstanceWrapper, + output_ptr: u32, + output_len: u32, +) -> Result> { + let mut output = vec![0; output_len as usize]; + instance.read_memory_into(Pointer::new(output_ptr), &mut output)?; + Ok(output) } diff --git a/client/executor/wasmtime/src/state_holder.rs b/client/executor/wasmtime/src/state_holder.rs new file mode 100644 index 0000000000000000000000000000000000000000..57564ed3ec414506e1960c41654e42be96e44576 --- /dev/null +++ b/client/executor/wasmtime/src/state_holder.rs @@ -0,0 +1,77 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::host::{HostContext, HostState}; +use std::cell::RefCell; +use std::rc::Rc; + +/// A common place to store a reference to the `HostState`. +/// +/// This structure is passed into each host function handler and retained in the implementation of +/// `WasmRuntime`. Whenever a call into a runtime method is initiated, the host state is populated +/// with the state for that runtime method call. +/// +/// During the execution of the runtime method call, wasm can call imported host functions. When +/// that happens the host function handler gets a `HostContext` (obtainable through having a +/// `HostState` reference). +#[derive(Clone)] +pub struct StateHolder { + // This is `Some` only during a call. + state: Rc>>, +} + +impl StateHolder { + /// Create a placeholder `StateHolder`. + pub fn empty() -> StateHolder { + StateHolder { + state: Rc::new(RefCell::new(None)), + } + } + + /// Provide `HostState` for the runtime method call and execute the given function `f`. + /// + /// During the execution of the provided function `with_context` will be callable. + pub fn with_initialized_state(&self, state: HostState, f: F) -> (R, HostState) + where + F: FnOnce() -> R, + { + *self.state.borrow_mut() = Some(state); + + let ret = f(); + let state = self + .state + .borrow_mut() + .take() + .expect("cannot be None since was just assigned; qed"); + + (ret, state) + } + + /// Create a `HostContext` from the contained `HostState` and execute the given function `f`. + /// + /// This function is only callable within closure passed to `init_state`. Otherwise, the passed + /// context will be `None`. + pub fn with_context(&self, f: F) -> R + where + F: FnOnce(Option) -> R, + { + let state = self.state.borrow(); + match *state { + Some(ref state) => f(Some(state.materialize())), + None => f(None), + } + } +} diff --git a/client/executor/wasmtime/src/trampoline.rs b/client/executor/wasmtime/src/trampoline.rs deleted file mode 100644 index 8a2147760921b62d55852d03de1562c000053148..0000000000000000000000000000000000000000 --- a/client/executor/wasmtime/src/trampoline.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! The trampoline is the dynamically generated entry point to a runtime host call. -//! -//! This code is based on and large parts are copied from wasmtime's -//! wasmtime-api/src/trampoline/func.rs. - -use crate::function_executor::{FunctionExecutorState, FunctionExecutor}; -use sc_executor_common::error::{Error, WasmError}; - -use cranelift_codegen::{Context, binemit, ir, isa}; -use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode}; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use cranelift_codegen::print_errors::pretty_error; -use wasmtime_jit::{CodeMemory, Compiler}; -use wasmtime_environ::CompiledFunction; -use wasmtime_runtime::{VMContext, VMFunctionBody}; -use sp_wasm_interface::{Function, Value, ValueType}; -use std::{cmp, panic::{self, AssertUnwindSafe}, ptr}; - -const CALL_SUCCESS: u32 = 0; -const CALL_FAILED_WITH_ERROR: u32 = 1; -const CALL_WITH_BAD_HOST_STATE: u32 = 2; - -/// A code to trap with that indicates a host call error. -const TRAP_USER_CODE: u16 = 0; - -/// The only Wasm types allowed in host function signatures (I32, I64, F32, F64) are all -/// represented in at most 8 bytes. -const MAX_WASM_TYPE_SIZE: usize = 8; - -/// The top-level host state of the "env" module. This state is used by the trampoline function to -/// construct a `FunctionExecutor` which can execute the host call. -pub struct EnvState { - host_functions: Vec<&'static dyn Function>, - compiler: Compiler, - // The code memory must be kept around on the state to prevent it from being dropped. - #[allow(dead_code)] - code_memory: CodeMemory, - trap: Option, - /// The executor state stored across host calls during a single Wasm runtime call. - /// During a runtime call, this MUST be `Some`. - pub executor_state: Option, -} - -impl EnvState { - /// Construct a new `EnvState` which owns the given code memory. - pub fn new( - code_memory: CodeMemory, - compiler: Compiler, - host_functions: &[&'static dyn Function], - ) -> Self { - EnvState { - trap: None, - compiler, - code_memory, - executor_state: None, - host_functions: host_functions.to_vec(), - } - } - - /// Resets the trap error to None and returns the current value. - pub fn take_trap(&mut self) -> Option { - self.trap.take() - } -} - -/// This is called by the dynamically generated trampoline taking the function index and reference -/// to the call arguments on the stack as arguments. Returns zero on success and a non-zero value -/// on failure. -unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: *mut i64) -> u32 { - if let Some(state) = (*vmctx).host_state().downcast_mut::() { - match stub_fn_inner( - vmctx, - &state.host_functions, - &mut state.compiler, - state.executor_state.as_mut(), - func_index, - values_vec, - ) { - Ok(()) => CALL_SUCCESS, - Err(err) => { - state.trap = Some(err); - CALL_FAILED_WITH_ERROR - } - } - } else { - // Well, we can't even set a trap message, so we'll just exit without one. - CALL_WITH_BAD_HOST_STATE - } -} - -/// Implements most of the logic in `stub_fn` but returning a `Result` instead of an integer error -/// for the sake of readability. -unsafe fn stub_fn_inner( - vmctx: *mut VMContext, - externals: &[&dyn Function], - compiler: &mut Compiler, - executor_state: Option<&mut FunctionExecutorState>, - func_index: u32, - values_vec: *mut i64, -) -> Result<(), Error> { - let func = externals.get(func_index as usize) - .ok_or_else(|| format!("call to undefined external function with index {}", func_index))?; - let executor_state = executor_state - .ok_or_else(|| "executor state is None during call to external function")?; - - // Build the external function context. - let mut context = FunctionExecutor::new(vmctx, compiler, executor_state)?; - let mut context = AssertUnwindSafe(&mut context); - - // Execute and write output back to the stack. - let return_val = panic::catch_unwind(move || { - let signature = func.signature(); - - // Read the arguments from the stack. - let mut args = signature.args.iter() - .enumerate() - .map(|(i, ¶m_type)| read_value_from(values_vec.offset(i as isize), param_type)); - - func.execute(&mut **context, &mut args) - }); - - match return_val { - Ok(ret_val) => { - if let Some(val) = ret_val - .map_err(|e| Error::FunctionExecution(func.name().to_string(), e))? { - write_value_to(values_vec, val); - } - - Ok(()) - }, - Err(e) => { - let message = if let Some(err) = e.downcast_ref::() { - err.to_string() - } else if let Some(err) = e.downcast_ref::<&str>() { - err.to_string() - } else { - "Panicked without any further information!".into() - }; - - Err(Error::FunctionExecution(func.name().to_string(), message)) - } - } -} - -/// Create a trampoline for invoking a host function. -/// -/// The trampoline is a dynamically generated entry point to a runtime host call. The function is -/// generated by manually constructing Cranelift IR and using the Cranelift compiler. The -/// trampoline embeds the function index as a constant and delegates to a stub function in Rust, -/// which takes the function index and a memory reference to the stack arguments and return value -/// slots. -/// -/// This code is of modified copy of wasmtime's wasmtime-api/src/trampoline/func.rs. -pub fn make_trampoline( - isa: &dyn isa::TargetIsa, - code_memory: &mut CodeMemory, - fn_builder_ctx: &mut FunctionBuilderContext, - func_index: u32, - signature: &ir::Signature, -) -> Result<*const VMFunctionBody, WasmError> { - // Mostly reverse copy of the similar method from wasmtime's - // wasmtime-jit/src/compiler.rs. - let pointer_type = isa.pointer_type(); - let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv); - - // Ensure that the first parameter of the generated function is the `VMContext` pointer. - assert_eq!( - signature.params[0], - ir::AbiParam::special(pointer_type, ir::ArgumentPurpose::VMContext) - ); - - // Add the `vmctx` parameter. - stub_sig.params.push(ir::AbiParam::special( - pointer_type, - ir::ArgumentPurpose::VMContext, - )); - - // Add the `func_index` parameter. - stub_sig.params.push(ir::AbiParam::new(ir::types::I32)); - - // Add the `values_vec` parameter. - stub_sig.params.push(ir::AbiParam::new(pointer_type)); - - // Add error/trap return. - stub_sig.returns.push(ir::AbiParam::new(ir::types::I32)); - - // Each parameter and return value gets a 64-bit (8-byte) wide slot on the stack, as that is - // large enough to fit all Wasm primitive types that can be used in host function signatures. - // The `VMContext` pointer, which is a parameter of the function signature, is excluded as it - // is passed directly to the stub function rather than being looked up on the caller stack from - // the `values_vec` pointer. - let values_vec_len = cmp::max(signature.params.len() - 1, signature.returns.len()); - let values_vec_size = (MAX_WASM_TYPE_SIZE * values_vec_len) as u32; - - let mut context = Context::new(); - context.func = - ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone()); - - let ss = context.func.create_stack_slot(StackSlotData::new( - StackSlotKind::ExplicitSlot, - values_vec_size, - )); - - { - let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx); - let block0 = builder.create_ebb(); - - builder.append_ebb_params_for_function_params(block0); - builder.switch_to_block(block0); - builder.seal_block(block0); - - let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0); - let mflags = ir::MemFlags::trusted(); - for i in 1..signature.params.len() { - let val = builder.func.dfg.ebb_params(block0)[i]; - builder.ins().store( - mflags, - val, - values_vec_ptr_val, - ((i - 1) * MAX_WASM_TYPE_SIZE) as i32, - ); - } - - let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0]; - let func_index_val = builder.ins().iconst(ir::types::I32, func_index as i64); - - let callee_args = vec![vmctx_ptr_val, func_index_val, values_vec_ptr_val]; - - let new_sig = builder.import_signature(stub_sig.clone()); - - let callee_value = builder - .ins() - .iconst(pointer_type, stub_fn as *const VMFunctionBody as i64); - let call = builder - .ins() - .call_indirect(new_sig, callee_value, &callee_args); - - let call_result = builder.func.dfg.inst_results(call)[0]; - builder.ins().trapnz(call_result, TrapCode::User(TRAP_USER_CODE)); - - let mflags = ir::MemFlags::trusted(); - let mut results = Vec::new(); - for (i, r) in signature.returns.iter().enumerate() { - let load = builder.ins().load( - r.value_type, - mflags, - values_vec_ptr_val, - (i * MAX_WASM_TYPE_SIZE) as i32, - ); - results.push(load); - } - builder.ins().return_(&results); - builder.finalize() - } - - let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink; - let mut trap_sink = binemit::NullTrapSink {}; - let mut stackmap_sink = binemit::NullStackmapSink {}; - context - .compile_and_emit( - isa, - &mut code_buf, - &mut reloc_sink, - &mut trap_sink, - &mut stackmap_sink, - ) - .map_err(|e| { - WasmError::Instantiation(format!( - "failed to compile trampoline: {}", - pretty_error(&context.func, Some(isa), e) - )) - })?; - - let mut unwind_info = Vec::new(); - context.emit_unwind_info(isa, &mut unwind_info); - - let func_ref = code_memory - .allocate_for_function(&CompiledFunction { - body: code_buf, - jt_offsets: context.func.jt_offsets, - unwind_info, - }) - .map_err(|e| WasmError::Instantiation(format!("failed to allocate code memory: {}", e)))?; - - Ok(func_ref.as_ptr()) -} - -/// We don't expect trampoline compilation to produce any relocations, so -/// this `RelocSink` just asserts that it doesn't recieve any. -struct RelocSink; - -impl binemit::RelocSink for RelocSink { - fn reloc_ebb( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _ebb_offset: binemit::CodeOffset, - ) { - panic!("trampoline compilation should not produce ebb relocs"); - } - fn reloc_external( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _name: &ir::ExternalName, - _addend: binemit::Addend, - ) { - panic!("trampoline compilation should not produce external symbol relocs"); - } - fn reloc_constant( - &mut self, - _code_offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _constant_offset: ir::ConstantOffset, - ) { - panic!("trampoline compilation should not produce constant relocs"); - } - fn reloc_jt( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _jt: ir::JumpTable, - ) { - panic!("trampoline compilation should not produce jump table relocs"); - } -} - -unsafe fn write_value_to(p: *mut i64, val: Value) { - match val { - Value::I32(i) => ptr::write(p as *mut i32, i), - Value::I64(i) => ptr::write(p as *mut i64, i), - Value::F32(u) => ptr::write(p as *mut u32, u), - Value::F64(u) => ptr::write(p as *mut u64, u), - } -} - -unsafe fn read_value_from(p: *const i64, ty: ValueType) -> Value { - match ty { - ValueType::I32 => Value::I32(ptr::read(p as *const i32)), - ValueType::I64 => Value::I64(ptr::read(p as *const i64)), - ValueType::F32 => Value::F32(ptr::read(p as *const u32)), - ValueType::F64 => Value::F64(ptr::read(p as *const u64)), - } -} diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 551295911a9cfcc158f6a4f8bbe3910b0884f4c8..d2de95d4cc7150d6ed8768ad48181ae9f9885cb4 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,31 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use sc_executor_common::error::{Error, Result}; - -use cranelift_codegen::{ir, isa}; use std::ops::Range; -use sp_wasm_interface::{Pointer, Signature, ValueType}; - -/// Read data from a slice of memory into a destination buffer. -/// -/// Returns an error if the read would go out of the memory bounds. -pub fn read_memory_into(memory: &[u8], address: Pointer, dest: &mut [u8]) -> Result<()> { - let range = checked_range(address.into(), dest.len(), memory.len()) - .ok_or_else(|| Error::Other("memory read is out of bounds".into()))?; - dest.copy_from_slice(&memory[range]); - Ok(()) -} - -/// Write data to a slice of memory. -/// -/// Returns an error if the write would go out of the memory bounds. -pub fn write_memory_from(memory: &mut [u8], address: Pointer, data: &[u8]) -> Result<()> { - let range = checked_range(address.into(), data.len(), memory.len()) - .ok_or_else(|| Error::Other("memory write is out of bounds".into()))?; - &mut memory[range].copy_from_slice(data); - Ok(()) -} /// Construct a range from an offset to a data length after the offset. /// Returns None if the end of the range would exceed some maximum offset. @@ -50,64 +26,3 @@ pub fn checked_range(offset: usize, len: usize, max: usize) -> Option ir::Signature { - ir::Signature { - params: signature.args.iter() - .map(cranelift_ir_type) - .map(ir::AbiParam::new) - .collect(), - returns: signature.return_value.iter() - .map(cranelift_ir_type) - .map(ir::AbiParam::new) - .collect(), - call_conv: call_conv.clone(), - } -} - -/// Convert a wasm_interface ValueType into a cranelift_codegen Type. -pub fn cranelift_ir_type(value_type: &ValueType) -> ir::types::Type { - match value_type { - ValueType::I32 => ir::types::I32, - ValueType::I64 => ir::types::I64, - ValueType::F32 => ir::types::F32, - ValueType::F64 => ir::types::F64, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use assert_matches::assert_matches; - - #[test] - fn test_read_memory_into() { - let mut memory = [0; 20]; - let mut dest = [0; 5]; - - &mut memory[15..20].copy_from_slice(b"hello"); - - read_memory_into(&memory[..], Pointer::new(15), &mut dest[..]).unwrap(); - - // Test that out of bounds read fails. - assert_matches!( - read_memory_into(&memory[..], Pointer::new(16), &mut dest[..]), - Err(Error::Other(_)) - ) - } - - #[test] - fn test_write_memory_from() { - let mut memory = [0; 20]; - let data = b"hello"; - - write_memory_from(&mut memory[..], Pointer::new(15), data).unwrap(); - - // Test that out of bounds write fails. - assert_matches!( - write_memory_from(&mut memory[..], Pointer::new(16), data), - Err(Error::Other(_)) - ) - } -} diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index 2c0a10857c7f1524369464d78cf2a36a21db8300..e96792258a279cd507f2ecba583925e5742af9b3 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -3,15 +3,16 @@ name = "sc-finality-grandpa" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] fork-tree = { version = "2.0.0", path = "../../utils/fork-tree" } -futures = "0.1.29" -futures03 = { package = "futures", version = "0.3.1", features = ["compat"] } -futures-timer = "2.0.2" +futures = "0.3.1" +futures-timer = "3.0.1" log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" rand = "0.7.2" +assert_matches = "1.3.0" parity-scale-codec = { version = "1.0.0", features = ["derive"] } sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" } sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } @@ -28,10 +29,11 @@ sc-network = { version = "0.8", path = "../network" } sc-network-gossip = { version = "0.8", path = "../network-gossip" } sp-finality-tracker = { version = "2.0.0", path = "../../primitives/finality-tracker" } sp-finality-grandpa = { version = "2.0.0", path = "../../primitives/finality-grandpa" } -finality-grandpa = { version = "0.10.3", features = ["derive-codec"] } +finality-grandpa = { version = "0.11.1", features = ["derive-codec"] } +pin-project = "0.4.6" [dev-dependencies] -finality-grandpa = { version = "0.10.3", features = ["derive-codec", "test-helpers"] } +finality-grandpa = { version = "0.11.1", features = ["derive-codec", "test-helpers"] } sc-network = { version = "0.8", path = "../network" } sc-network-test = { version = "0.8.0", path = "../network/test" } sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } @@ -42,3 +44,4 @@ env_logger = "0.7.0" tokio = "0.1.22" tempfile = "3.1.0" sp-api = { version = "2.0.0", path = "../../primitives/api" } +futures01 = { package = "futures", version = "0.1.29" } diff --git a/client/finality-grandpa/src/communication/gossip.rs b/client/finality-grandpa/src/communication/gossip.rs index 7b21c1d0797d315ac49acd951d223d2c48b49617..7fef47867f0df1f722abfcd445045d6ff83a9fb7 100644 --- a/client/finality-grandpa/src/communication/gossip.rs +++ b/client/finality-grandpa/src/communication/gossip.rs @@ -90,8 +90,7 @@ use sp_finality_grandpa::AuthorityId; use sc_telemetry::{telemetry, CONSENSUS_DEBUG}; use log::{trace, debug}; -use futures::prelude::*; -use futures03::channel::mpsc; +use futures::channel::mpsc; use rand::seq::SliceRandom; use crate::{environment, CatchUp, CompactCommit, SignedMessage}; diff --git a/client/finality-grandpa/src/communication/mod.rs b/client/finality-grandpa/src/communication/mod.rs index 18cb14c7396367b06fc54cfc4cfc5f3f98835ed8..540923c1b1b89a94833f63fd4b9bc7e7f400776b 100644 --- a/client/finality-grandpa/src/communication/mod.rs +++ b/client/finality-grandpa/src/communication/mod.rs @@ -27,16 +27,10 @@ //! In the future, there will be a fallback for allowing sending the same message //! under certain conditions that are used to un-stick the protocol. -use futures::{prelude::*, sync::mpsc}; -use futures03::{ - channel::mpsc as mpsc03, - compat::Compat, - future::{Future as Future03}, - stream::StreamExt, -}; +use futures::{prelude::*, channel::mpsc}; use log::{debug, trace}; use parking_lot::Mutex; -use std::{pin::Pin, sync::Arc, task::{Context, Poll as Poll03}}; +use std::{pin::Pin, sync::Arc, task::{Context, Poll}}; use finality_grandpa::Message::{Prevote, Precommit, PrimaryPropose}; use finality_grandpa::{voter, voter_set::VoterSet}; @@ -48,8 +42,8 @@ use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Numb use sc_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; use crate::{ - CatchUp, Commit, CommunicationIn, CommunicationOut, CompactCommit, Error, - Message, SignedMessage, + CatchUp, Commit, CommunicationIn, CommunicationOutH, + CompactCommit, Error, Message, SignedMessage, }; use crate::environment::HasVoted; use gossip::{ @@ -68,7 +62,7 @@ pub mod gossip; mod periodic; #[cfg(test)] -mod tests; +pub(crate) mod tests; pub use sp_finality_grandpa::GRANDPA_ENGINE_ID; @@ -170,7 +164,7 @@ pub(crate) struct NetworkBridge> { // thus one has to wrap gossip_validator_report_stream with an `Arc` `Mutex`. Given that it is // just an `UnboundedReceiver`, one could also switch to a multi-producer-*multi*-consumer // channel implementation. - gossip_validator_report_stream: Arc>>, + gossip_validator_report_stream: Arc>>, } impl> Unpin for NetworkBridge {} @@ -184,7 +178,6 @@ impl> NetworkBridge { service: N, config: crate::Config, set_state: crate::environment::SharedVoterSetState, - executor: &impl futures03::task::Spawn, ) -> Self { let (validator, report_stream) = GossipValidator::new( config, @@ -192,7 +185,7 @@ impl> NetworkBridge { ); let validator = Arc::new(validator); - let gossip_engine = GossipEngine::new(service.clone(), executor, GRANDPA_ENGINE_ID, validator.clone()); + let gossip_engine = GossipEngine::new(service.clone(), GRANDPA_ENGINE_ID, validator.clone()); { // register all previous votes with the gossip service so that they're @@ -276,8 +269,8 @@ impl> NetworkBridge { local_key: Option, has_voted: HasVoted, ) -> ( - impl Stream,Error=Error>, - impl Sink,SinkError=Error>, + impl Stream> + Unpin, + OutgoingMessages, ) { self.note_round( round, @@ -295,22 +288,20 @@ impl> NetworkBridge { }); let topic = round_topic::(round.0, set_id.0); - let incoming = Compat::new(self.gossip_engine.messages_for(topic) - .map(|item| Ok::<_, ()>(item))) - .filter_map(|notification| { + let incoming = self.gossip_engine.messages_for(topic) + .filter_map(move |notification| { let decoded = GossipMessage::::decode(&mut ¬ification.message[..]); - if let Err(ref e) = decoded { - debug!(target: "afg", "Skipping malformed message {:?}: {}", notification, e); - } - decoded.ok() - }) - .and_then(move |msg| { - match msg { - GossipMessage::Vote(msg) => { + + match decoded { + Err(ref e) => { + debug!(target: "afg", "Skipping malformed message {:?}: {}", notification, e); + return future::ready(None); + } + Ok(GossipMessage::Vote(msg)) => { // check signature. if !voters.contains_key(&msg.message.id) { debug!(target: "afg", "Skipping message from unknown voter {}", msg.message.id); - return Ok(None); + return future::ready(None); } if voters.len() <= TELEMETRY_VOTERS_LIMIT { @@ -339,18 +330,16 @@ impl> NetworkBridge { }; } - Ok(Some(msg.message)) + future::ready(Some(msg.message)) } _ => { debug!(target: "afg", "Skipping unknown message type"); - return Ok(None); + return future::ready(None); } } - }) - .filter_map(|x| x) - .map_err(|()| Error::Network(format!("Failed to receive message on unbounded stream"))); + }); - let (tx, out_rx) = mpsc::unbounded(); + let (tx, out_rx) = mpsc::channel(0); let outgoing = OutgoingMessages:: { round: round.0, set_id: set_id.0, @@ -360,14 +349,10 @@ impl> NetworkBridge { has_voted, }; - let out_rx = out_rx.map_err(move |()| Error::Network( - format!("Failed to receive on unbounded receiver for round {}", round.0) - )); - // Combine incoming votes from external GRANDPA nodes with outgoing // votes from our own GRANDPA voter to have a single // vote-import-pipeline. - let incoming = incoming.select(out_rx); + let incoming = stream::select(incoming, out_rx); (incoming, outgoing) } @@ -379,8 +364,8 @@ impl> NetworkBridge { voters: Arc>, is_voter: bool, ) -> ( - impl Stream, Error = Error>, - impl Sink, SinkError = Error>, + impl Stream>, + impl Sink, Error = Error> + Unpin, ) { self.validator.note_set( set_id, @@ -388,10 +373,9 @@ impl> NetworkBridge { |to, neighbor| self.neighbor_sender.send(to, neighbor), ); - let service = self.gossip_engine.clone(); let topic = global_topic::(set_id.0); let incoming = incoming_global( - service, + self.gossip_engine.clone(), topic, voters, self.validator.clone(), @@ -408,7 +392,7 @@ impl> NetworkBridge { let outgoing = outgoing.with(|out| { let voter::CommunicationOut::Commit(round, commit) = out; - Ok((round, commit)) + future::ok((round, commit)) }); (incoming, outgoing) @@ -430,35 +414,41 @@ impl> NetworkBridge { } } -impl> Future03 for NetworkBridge { +impl> Future for NetworkBridge { type Output = Result<(), Error>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll03 { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { loop { match self.neighbor_packet_worker.lock().poll_next_unpin(cx) { - Poll03::Ready(Some((to, packet))) => { + Poll::Ready(Some((to, packet))) => { self.gossip_engine.send_message(to, packet.encode()); }, - Poll03::Ready(None) => return Poll03::Ready( + Poll::Ready(None) => return Poll::Ready( Err(Error::Network("Neighbor packet worker stream closed.".into())) ), - Poll03::Pending => break, + Poll::Pending => break, } } loop { match self.gossip_validator_report_stream.lock().poll_next_unpin(cx) { - Poll03::Ready(Some(PeerReport { who, cost_benefit })) => { + Poll::Ready(Some(PeerReport { who, cost_benefit })) => { self.gossip_engine.report(who, cost_benefit); }, - Poll03::Ready(None) => return Poll03::Ready( + Poll::Ready(None) => return Poll::Ready( Err(Error::Network("Gossip validator report stream closed.".into())) ), - Poll03::Pending => break, + Poll::Pending => break, } } - Poll03::Pending + match self.gossip_engine.poll_unpin(cx) { + // The gossip engine future finished. We should do the same. + Poll::Ready(()) => return Poll::Ready(Ok(())), + Poll::Pending => {}, + } + + Poll::Pending } } @@ -468,7 +458,7 @@ fn incoming_global( voters: Arc>, gossip_validator: Arc>, neighbor_sender: periodic::NeighborPacketSender, -) -> impl Stream, Error = Error> { +) -> impl Stream> { let process_commit = move | msg: FullCommitMessage, mut notification: sc_network_gossip::TopicNotification, @@ -571,29 +561,27 @@ fn incoming_global( Some(voter::CommunicationIn::CatchUp(msg.message, cb)) }; - Compat::new(gossip_engine.messages_for(topic) - .map(|m| Ok::<_, ()>(m))) + gossip_engine.messages_for(topic) .filter_map(|notification| { // this could be optimized by decoding piecewise. let decoded = GossipMessage::::decode(&mut ¬ification.message[..]); if let Err(ref e) = decoded { trace!(target: "afg", "Skipping malformed commit message {:?}: {}", notification, e); } - decoded.map(move |d| (notification, d)).ok() + future::ready(decoded.map(move |d| (notification, d)).ok()) }) .filter_map(move |(notification, msg)| { - match msg { + future::ready(match msg { GossipMessage::Commit(msg) => process_commit(msg, notification, &mut gossip_engine, &gossip_validator, &*voters), GossipMessage::CatchUp(msg) => process_catch_up(msg, notification, &mut gossip_engine, &gossip_validator, &*voters), _ => { debug!(target: "afg", "Skipping unknown message type"); - return None; + None } - } + }) }) - .map_err(|()| Error::Network(format!("Failed to receive message on unbounded stream"))) } impl> Clone for NetworkBridge { @@ -690,21 +678,29 @@ pub(crate) fn check_message_sig_with_buffer( /// use the same raw message and key to sign. This is currently true for /// `ed25519` and `BLS` signatures (which we might use in the future), care must /// be taken when switching to different key types. -struct OutgoingMessages { +pub(crate) struct OutgoingMessages { round: RoundNumber, set_id: SetIdNumber, locals: Option<(AuthorityPair, AuthorityId)>, - sender: mpsc::UnboundedSender>, + sender: mpsc::Sender>, network: GossipEngine, has_voted: HasVoted, } -impl Sink for OutgoingMessages +impl Unpin for OutgoingMessages {} + +impl Sink> for OutgoingMessages { - type SinkItem = Message; - type SinkError = Error; + type Error = Error; + + fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Sink::poll_ready(Pin::new(&mut self.sender), cx) + .map(|elem| { elem.map_err(|e| { + Error::Network(format!("Failed to poll_ready channel sender: {:?}", e)) + })}) + } - fn start_send(&mut self, mut msg: Message) -> StartSend, Error> { + fn start_send(mut self: Pin<&mut Self>, mut msg: Message) -> Result<(), Self::Error> { // if we've voted on this round previously under the same key, send that vote instead match &mut msg { finality_grandpa::Message::PrimaryPropose(ref mut vote) => @@ -760,17 +756,23 @@ impl Sink for OutgoingMessages self.network.gossip_message(topic, message.encode(), false); // forward the message to the inner sender. - let _ = self.sender.unbounded_send(signed); - } + return self.sender.start_send(signed).map_err(|e| { + Error::Network(format!("Failed to start_send on channel sender: {:?}", e)) + }); + }; - Ok(AsyncSink::Ready) + Ok(()) } - fn poll_complete(&mut self) -> Poll<(), Error> { Ok(Async::Ready(())) } + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } - fn close(&mut self) -> Poll<(), Error> { - // ignore errors since we allow this inner sender to be closed already. - self.sender.close().or_else(|_| Ok(Async::Ready(()))) + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Sink::poll_close(Pin::new(&mut self.sender), cx) + .map(|elem| { elem.map_err(|e| { + Error::Network(format!("Failed to poll_close channel sender: {:?}", e)) + })}) } } @@ -978,13 +980,16 @@ impl CommitsOut { } } -impl Sink for CommitsOut { - type SinkItem = (RoundNumber, Commit); - type SinkError = Error; +impl Sink<(RoundNumber, Commit)> for CommitsOut { + type Error = Error; + + fn poll_ready(self: Pin<&mut Self>, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } - fn start_send(&mut self, input: (RoundNumber, Commit)) -> StartSend { + fn start_send(self: Pin<&mut Self>, input: (RoundNumber, Commit)) -> Result<(), Self::Error> { if !self.is_voter { - return Ok(AsyncSink::Ready); + return Ok(()); } let (round, commit) = input; @@ -1020,9 +1025,14 @@ impl Sink for CommitsOut { ); self.network.gossip_message(topic, message.encode(), false); - Ok(AsyncSink::Ready) + Ok(()) } - fn close(&mut self) -> Poll<(), Error> { Ok(Async::Ready(())) } - fn poll_complete(&mut self) -> Poll<(), Error> { Ok(Async::Ready(())) } + fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } } diff --git a/client/finality-grandpa/src/communication/periodic.rs b/client/finality-grandpa/src/communication/periodic.rs index d5c8c1e0b856c0666f3b994f71f50ffe7dd4e222..f2e79e8f14486e5bb0951acfd701e44aaec5a28c 100644 --- a/client/finality-grandpa/src/communication/periodic.rs +++ b/client/finality-grandpa/src/communication/periodic.rs @@ -17,9 +17,9 @@ //! Periodic rebroadcast of neighbor packets. use futures_timer::Delay; -use futures03::{channel::mpsc, future::{FutureExt as _}, prelude::*, ready, stream::Stream}; +use futures::{channel::mpsc, future::{FutureExt as _}, prelude::*, ready, stream::Stream}; use log::debug; -use std::{pin::Pin, task::{Context, Poll}, time::{Instant, Duration}}; +use std::{pin::Pin, task::{Context, Poll}, time::Duration}; use sc_network::PeerId; use sp_runtime::traits::{NumberFor, Block as BlockT}; @@ -28,10 +28,6 @@ use super::gossip::{NeighborPacket, GossipMessage}; // How often to rebroadcast, in cases where no new packets are created. const REBROADCAST_AFTER: Duration = Duration::from_secs(2 * 60); -fn rebroadcast_instant() -> Instant { - Instant::now() + REBROADCAST_AFTER -} - /// A sender used to send neighbor packets to a background job. #[derive(Clone)] pub(super) struct NeighborPacketSender( @@ -85,7 +81,7 @@ impl Stream for NeighborPacketWorker { match this.rx.poll_next_unpin(cx) { Poll::Ready(None) => return Poll::Ready(None), Poll::Ready(Some((to, packet))) => { - this.delay.reset(rebroadcast_instant()); + this.delay.reset(REBROADCAST_AFTER); this.last = Some((to.clone(), packet.clone())); return Poll::Ready(Some((to, GossipMessage::::from(packet.clone())))); @@ -98,7 +94,7 @@ impl Stream for NeighborPacketWorker { // Getting this far here implies that the timer fired. - this.delay.reset(rebroadcast_instant()); + this.delay.reset(REBROADCAST_AFTER); // Make sure the underlying task is scheduled for wake-up. // diff --git a/client/finality-grandpa/src/communication/tests.rs b/client/finality-grandpa/src/communication/tests.rs index c104af033926067711aad368d26cdd5ab8a3eeb1..040ee4c7bbd2257e6b188a07ee32b557ddd821e4 100644 --- a/client/finality-grandpa/src/communication/tests.rs +++ b/client/finality-grandpa/src/communication/tests.rs @@ -16,12 +16,11 @@ //! Tests for the communication portion of the GRANDPA crate. -use futures::sync::mpsc; +use futures::channel::mpsc; use futures::prelude::*; use sc_network::{Event as NetworkEvent, PeerId, config::Roles}; use sc_network_test::{Block, Hash}; use sc_network_gossip::Validator; -use tokio::runtime::current_thread; use std::sync::Arc; use sp_keyring::Ed25519Keyring; use parity_scale_codec::Encode; @@ -32,7 +31,8 @@ use sp_finality_grandpa::{AuthorityList, GRANDPA_ENGINE_ID}; use super::gossip::{self, GossipValidator}; use super::{AuthorityId, VoterSet, Round, SetId}; -enum Event { +#[derive(Debug)] +pub(crate) enum Event { EventStream(mpsc::UnboundedSender), WriteNotification(sc_network::PeerId, Vec), Report(sc_network::PeerId, sc_network::ReputationChange), @@ -40,15 +40,23 @@ enum Event { } #[derive(Clone)] -struct TestNetwork { +pub(crate) struct TestNetwork { sender: mpsc::UnboundedSender, } -impl sc_network_gossip::Network for TestNetwork { - fn event_stream(&self) -> Box + Send> { +impl TestNetwork { + fn event_stream_03(&self) -> Pin + Send>> { let (tx, rx) = mpsc::unbounded(); let _ = self.sender.unbounded_send(Event::EventStream(tx)); - Box::new(rx) + Box::pin(rx) + } +} + +impl sc_network_gossip::Network for TestNetwork { + fn event_stream(&self) -> Box + Send> { + Box::new( + self.event_stream_03().map(Ok::<_, ()>).compat() + ) } fn report_peer(&self, who: sc_network::PeerId, cost_benefit: sc_network::ReputationChange) { @@ -94,27 +102,35 @@ impl sc_network_gossip::ValidatorContext for TestNetwork { fn send_topic(&mut self, _: &sc_network::PeerId, _: Hash, _: bool) { } } -struct Tester { - net_handle: super::NetworkBridge, +pub(crate) struct Tester { + pub(crate) net_handle: super::NetworkBridge, gossip_validator: Arc>, - events: mpsc::UnboundedReceiver, + pub(crate) events: mpsc::UnboundedReceiver, } impl Tester { - fn filter_network_events(self, mut pred: F) -> impl Future + fn filter_network_events(self, mut pred: F) -> impl Future where F: FnMut(Event) -> bool { let mut s = Some(self); - futures::future::poll_fn(move || loop { - match s.as_mut().unwrap().events.poll().expect("concluded early") { - Async::Ready(None) => panic!("concluded early"), - Async::Ready(Some(item)) => if pred(item) { - return Ok(Async::Ready(s.take().unwrap())) + futures::future::poll_fn(move |cx| loop { + match Stream::poll_next(Pin::new(&mut s.as_mut().unwrap().events), cx) { + Poll::Ready(None) => panic!("concluded early"), + Poll::Ready(Some(item)) => if pred(item) { + return Poll::Ready(s.take().unwrap()) }, - Async::NotReady => return Ok(Async::NotReady), + Poll::Pending => return Poll::Pending, } }) } + + pub(crate) fn trigger_gossip_validator_reputation_change(&self, p: &PeerId) { + self.gossip_validator.validate( + &mut crate::communication::tests::NoopContext, + p, + &vec![1, 2, 3], + ); + } } // some random config (not really needed) @@ -149,8 +165,8 @@ fn voter_set_state() -> SharedVoterSetState { } // needs to run in a tokio runtime. -fn make_test_network(executor: &impl futures03::task::Spawn) -> ( - impl Future, +pub(crate) fn make_test_network() -> ( + impl Future, TestNetwork, ) { let (tx, rx) = mpsc::unbounded(); @@ -159,7 +175,7 @@ fn make_test_network(executor: &impl futures03::task::Spawn) -> ( #[derive(Clone)] struct Exit; - impl futures03::Future for Exit { + impl futures::Future for Exit { type Output = (); fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<()> { @@ -171,11 +187,10 @@ fn make_test_network(executor: &impl futures03::task::Spawn) -> ( net.clone(), config(), voter_set_state(), - executor, ); ( - futures::future::ok(Tester { + futures::future::ready(Tester { gossip_validator: bridge.validator.clone(), net_handle: bridge, events: rx, @@ -245,14 +260,13 @@ fn good_commit_leads_to_relay() { let id = sc_network::PeerId::random(); let global_topic = super::global_topic::(set_id); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); - let test = make_test_network(&threads_pool).0 - .and_then(move |tester| { + let test = make_test_network().0 + .then(move |tester| { // register a peer. tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::FULL); - Ok((tester, id)) + future::ready((tester, id)) }) - .and_then(move |(tester, id)| { + .then(move |(tester, id)| { // start round, dispatch commit, and wait for broadcast. let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false); @@ -265,6 +279,7 @@ fn good_commit_leads_to_relay() { } let commit_to_send = encoded_commit.clone(); + let network_bridge = tester.net_handle.clone(); // asking for global communication will cause the test network // to send us an event asking us for a stream. use it to @@ -305,12 +320,11 @@ fn good_commit_leads_to_relay() { }, _ => panic!("commit expected"), } - }) - .map_err(|_| panic!("could not process commit")); + }); // once the message is sent and commit is "handled" we should have // a repropagation event coming from the network. - send_message.join(handle_commit).and_then(move |(tester, ())| { + let fut = future::join(send_message, handle_commit).then(move |(tester, ())| { tester.filter_network_events(move |event| match event { Event::WriteNotification(_, data) => { data == encoded_commit @@ -318,11 +332,14 @@ fn good_commit_leads_to_relay() { _ => false, }) }) - .map_err(|_| panic!("could not watch for gossip message")) - .map(|_| ()) + .map(|_| ()); + + // Poll both the future sending and handling the commit, as well as the underlying + // NetworkBridge. Complete once the former completes. + future::select(fut, network_bridge) }); - current_thread::Runtime::new().unwrap().block_on(test).unwrap(); + futures::executor::block_on(test); } #[test] @@ -371,14 +388,13 @@ fn bad_commit_leads_to_report() { let id = sc_network::PeerId::random(); let global_topic = super::global_topic::(set_id); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); - let test = make_test_network(&threads_pool).0 - .and_then(move |tester| { + let test = make_test_network().0 + .map(move |tester| { // register a peer. tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::FULL); - Ok((tester, id)) + (tester, id) }) - .and_then(move |(tester, id)| { + .then(move |(tester, id)| { // start round, dispatch commit, and wait for broadcast. let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false); @@ -391,6 +407,7 @@ fn bad_commit_leads_to_report() { } let commit_to_send = encoded_commit.clone(); + let network_bridge = tester.net_handle.clone(); // asking for global communication will cause the test network // to send us an event asking us for a stream. use it to @@ -413,7 +430,7 @@ fn bad_commit_leads_to_report() { _ => false, }); - // when the commit comes in, we'll tell the callback it was good. + // when the commit comes in, we'll tell the callback it was bad. let handle_commit = commits_in.into_future() .map(|(item, _)| { match item.unwrap() { @@ -422,12 +439,11 @@ fn bad_commit_leads_to_report() { }, _ => panic!("commit expected"), } - }) - .map_err(|_| panic!("could not process commit")); + }); // once the message is sent and commit is "handled" we should have // a report event coming from the network. - send_message.join(handle_commit).and_then(move |(tester, ())| { + let fut = future::join(send_message, handle_commit).then(move |(tester, ())| { tester.filter_network_events(move |event| match event { Event::Report(who, cost_benefit) => { who == id && cost_benefit == super::cost::INVALID_COMMIT @@ -435,26 +451,28 @@ fn bad_commit_leads_to_report() { _ => false, }) }) - .map_err(|_| panic!("could not watch for peer report")) - .map(|_| ()) + .map(|_| ()); + + // Poll both the future sending and handling the commit, as well as the underlying + // NetworkBridge. Complete once the former completes. + future::select(fut, network_bridge) }); - current_thread::Runtime::new().unwrap().block_on(test).unwrap(); + futures::executor::block_on(test); } #[test] fn peer_with_higher_view_leads_to_catch_up_request() { let id = sc_network::PeerId::random(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); - let (tester, mut net) = make_test_network(&threads_pool); + let (tester, mut net) = make_test_network(); let test = tester - .and_then(move |tester| { + .map(move |tester| { // register a peer with authority role. tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::AUTHORITY); - Ok((tester, id)) + ((tester, id)) }) - .and_then(move |(tester, id)| { + .then(move |(tester, id)| { // send neighbor message at round 10 and height 50 let result = tester.gossip_validator.validate( &mut net, @@ -494,9 +512,8 @@ fn peer_with_higher_view_leads_to_catch_up_request() { }, _ => false, }) - .map_err(|_| panic!("could not watch for peer send message")) .map(|_| ()) }); - current_thread::Runtime::new().unwrap().block_on(test).unwrap(); + futures::executor::block_on(test); } diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index 372229001dd196991a09bd8f697f1664dd5d2856..fd88113776c9c02c9feca3292e096e3958d50693 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -16,13 +16,13 @@ use std::collections::BTreeMap; use std::iter::FromIterator; +use std::pin::Pin; use std::sync::Arc; use std::time::Duration; use log::{debug, warn, info}; use parity_scale_codec::{Decode, Encode}; use futures::prelude::*; -use futures03::future::{FutureExt as _, TryFutureExt as _}; use futures_timer::Delay; use parking_lot::RwLock; use sp_blockchain::{HeaderBackend, Error as ClientError}; @@ -564,19 +564,18 @@ where NumberFor: BlockNumberOps, Client: AuxStore, { - type Timer = Box + Send>; + type Timer = Pin> + Send>>; type Id = AuthorityId; type Signature = AuthoritySignature; // regular round message streams - type In = Box, Self::Signature, Self::Id>, + type In = Pin, Self::Signature, Self::Id>, Self::Error> + > + Send>>; + type Out = Pin>, Error = Self::Error, - > + Send>; - type Out = Box>, - SinkError = Self::Error, - > + Send>; + > + Send>>; type Error = CommandOrError>; @@ -610,7 +609,7 @@ where // schedule incoming messages from the network to be held until // corresponding blocks are imported. - let incoming = Box::new(UntilVoteTargetImported::new( + let incoming = Box::pin(UntilVoteTargetImported::new( self.client.import_notification_stream(), self.network.clone(), self.client.clone(), @@ -619,12 +618,12 @@ where ).map_err(Into::into)); // schedule network message cleanup when sink drops. - let outgoing = Box::new(outgoing.sink_map_err(Into::into)); + let outgoing = Box::pin(outgoing.sink_err_into()); voter::RoundData { voter_id: local_key.map(|pair| pair.public()), - prevote_timer: Box::new(prevote_timer.map(Ok).compat()), - precommit_timer: Box::new(precommit_timer.map(Ok).compat()), + prevote_timer: Box::pin(prevote_timer.map(Ok)), + precommit_timer: Box::pin(precommit_timer.map(Ok)), incoming, outgoing, } @@ -898,7 +897,7 @@ where //random between 0-1 seconds. let delay: u64 = thread_rng().gen_range(0, 1000); - Box::new(Delay::new(Duration::from_millis(delay)).map(Ok).compat()) + Box::pin(Delay::new(Duration::from_millis(delay)).map(Ok)) } fn prevote_equivocation( diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index ad1b2b1a87fb6ad1b2995116baffdf9a0befb142..64fc62bfe7a8bd4d46c94fd2c0904e5b862ad989 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -18,7 +18,7 @@ use std::{sync::Arc, collections::HashMap}; use log::{debug, trace, info}; use parity_scale_codec::Encode; -use futures::sync::mpsc; +use futures::channel::mpsc; use parking_lot::RwLockWriteGuard; use sp_blockchain::{HeaderBackend, BlockStatus, well_known_cache_keys}; diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 071214961f9a14917b1d0e8e421da3a6ec843baa..45a24002269515db31369174baf8525cd0cbf391 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -53,9 +53,9 @@ //! included in the newly-finalized chain. use futures::prelude::*; -use futures03::{StreamExt, future::ready}; -use log::{debug, error, info}; -use futures::sync::mpsc; +use futures::StreamExt; +use log::{debug, info}; +use futures::channel::mpsc; use sc_client_api::{BlockchainEvents, CallExecutor, backend::{AuxStore, Backend}, ExecutionStrategy}; use sp_blockchain::{HeaderBackend, Error as ClientError}; use sc_client::Client; @@ -66,7 +66,7 @@ use sc_keystore::KeyStorePtr; use sp_inherents::InherentDataProviders; use sp_consensus::SelectChain; use sp_core::Pair; -use sc_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG, CONSENSUS_WARN}; +use sc_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG}; use serde_json; use sp_finality_tracker; @@ -77,6 +77,8 @@ use finality_grandpa::{voter, BlockNumberOps, voter_set::VoterSet}; use std::{fmt, io}; use std::sync::Arc; use std::time::Duration; +use std::pin::Pin; +use std::task::{Poll, Context}; mod authorities; mod aux_schema; @@ -169,15 +171,6 @@ type CommunicationInH = finality_grandpa::voter::CommunicationIn< AuthorityId, >; -/// A global communication sink for commits. Not exposed publicly, used -/// internally to simplify types in the communication layer. -type CommunicationOut = finality_grandpa::voter::CommunicationOut< - ::Hash, - NumberFor, - AuthoritySignature, - AuthorityId, ->; - /// Global communication sink for commits with the hash type not being derived /// from the block, useful for forcing the hash to some type (e.g. `H256`) when /// the compiler can't do the inference. @@ -456,13 +449,12 @@ fn global_communication( keystore: &Option, ) -> ( impl Stream< - Item = CommunicationInH, - Error = CommandOrError>, + Item = Result, CommandOrError>>, >, impl Sink< - SinkItem = CommunicationOutH, - SinkError = CommandOrError>, - >, + CommunicationOutH, + Error = CommandOrError>, + > + Unpin, ) where B: Backend, E: CallExecutor + Send + Sync, @@ -524,7 +516,7 @@ fn register_finality_tracker_inherent_data_provider( } /// Parameters used to run Grandpa. -pub struct GrandpaParams { +pub struct GrandpaParams { /// Configuration for the GRANDPA service. pub config: Config, /// A link to the block import worker. @@ -536,18 +528,16 @@ pub struct GrandpaParams { /// Handle to a future that will resolve on exit. pub on_exit: X, /// If supplied, can be used to hook on telemetry connection established events. - pub telemetry_on_connect: Option>, + pub telemetry_on_connect: Option>, /// A voting rule used to potentially restrict target votes. pub voting_rule: VR, - /// How to spawn background tasks. - pub executor: Sp, } /// Run a GRANDPA voter as a task. Provide configuration and a link to a /// block import worker that has already been instantiated with `block_import`. -pub fn run_grandpa_voter( - grandpa_params: GrandpaParams, -) -> sp_blockchain::Result + Send + 'static> where +pub fn run_grandpa_voter( + grandpa_params: GrandpaParams, +) -> sp_blockchain::Result + Unpin + Send + 'static> where Block::Hash: Ord, B: Backend + 'static, E: CallExecutor + Send + Sync + 'static, @@ -557,9 +547,8 @@ pub fn run_grandpa_voter( NumberFor: BlockNumberOps, DigestFor: Encode, RA: Send + Sync + 'static, - X: futures03::Future + Clone + Send + Unpin + 'static, + X: futures::Future + Clone + Send + Unpin + 'static, Client: AuxStore, - Sp: futures03::task::Spawn + 'static, { let GrandpaParams { config, @@ -569,7 +558,6 @@ pub fn run_grandpa_voter( on_exit, telemetry_on_connect, voting_rule, - executor, } = grandpa_params; let LinkHalf { @@ -583,7 +571,6 @@ pub fn run_grandpa_voter( network, config.clone(), persistent_data.set_state.clone(), - &executor, ); register_finality_tracker_inherent_data_provider(client.clone(), &inherent_data_providers)?; @@ -608,13 +595,11 @@ pub fn run_grandpa_voter( .expect("authorities is always at least an empty vector; elements are always of type string") } ); - ready(()) - }) - .unit_error() - .compat(); - futures::future::Either::A(events) + future::ready(()) + }); + future::Either::Left(events) } else { - futures::future::Either::B(futures::future::empty()) + future::Either::Right(future::pending()) }; let voter_work = VoterWork::new( @@ -628,28 +613,22 @@ pub fn run_grandpa_voter( ); let voter_work = voter_work - .map(|_| ()) - .map_err(|e| { - error!("GRANDPA Voter failed: {:?}", e); - telemetry!(CONSENSUS_WARN; "afg.voter_failed"; "e" => ?e); - }); + .map(|_| ()); // Make sure that `telemetry_task` doesn't accidentally finish and kill grandpa. let telemetry_task = telemetry_task - .then(|_| futures::future::empty::<(), ()>()); - - use futures03::{FutureExt, TryFutureExt}; + .then(|_| future::pending::<()>()); - Ok(voter_work.select(on_exit.map(Ok).compat()).select2(telemetry_task).then(|_| Ok(()))) + Ok(future::select(future::select(voter_work, on_exit), telemetry_task).map(drop)) } /// Future that powers the voter. #[must_use] struct VoterWork, RA, SC, VR> { - voter: Box>> + Send>, + voter: Pin>>> + Send>>, env: Arc>, voter_commands_rx: mpsc::UnboundedReceiver>>, - network: futures03::compat::Compat>, + network: NetworkBridge, } impl VoterWork @@ -691,10 +670,10 @@ where let mut work = VoterWork { // `voter` is set to a temporary value and replaced below when // calling `rebuild_voter`. - voter: Box::new(futures::empty()) as Box<_>, + voter: Box::pin(future::pending()), env, voter_commands_rx, - network: futures03::future::TryFutureExt::compat(network), + network, }; work.rebuild_voter(); work @@ -757,10 +736,10 @@ where last_finalized, ); - self.voter = Box::new(voter); + self.voter = Box::pin(voter); }, VoterSetState::Paused { .. } => - self.voter = Box::new(futures::empty()), + self.voter = Box::pin(future::pending()), }; } @@ -841,61 +820,47 @@ where VR: VotingRule> + Clone + 'static, Client: AuxStore, { - type Item = (); - type Error = Error; + type Output = Result<(), Error>; - fn poll(&mut self) -> Poll { - match self.voter.poll() { - Ok(Async::NotReady) => {} - Ok(Async::Ready(())) => { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match Future::poll(Pin::new(&mut self.voter), cx) { + Poll::Pending => {} + Poll::Ready(Ok(())) => { // voters don't conclude naturally - return Err(Error::Safety("GRANDPA voter has concluded.".into())) + return Poll::Ready(Err(Error::Safety("GRANDPA voter has concluded.".into()))) } - Err(CommandOrError::Error(e)) => { + Poll::Ready(Err(CommandOrError::Error(e))) => { // return inner observer error - return Err(e) + return Poll::Ready(Err(e)) } - Err(CommandOrError::VoterCommand(command)) => { + Poll::Ready(Err(CommandOrError::VoterCommand(command))) => { // some command issued internally self.handle_voter_command(command)?; - futures::task::current().notify(); + cx.waker().wake_by_ref(); } } - match self.voter_commands_rx.poll() { - Ok(Async::NotReady) => {} - Err(_) => { - // the `voter_commands_rx` stream should not fail. - return Ok(Async::Ready(())) - } - Ok(Async::Ready(None)) => { + match Stream::poll_next(Pin::new(&mut self.voter_commands_rx), cx) { + Poll::Pending => {} + Poll::Ready(None) => { // the `voter_commands_rx` stream should never conclude since it's never closed. - return Ok(Async::Ready(())) + return Poll::Ready(Ok(())) } - Ok(Async::Ready(Some(command))) => { + Poll::Ready(Some(command)) => { // some command issued externally self.handle_voter_command(command)?; - futures::task::current().notify(); + cx.waker().wake_by_ref(); } } - match self.network.poll() { - Ok(Async::NotReady) => {}, - Ok(Async::Ready(())) => { - // the network bridge future should never conclude. - return Ok(Async::Ready(())) - } - e @ Err(_) => return e, - }; - - Ok(Async::NotReady) + Future::poll(Pin::new(&mut self.network), cx) } } #[deprecated(since = "1.1.0", note = "Please switch to run_grandpa_voter.")] -pub fn run_grandpa( - grandpa_params: GrandpaParams, -) -> sp_blockchain::Result + Send + 'static> where +pub fn run_grandpa( + grandpa_params: GrandpaParams, +) -> sp_blockchain::Result + Send + 'static> where Block::Hash: Ord, B: Backend + 'static, E: CallExecutor + Send + Sync + 'static, @@ -905,9 +870,8 @@ pub fn run_grandpa( DigestFor: Encode, RA: Send + Sync + 'static, VR: VotingRule> + Clone + 'static, - X: futures03::Future + Clone + Send + Unpin + 'static, + X: futures::Future + Clone + Send + Unpin + 'static, Client: AuxStore, - Sp: futures03::task::Spawn + 'static, { run_grandpa_voter(grandpa_params) } diff --git a/client/finality-grandpa/src/light_import.rs b/client/finality-grandpa/src/light_import.rs index 0da22487789220c03ef201dfcf0c05be35d75d52..4ae35167be8ba00c3f3e4dd92ecd84eb947de422 100644 --- a/client/finality-grandpa/src/light_import.rs +++ b/client/finality-grandpa/src/light_import.rs @@ -697,7 +697,8 @@ pub mod tests { storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: true, import_existing: false, }; diff --git a/client/finality-grandpa/src/observer.rs b/client/finality-grandpa/src/observer.rs index 989a1e1655e8d1109724aa0fb013864bdeec5a99..ffe71d573a864899bfad7f86769dbda1ab28ca22 100644 --- a/client/finality-grandpa/src/observer.rs +++ b/client/finality-grandpa/src/observer.rs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +use std::pin::Pin; use std::sync::Arc; +use std::task::{Context, Poll}; -use futures::prelude::*; -use futures::{future, sync::mpsc}; +use futures::{prelude::*, channel::mpsc}; use finality_grandpa::{ BlockNumberOps, Error as GrandpaError, voter, voter_set::VoterSet @@ -64,14 +65,13 @@ fn grandpa_observer( last_finalized_number: NumberFor, commits: S, note_round: F, -) -> impl Future>> where +) -> impl Future>>> where NumberFor: BlockNumberOps, B: Backend, E: CallExecutor + Send + Sync + 'static, RA: Send + Sync, S: Stream< - Item = CommunicationIn, - Error = CommandOrError>, + Item = Result, CommandOrError>>, >, F: Fn(u64), { @@ -80,7 +80,7 @@ fn grandpa_observer( let client = client.clone(); let voters = voters.clone(); - let observer = commits.fold(last_finalized_number, move |last_finalized_number, global| { + let observer = commits.try_fold(last_finalized_number, move |last_finalized_number, global| { let (round, commit, callback) = match global { voter::CommunicationIn::Commit(round, commit, callback) => { let commit = finality_grandpa::Commit::from(commit); @@ -143,27 +143,25 @@ fn grandpa_observer( } }); - observer.map(|_| ()) + observer.map_ok(|_| ()) } /// Run a GRANDPA observer as a task, the observer will finalize blocks only by /// listening for and validating GRANDPA commits instead of following the full /// protocol. Provide configuration and a link to a block import worker that has /// already been instantiated with `block_import`. -pub fn run_grandpa_observer( +pub fn run_grandpa_observer( config: Config, link: LinkHalf, network: N, - on_exit: impl futures03::Future + Clone + Send + Unpin + 'static, - executor: Sp, -) -> sp_blockchain::Result + Send + 'static> where + on_exit: impl futures::Future + Clone + Send + Unpin + 'static, +) -> sp_blockchain::Result + Unpin + Send + 'static> where B: Backend + 'static, E: CallExecutor + Send + Sync + 'static, N: NetworkT + Send + Clone + 'static, SC: SelectChain + 'static, NumberFor: BlockNumberOps, RA: Send + Sync + 'static, - Sp: futures03::task::Spawn + 'static, Client: AuxStore, { let LinkHalf { @@ -177,7 +175,6 @@ pub fn run_grandpa_observer( network, config.clone(), persistent_data.set_state.clone(), - &executor, ); let observer_work = ObserverWork::new( @@ -189,20 +186,18 @@ pub fn run_grandpa_observer( ); let observer_work = observer_work - .map(|_| ()) + .map_ok(|_| ()) .map_err(|e| { warn!("GRANDPA Observer failed: {:?}", e); }); - use futures03::{FutureExt, TryFutureExt}; - - Ok(observer_work.select(on_exit.map(Ok).compat()).map(|_| ()).map_err(|_| ())) + Ok(future::select(observer_work, on_exit).map(drop)) } /// Future that powers the observer. #[must_use] struct ObserverWork, E, Backend, RA> { - observer: Box>> + Send>, + observer: Pin>>> + Send>>, client: Arc>, network: NetworkBridge, persistent_data: PersistentData, @@ -231,7 +226,7 @@ where let mut work = ObserverWork { // `observer` is set to a temporary value and replaced below when // calling `rebuild_observer`. - observer: Box::new(futures::empty()) as Box<_>, + observer: Box::pin(future::pending()) as Pin>, client, network, persistent_data, @@ -286,7 +281,7 @@ where note_round, ); - self.observer = Box::new(observer); + self.observer = Box::pin(observer); } fn handle_voter_command( @@ -336,44 +331,113 @@ where Bk: Backend + 'static, Client: AuxStore, { - type Item = (); - type Error = Error; + type Output = Result<(), Error>; - fn poll(&mut self) -> Poll { - match self.observer.poll() { - Ok(Async::NotReady) => {} - Ok(Async::Ready(())) => { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match Future::poll(Pin::new(&mut self.observer), cx) { + Poll::Pending => {} + Poll::Ready(Ok(())) => { // observer commit stream doesn't conclude naturally; this could reasonably be an error. - return Ok(Async::Ready(())) + return Poll::Ready(Ok(())) } - Err(CommandOrError::Error(e)) => { + Poll::Ready(Err(CommandOrError::Error(e))) => { // return inner observer error - return Err(e) + return Poll::Ready(Err(e)) } - Err(CommandOrError::VoterCommand(command)) => { + Poll::Ready(Err(CommandOrError::VoterCommand(command))) => { // some command issued internally self.handle_voter_command(command)?; - futures::task::current().notify(); + cx.waker().wake_by_ref(); } } - match self.voter_commands_rx.poll() { - Ok(Async::NotReady) => {} - Err(_) => { - // the `voter_commands_rx` stream should not fail. - return Ok(Async::Ready(())) - } - Ok(Async::Ready(None)) => { + match Stream::poll_next(Pin::new(&mut self.voter_commands_rx), cx) { + Poll::Pending => {} + Poll::Ready(None) => { // the `voter_commands_rx` stream should never conclude since it's never closed. - return Ok(Async::Ready(())) + return Poll::Ready(Ok(())) } - Ok(Async::Ready(Some(command))) => { + Poll::Ready(Some(command)) => { // some command issued externally self.handle_voter_command(command)?; - futures::task::current().notify(); + cx.waker().wake_by_ref(); } } - Ok(Async::NotReady) + Future::poll(Pin::new(&mut self.network), cx) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use assert_matches::assert_matches; + use crate::{aux_schema, communication::tests::{Event, make_test_network}}; + use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt}; + use sc_network::PeerId; + + use futures::executor::{self, ThreadPool}; + + /// Ensure `Future` implementation of `ObserverWork` is polling its `NetworkBridge`. Regression + /// test for bug introduced in d4fbb897c and fixed in b7af8b339. + /// + /// When polled, `NetworkBridge` forwards reputation change requests from the `GossipValidator` + /// to the underlying `dyn Network`. This test triggers a reputation change by calling + /// `GossipValidator::validate` with an invalid gossip message. After polling the `ObserverWork` + /// which should poll the `NetworkBridge`, the reputation change should be forwarded to the test + /// network. + #[test] + fn observer_work_polls_underlying_network_bridge() { + // Create a test network. + let (tester_fut, _network) = make_test_network(); + let mut tester = executor::block_on(tester_fut); + + // Create an observer. + let (client, backend) = { + let builder = TestClientBuilder::with_default_backend(); + let backend = builder.backend(); + let (client, _) = builder.build_with_longest_chain(); + (Arc::new(client), backend) + }; + + let persistent_data = aux_schema::load_persistent( + &*backend, + client.chain_info().genesis_hash, + 0, + || Ok(vec![]), + ).unwrap(); + + let (_tx, voter_command_rx) = mpsc::unbounded(); + let observer = ObserverWork::new( + client, + tester.net_handle.clone(), + persistent_data, + None, + voter_command_rx, + ); + + // Trigger a reputation change through the gossip validator. + let peer_id = PeerId::random(); + tester.trigger_gossip_validator_reputation_change(&peer_id); + + executor::block_on(async move { + // Ignore initial event stream request by gossip engine. + match tester.events.next().now_or_never() { + Some(Some(Event::EventStream(_))) => {}, + _ => panic!("expected event stream request"), + }; + + assert!( + tester.events.next().now_or_never().is_none(), + "expect no further network events", + ); + + // Poll the observer once and have it forward the reputation change from the gossip + // validator to the test network. + assert!(observer.now_or_never().is_none()); + + assert_matches!(tester.events.next().now_or_never(), Some(Some(Event::Report(_, _)))); + }); } } diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 9ad12c6c317adcbb87bda1229f74af950a13fbbc..cf340c695451c51e6c56b2f998787805c7a97814 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -25,7 +25,6 @@ use sc_network_test::{ use sc_network::config::{ProtocolConfig, Roles, BoxFinalityProofRequestBuilder}; use parking_lot::Mutex; use futures_timer::Delay; -use futures03::TryStreamExt as _; use tokio::runtime::current_thread; use sp_keyring::Ed25519Keyring; use sc_client::LongestChain; @@ -37,15 +36,19 @@ use sp_consensus::{ BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult, BlockImport, import_queue::{BoxJustificationImport, BoxFinalityProofImport}, }; -use std::collections::{HashMap, HashSet}; -use std::result; +use std::{ + collections::{HashMap, HashSet}, + result, + pin::Pin, task, +}; use parity_scale_codec::Decode; -use sp_runtime::traits::{Header as HeaderT, HasherFor}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, HasherFor}; use sp_runtime::generic::{BlockId, DigestItem}; use sp_core::{H256, NativeOrEncoded, ExecutionContext, crypto::Public}; use sp_finality_grandpa::{GRANDPA_ENGINE_ID, AuthorityList, GrandpaApi}; use sp_state_machine::{InMemoryBackend, prove_read, read_proof_check}; -use std::{pin::Pin, task}; +use futures01::Async; +use futures::compat::Future01CompatExt; use authorities::AuthoritySet; use finality_proof::{ @@ -194,7 +197,7 @@ impl TestNetFactory for GrandpaTestNet { #[derive(Clone)] struct Exit; -impl futures03::Future for Exit { +impl futures::Future for Exit { type Output = (); fn poll(self: Pin<&mut Self>, _: &mut task::Context) -> task::Poll<()> { @@ -369,17 +372,27 @@ fn create_keystore(authority: Ed25519Keyring) -> (KeyStorePtr, tempfile::TempDir (keystore, keystore_path) } +fn block_until_complete(future: impl Future + Unpin, net: &Arc>, runtime: &mut current_thread::Runtime) { + let drive_to_completion = futures01::future::poll_fn(|| { + net.lock().poll(); Ok::, ()>(Async::NotReady) + }); + runtime.block_on( + future::select(future, drive_to_completion.compat()) + .map(|_| Ok::<(), ()>(())) + .compat() + ).unwrap(); +} + // run the voters to completion. provide a closure to be invoked after // the voters are spawned but before blocking on them. fn run_to_completion_with( runtime: &mut current_thread::Runtime, - threads_pool: &futures03::executor::ThreadPool, blocks: u64, net: Arc>, peers: &[Ed25519Keyring], with: F, ) -> u64 where - F: FnOnce(current_thread::Handle) -> Option>> + F: FnOnce(current_thread::Handle) -> Option>>> { use parking_lot::RwLock; @@ -409,17 +422,16 @@ fn run_to_completion_with( }; wait_for.push( - Box::new( + Box::pin( client.finality_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() .take_while(move |n| { let mut highest_finalized = highest_finalized.write(); if *n.header.number() > *highest_finalized { *highest_finalized = *n.header.number(); } - Ok(n.header.number() < &blocks) + future::ready(n.header.number() < &blocks) }) - .collect() + .collect::>() .map(|_| ()) ) ); @@ -441,35 +453,29 @@ fn run_to_completion_with( on_exit: Exit, telemetry_on_connect: None, voting_rule: (), - executor: threads_pool.clone(), }; let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network"); assert_send(&voter); - runtime.spawn(voter); + runtime.spawn(voter.unit_error().compat()); } // wait for all finalized on each. - let wait_for = ::futures::future::join_all(wait_for) - .map(|_| ()) - .map_err(|_| ()); - - let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); - let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); + let wait_for = ::futures::future::join_all(wait_for); + block_until_complete(wait_for, &net, runtime); let highest_finalized = *highest_finalized.read(); highest_finalized } fn run_to_completion( runtime: &mut current_thread::Runtime, - threads_pool: &futures03::executor::ThreadPool, blocks: u64, net: Arc>, peers: &[Ed25519Keyring] ) -> u64 { - run_to_completion_with(runtime, threads_pool, blocks, net, peers, |_| None) + run_to_completion_with(runtime, blocks, net, peers, |_| None) } fn add_scheduled_change(block: &mut Block, change: ScheduledChange) { @@ -494,7 +500,6 @@ fn add_forced_change( fn finalize_3_voters_no_observers() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); @@ -508,7 +513,7 @@ fn finalize_3_voters_no_observers() { } let net = Arc::new(Mutex::new(net)); - run_to_completion(&mut runtime, &threads_pool, 20, net.clone(), peers); + run_to_completion(&mut runtime, 20, net.clone(), peers); // normally there's no justification for finalized blocks assert!( @@ -520,7 +525,6 @@ fn finalize_3_voters_no_observers() { #[test] fn finalize_3_voters_1_full_observer() { let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); @@ -553,9 +557,8 @@ fn finalize_3_voters_1_full_observer() { }; finality_notifications.push( client.finality_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .take_while(|n| Ok(n.header.number() < &20)) - .for_each(move |_| Ok(())) + .take_while(|n| future::ready(n.header.number() < &20)) + .for_each(move |_| future::ready(())) ); let keystore = if let Some(local_key) = local_key { @@ -581,23 +584,20 @@ fn finalize_3_voters_1_full_observer() { on_exit: Exit, telemetry_on_connect: None, voting_rule: (), - executor: threads_pool.clone(), }; voters.push(run_grandpa_voter(grandpa_params).expect("all in order with client and network")); } for voter in voters { - runtime.spawn(voter); + runtime.spawn(voter.unit_error().compat()); } // wait for all finalized on each. let wait_for = futures::future::join_all(finality_notifications) - .map(|_| ()) - .map_err(|_| ()); + .map(|_| ()); - let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); - let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); + block_until_complete(wait_for, &net, &mut runtime); } #[test] @@ -629,7 +629,6 @@ fn transition_3_voters_twice_1_full_observer() { let net = Arc::new(Mutex::new(GrandpaTestNet::new(api, 8))); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); net.lock().peer(0).push_blocks(1, false); net.lock().block_until_sync(&mut runtime); @@ -652,8 +651,7 @@ fn transition_3_voters_twice_1_full_observer() { // wait for blocks to be finalized before generating new ones let block_production = client.finality_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .take_while(|n| Ok(n.header.number() < &30)) + .take_while(|n| future::ready(n.header.number() < &30)) .for_each(move |n| { match n.header.number() { 1 => { @@ -690,10 +688,10 @@ fn transition_3_voters_twice_1_full_observer() { _ => {}, } - Ok(()) + future::ready(()) }); - runtime.spawn(block_production); + runtime.spawn(block_production.unit_error().compat()); } let mut finality_notifications = Vec::new(); @@ -723,9 +721,8 @@ fn transition_3_voters_twice_1_full_observer() { finality_notifications.push( client.finality_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .take_while(|n| Ok(n.header.number() < &30)) - .for_each(move |_| Ok(())) + .take_while(|n| future::ready(n.header.number() < &30)) + .for_each(move |_| future::ready(())) .map(move |()| { let full_client = client.as_full().expect("only full clients are used in test"); let set: AuthoritySet = crate::aux_schema::load_authorities(&*full_client).unwrap(); @@ -750,26 +747,21 @@ fn transition_3_voters_twice_1_full_observer() { on_exit: Exit, telemetry_on_connect: None, voting_rule: (), - executor: threads_pool.clone(), }; let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network"); - runtime.spawn(voter); + runtime.spawn(voter.unit_error().compat()); } // wait for all finalized on each. - let wait_for = ::futures::future::join_all(finality_notifications) - .map(|_| ()) - .map_err(|_| ()); + let wait_for = ::futures::future::join_all(finality_notifications); - let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); - let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); + block_until_complete(wait_for, &net, &mut runtime); } #[test] fn justification_is_emitted_when_consensus_data_changes() { let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3); @@ -778,7 +770,7 @@ fn justification_is_emitted_when_consensus_data_changes() { net.peer(0).push_authorities_change_block(new_authorities); net.block_until_sync(&mut runtime); let net = Arc::new(Mutex::new(net)); - run_to_completion(&mut runtime, &threads_pool, 1, net.clone(), peers); + run_to_completion(&mut runtime, 1, net.clone(), peers); // ... and check that there's justification for block#1 assert!(net.lock().peer(0).client().justification(&BlockId::Number(1)).unwrap().is_some(), @@ -788,7 +780,6 @@ fn justification_is_emitted_when_consensus_data_changes() { #[test] fn justification_is_generated_periodically() { let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); @@ -797,7 +788,7 @@ fn justification_is_generated_periodically() { net.block_until_sync(&mut runtime); let net = Arc::new(Mutex::new(net)); - run_to_completion(&mut runtime, &threads_pool, 32, net.clone(), peers); + run_to_completion(&mut runtime, 32, net.clone(), peers); // when block#32 (justification_period) is finalized, justification // is required => generated @@ -828,7 +819,6 @@ fn consensus_changes_works() { #[test] fn sync_justifications_on_change_blocks() { let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let peers_b = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers_b); @@ -860,7 +850,7 @@ fn sync_justifications_on_change_blocks() { } let net = Arc::new(Mutex::new(net)); - run_to_completion(&mut runtime, &threads_pool, 25, net.clone(), peers_a); + run_to_completion(&mut runtime, 25, net.clone(), peers_a); // the first 3 peers are grandpa voters and therefore have already finalized // block 21 and stored a justification @@ -869,21 +859,20 @@ fn sync_justifications_on_change_blocks() { } // the last peer should get the justification by syncing from other peers - runtime.block_on(futures::future::poll_fn(move || -> std::result::Result<_, ()> { + futures::executor::block_on(futures::future::poll_fn(move |_| { if net.lock().peer(3).client().justification(&BlockId::Number(21)).unwrap().is_none() { net.lock().poll(); - Ok(Async::NotReady) + Poll::Pending } else { - Ok(Async::Ready(())) + Poll::Ready(()) } - })).unwrap() + })) } #[test] fn finalizes_multiple_pending_changes_in_order() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let peers_b = &[Ed25519Keyring::Dave, Ed25519Keyring::Eve, Ed25519Keyring::Ferdie]; @@ -937,14 +926,13 @@ fn finalizes_multiple_pending_changes_in_order() { } let net = Arc::new(Mutex::new(net)); - run_to_completion(&mut runtime, &threads_pool, 30, net.clone(), all_peers); + run_to_completion(&mut runtime, 30, net.clone(), all_peers); } #[test] fn force_change_to_new_set() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); // two of these guys are offline. let genesis_authorities = &[ Ed25519Keyring::Alice, @@ -995,7 +983,7 @@ fn force_change_to_new_set() { // it will only finalize if the forced transition happens. // we add_blocks after the voters are spawned because otherwise // the link-halfs have the wrong AuthoritySet - run_to_completion(&mut runtime, &threads_pool, 25, net, peers_a); + run_to_completion(&mut runtime, 25, net, peers_a); } #[test] @@ -1032,7 +1020,8 @@ fn allows_reimporting_change_blocks() { storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, } @@ -1091,7 +1080,8 @@ fn test_bad_justification() { storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, } @@ -1119,11 +1109,10 @@ fn voter_persists_its_votes() { use std::iter::FromIterator; use std::sync::atomic::{AtomicUsize, Ordering}; use futures::future; - use futures::sync::mpsc; + use futures::channel::mpsc; let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); // we have two authorities but we'll only be running the voter for alice // we are going to be listening for the prevotes it casts @@ -1157,56 +1146,54 @@ fn voter_persists_its_votes() { keystore_paths.push(keystore_path); struct ResettableVoter { - voter: Box + Send>, + voter: Pin + Send + Unpin>>, voter_rx: mpsc::UnboundedReceiver<()>, net: Arc>, client: PeersClient, keystore: KeyStorePtr, - threads_pool: futures03::executor::ThreadPool, } impl Future for ResettableVoter { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { - match self.voter.poll() { - Ok(Async::Ready(())) | Err(_) => panic!("error in the voter"), - Ok(Async::NotReady) => {}, + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = Pin::into_inner(self); + + if let Poll::Ready(()) = Pin::new(&mut this.voter).poll(cx) { + panic!("error in the voter"); } - match self.voter_rx.poll() { - Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), - Ok(Async::NotReady) => {} - Ok(Async::Ready(Some(()))) => { + match Pin::new(&mut this.voter_rx).poll_next(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(())) => { let (_block_import, _, _, _, link) = - self.net.lock() + this.net.lock() .make_block_import::< TransactionFor - >(self.client.clone()); + >(this.client.clone()); let link = link.lock().take().unwrap(); let grandpa_params = GrandpaParams { config: Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - keystore: Some(self.keystore.clone()), + keystore: Some(this.keystore.clone()), name: Some(format!("peer#{}", 0)), is_authority: true, observer_enabled: true, }, link, - network: self.net.lock().peers[0].network_service().clone(), + network: this.net.lock().peers[0].network_service().clone(), inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, voting_rule: VotingRulesBuilder::default().build(), - executor: self.threads_pool.clone(), }; let voter = run_grandpa_voter(grandpa_params) .expect("all in order with client and network") - .then(move |r| { + .map(move |r| { // we need to keep the block_import alive since it owns the // sender for the voter commands channel, if that gets dropped // then the voter will stop @@ -1214,30 +1201,29 @@ fn voter_persists_its_votes() { r }); - self.voter = Box::new(voter); + this.voter = Box::pin(voter); // notify current task in order to poll the voter - futures::task::current().notify(); + cx.waker().wake_by_ref(); } }; - Ok(Async::NotReady) + Poll::Pending } } - // we create a "dummy" voter by setting it to `empty` and triggering the `tx`. + // we create a "dummy" voter by setting it to `pending` and triggering the `tx`. // this way, the `ResettableVoter` will reset its `voter` field to a value ASAP. voter_tx.unbounded_send(()).unwrap(); runtime.spawn(ResettableVoter { - voter: Box::new(futures::future::empty()), + voter: Box::pin(futures::future::pending()), voter_rx, net: net.clone(), client: client.clone(), keystore, - threads_pool: threads_pool.clone(), - }); + }.unit_error().compat()); } - let (exit_tx, exit_rx) = futures::sync::oneshot::channel::<()>(); + let (exit_tx, exit_rx) = futures::channel::oneshot::channel::<()>(); // create the communication layer for bob, but don't start any // voter. instead we'll listen for the prevote that alice casts @@ -1269,7 +1255,6 @@ fn voter_persists_its_votes() { net.lock().peers[1].network_service().clone(), config.clone(), set_state, - &threads_pool, ); let (round_rx, round_tx) = network.round_communication( @@ -1280,109 +1265,111 @@ fn voter_persists_its_votes() { HasVoted::No, ); + runtime.spawn( + network.map_err(|e| panic!("network bridge should not error: {:?}", e)) + .compat(), + ); + let round_tx = Arc::new(Mutex::new(round_tx)); let exit_tx = Arc::new(Mutex::new(Some(exit_tx))); let net = net.clone(); - let state = AtomicUsize::new(0); + let state = Arc::new(AtomicUsize::new(0)); runtime.spawn(round_rx.for_each(move |signed| { - if state.compare_and_swap(0, 1, Ordering::SeqCst) == 0 { - // the first message we receive should be a prevote from alice. - let prevote = match signed.message { - finality_grandpa::Message::Prevote(prevote) => prevote, - _ => panic!("voter should prevote."), - }; - - // its chain has 20 blocks and the voter targets 3/4 of the - // unfinalized chain, so the vote should be for block 15 - assert!(prevote.target_number == 15); - - // we push 20 more blocks to alice's chain - net.lock().peer(0).push_blocks(20, false); - - let net2 = net.clone(); - let net = net.clone(); - let voter_tx = voter_tx.clone(); - let round_tx = round_tx.clone(); - - let interval = futures03::stream::unfold(Delay::new(Duration::from_millis(200)), |delay| - Box::pin(async move { - delay.await; - Some(((), Delay::new(Duration::from_millis(200)))) - })).map(Ok::<_, ()>).compat(); - - future::Either::A(interval - .take_while(move |_| { - Ok(net2.lock().peer(1).client().info().best_number != 40) - }) - .for_each(|_| Ok(())) - .and_then(move |_| { - let block_30_hash = - net.lock().peer(0).client().as_full().unwrap().hash(30).unwrap().unwrap(); - - // we restart alice's voter - voter_tx.unbounded_send(()).unwrap(); - - // and we push our own prevote for block 30 - let prevote = finality_grandpa::Prevote { - target_number: 30, - target_hash: block_30_hash, - }; - - round_tx.lock().start_send(finality_grandpa::Message::Prevote(prevote)).unwrap(); - Ok(()) - }).map_err(|_| panic!())) - - } else if state.compare_and_swap(1, 2, Ordering::SeqCst) == 1 { - // the next message we receive should be our own prevote - let prevote = match signed.message { - finality_grandpa::Message::Prevote(prevote) => prevote, - _ => panic!("We should receive our own prevote."), - }; - - // targeting block 30 - assert!(prevote.target_number == 30); - - // after alice restarts it should send its previous prevote - // therefore we won't ever receive it again since it will be a - // known message on the gossip layer - - future::Either::B(future::ok(())) - - } else if state.compare_and_swap(2, 3, Ordering::SeqCst) == 2 { - // we then receive a precommit from alice for block 15 - // even though we casted a prevote for block 30 - let precommit = match signed.message { - finality_grandpa::Message::Precommit(precommit) => precommit, - _ => panic!("voter should precommit."), - }; - - assert!(precommit.target_number == 15); - - // signal exit - exit_tx.clone().lock().take().unwrap().send(()).unwrap(); - - future::Either::B(future::ok(())) - - } else { - panic!() + let net2 = net.clone(); + let net = net.clone(); + let voter_tx = voter_tx.clone(); + let round_tx = round_tx.clone(); + let state = state.clone(); + let exit_tx = exit_tx.clone(); + + async move { + if state.compare_and_swap(0, 1, Ordering::SeqCst) == 0 { + // the first message we receive should be a prevote from alice. + let prevote = match signed.message { + finality_grandpa::Message::Prevote(prevote) => prevote, + _ => panic!("voter should prevote."), + }; + + // its chain has 20 blocks and the voter targets 3/4 of the + // unfinalized chain, so the vote should be for block 15 + assert!(prevote.target_number == 15); + + // we push 20 more blocks to alice's chain + net.lock().peer(0).push_blocks(20, false); + + let interval = futures::stream::unfold(Delay::new(Duration::from_millis(200)), |delay| + Box::pin(async move { + delay.await; + Some(((), Delay::new(Duration::from_millis(200)))) + }) + ); + + interval + .take_while(move |_| { + future::ready(net2.lock().peer(1).client().info().best_number != 40) + }) + .for_each(|_| future::ready(())) + .await; + + let block_30_hash = + net.lock().peer(0).client().as_full().unwrap().hash(30).unwrap().unwrap(); + + // we restart alice's voter + voter_tx.unbounded_send(()).unwrap(); + + // and we push our own prevote for block 30 + let prevote = finality_grandpa::Prevote { + target_number: 30, + target_hash: block_30_hash, + }; + + // One should either be calling `Sink::send` or `Sink::start_send` followed + // by `Sink::poll_complete` to make sure items are being flushed. Given that + // we send in a loop including a delay until items are received, this can be + // ignored for the sake of reduced complexity. + Pin::new(&mut *round_tx.lock()).start_send(finality_grandpa::Message::Prevote(prevote)).unwrap(); + } else if state.compare_and_swap(1, 2, Ordering::SeqCst) == 1 { + // the next message we receive should be our own prevote + let prevote = match signed.message { + finality_grandpa::Message::Prevote(prevote) => prevote, + _ => panic!("We should receive our own prevote."), + }; + + // targeting block 30 + assert!(prevote.target_number == 30); + + // after alice restarts it should send its previous prevote + // therefore we won't ever receive it again since it will be a + // known message on the gossip layer + + } else if state.compare_and_swap(2, 3, Ordering::SeqCst) == 2 { + // we then receive a precommit from alice for block 15 + // even though we casted a prevote for block 30 + let precommit = match signed.message { + finality_grandpa::Message::Precommit(precommit) => precommit, + _ => panic!("voter should precommit."), + }; + + assert!(precommit.target_number == 15); + + // signal exit + exit_tx.clone().lock().take().unwrap().send(()).unwrap(); + } else { + panic!() + } } - - }).map_err(|_| ())); + }).map(Ok).boxed().compat()); } - let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); - let exit = exit_rx.into_future().map(|_| ()).map_err(|_| ()); - - runtime.block_on(drive_to_completion.select(exit).map(|_| ()).map_err(|_| ())).unwrap(); + block_until_complete(exit_rx.into_future(), &net, &mut runtime); } #[test] fn finalize_3_voters_1_light_observer() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let authorities = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(authorities); @@ -1399,11 +1386,12 @@ fn finalize_3_voters_1_light_observer() { let link = net.lock().peer(3).data.lock().take().expect("link initialized on startup; qed"); let finality_notifications = net.lock().peer(3).client().finality_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .take_while(|n| Ok(n.header.number() < &20)) - .collect(); + .take_while(|n| { + future::ready(n.header.number() < &20) + }) + .collect::>(); - run_to_completion_with(&mut runtime, &threads_pool, 20, net.clone(), authorities, |executor| { + run_to_completion_with(&mut runtime, 20, net.clone(), authorities, |executor| { executor.spawn( run_grandpa_observer( Config { @@ -1417,11 +1405,10 @@ fn finalize_3_voters_1_light_observer() { link, net.lock().peers[3].network_service().clone(), Exit, - threads_pool.clone(), - ).unwrap() + ).unwrap().unit_error().compat() ).unwrap(); - Some(Box::new(finality_notifications.map(|_| ()))) + Some(Box::pin(finality_notifications.map(|_| ()))) }); } @@ -1429,7 +1416,6 @@ fn finalize_3_voters_1_light_observer() { fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { let _ = ::env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers = &[Ed25519Keyring::Alice]; let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 1); @@ -1439,18 +1425,19 @@ fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { // && instead fetches finality proof for block #1 net.peer(0).push_authorities_change_block(vec![sp_consensus_babe::AuthorityId::from_slice(&[42; 32])]); let net = Arc::new(Mutex::new(net)); - run_to_completion(&mut runtime, &threads_pool, 1, net.clone(), peers); + run_to_completion(&mut runtime, 1, net.clone(), peers); net.lock().block_until_sync(&mut runtime); // check that the block#1 is finalized on light client - runtime.block_on(futures::future::poll_fn(move || -> std::result::Result<_, ()> { + let mut runtime = current_thread::Runtime::new().unwrap(); + let _ = runtime.block_on(futures::future::poll_fn(move |_| { if net.lock().peer(1).client().info().finalized_number == 1 { - Ok(Async::Ready(())) + Poll::Ready(()) } else { net.lock().poll(); - Ok(Async::NotReady) + Poll::Pending } - })).unwrap() + }).unit_error().compat()); } #[test] @@ -1460,7 +1447,6 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ let _ = ::env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); // two of these guys are offline. let genesis_authorities = if FORCE_CHANGE { @@ -1508,7 +1494,7 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ net.lock().block_until_sync(&mut runtime); // finalize block #11 on full clients - run_to_completion(&mut runtime, &threads_pool, 11, net.clone(), peers_a); + run_to_completion(&mut runtime, 11, net.clone(), peers_a); // request finalization by light client net.lock().add_light_peer(&GrandpaTestNet::default_config()); @@ -1525,7 +1511,6 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ fn voter_catches_up_to_latest_round_when_behind() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers); @@ -1537,7 +1522,7 @@ fn voter_catches_up_to_latest_round_when_behind() { let net = Arc::new(Mutex::new(net)); let mut finality_notifications = Vec::new(); - let voter = |keystore, peer_id, link, net: Arc>| -> Box + Send> { + let voter = |keystore, peer_id, link, net: Arc>| -> Pin + Send>> { let grandpa_params = GrandpaParams { config: Config { gossip_duration: TEST_GOSSIP_DURATION, @@ -1553,10 +1538,9 @@ fn voter_catches_up_to_latest_round_when_behind() { on_exit: Exit, telemetry_on_connect: None, voting_rule: (), - executor: threads_pool.clone(), }; - Box::new(run_grandpa_voter(grandpa_params).expect("all in order with client and network")) + Box::pin(run_grandpa_voter(grandpa_params).expect("all in order with client and network")) }; let mut keystore_paths = Vec::new(); @@ -1574,9 +1558,8 @@ fn voter_catches_up_to_latest_round_when_behind() { finality_notifications.push( client.finality_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .take_while(|n| Ok(n.header.number() < &50)) - .for_each(move |_| Ok(())) + .take_while(|n| future::ready(n.header.number() < &50)) + .for_each(move |_| future::ready(())) ); let (keystore, keystore_path) = create_keystore(*key); @@ -1584,14 +1567,13 @@ fn voter_catches_up_to_latest_round_when_behind() { let voter = voter(Some(keystore), peer_id, link, net.clone()); - runtime.spawn(voter); + runtime.spawn(voter.unit_error().compat()); } // wait for them to finalize block 50. since they'll vote on 3/4 of the // unfinalized chain it will take at least 4 rounds to do it. let wait_for_finality = ::futures::future::join_all(finality_notifications) - .map(|_| ()) - .map_err(|_| ()); + .map(|_| ()); // spawn a new voter, it should be behind by at least 4 rounds and should be // able to catch up to the latest round @@ -1599,7 +1581,7 @@ fn voter_catches_up_to_latest_round_when_behind() { let net = net.clone(); let runtime = runtime.handle(); - wait_for_finality.and_then(move |_| { + wait_for_finality.then(move |_| { let peer_id = 2; let link = { let net = net.lock(); @@ -1611,20 +1593,20 @@ fn voter_catches_up_to_latest_round_when_behind() { let voter = voter(None, peer_id, link, net); - runtime.spawn(voter).unwrap(); + runtime.spawn(voter.unit_error().compat()).unwrap(); let start_time = std::time::Instant::now(); let timeout = Duration::from_secs(5 * 60); - let wait_for_catch_up = futures::future::poll_fn(move || { + let wait_for_catch_up = futures::future::poll_fn(move |_| { // The voter will start at round 1 and since everyone else is // already at a later round the only way to get to round 4 (or // later) is by issuing a catch up request. if set_state.read().last_completed_round().number >= 4 { - Ok(Async::Ready(())) + Poll::Ready(()) } else if start_time.elapsed() > timeout { panic!("Timed out while waiting for catch up to happen") } else { - Ok(Async::NotReady) + Poll::Pending } }); @@ -1632,8 +1614,14 @@ fn voter_catches_up_to_latest_round_when_behind() { }) }; - let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); - let _ = runtime.block_on(test.select(drive_to_completion).map_err(|_| ())).unwrap(); + let drive_to_completion = futures01::future::poll_fn(|| { + net.lock().poll(); Ok::, ()>(Async::NotReady) + }); + runtime.block_on( + future::select(test, drive_to_completion.compat()) + .map(|_| Ok::<(), ()>(())) + .compat() + ).unwrap(); } #[test] @@ -1641,8 +1629,6 @@ fn grandpa_environment_respects_voting_rules() { use finality_grandpa::Chain; use sc_network_test::TestClient; - let threads_pool = futures03::executor::ThreadPool::new().unwrap(); - let peers = &[Ed25519Keyring::Alice]; let voters = make_ids(peers); @@ -1673,7 +1659,6 @@ fn grandpa_environment_respects_voting_rules() { network_service.clone(), config.clone(), set_state.clone(), - &threads_pool, ); Environment { @@ -1829,7 +1814,8 @@ fn imports_justification_for_regular_blocks_on_import() { storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }; diff --git a/client/finality-grandpa/src/until_imported.rs b/client/finality-grandpa/src/until_imported.rs index f53b651bcf48c95808053af86f9b006016edb721..c3a52fcf56fe197602ba844e61500686820f42cf 100644 --- a/client/finality-grandpa/src/until_imported.rs +++ b/client/finality-grandpa/src/until_imported.rs @@ -33,13 +33,15 @@ use sc_client_api::{BlockImportNotification, ImportNotifications}; use futures::prelude::*; use futures::stream::Fuse; use futures_timer::Delay; -use futures03::{StreamExt as _, TryStreamExt as _}; +use futures::channel::mpsc::UnboundedReceiver; use finality_grandpa::voter; use parking_lot::Mutex; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use std::collections::{HashMap, VecDeque}; +use std::pin::Pin; use std::sync::{atomic::{AtomicUsize, Ordering}, Arc}; +use std::task::{Context, Poll}; use std::time::{Duration, Instant}; use sp_finality_grandpa::AuthorityId; @@ -70,13 +72,15 @@ pub(crate) trait BlockUntilImported: Sized { } /// Buffering imported messages until blocks with given hashes are imported. +#[pin_project::pin_project] pub(crate) struct UntilImported> { - import_notifications: Fuse, Error = ()> + Send>>, + import_notifications: Fuse>>, block_sync_requester: BlockSyncRequester, status_check: BlockStatus, + #[pin] inner: Fuse, ready: VecDeque, - check_pending: Box + Send>, + check_pending: Pin> + Send>>, /// Mapping block hashes to their block number, the point in time it was /// first encountered (Instant) and a list of GRANDPA messages referencing /// the block hash. @@ -87,8 +91,9 @@ pub(crate) struct UntilImported UntilImported where Block: BlockT, BlockStatus: BlockStatusT, + BlockSyncRequester: BlockSyncRequesterT, + I: Stream, M: BlockUntilImported, - I: Stream, { /// Create a new `UntilImported` wrapper. pub(crate) fn new( @@ -105,22 +110,19 @@ impl UntilImported _>(|v| Ok::<_, ()>(v)).compat(); - Box::new(stream) as Box + Send> - }.fuse(), + import_notifications: import_notifications.fuse(), block_sync_requester, status_check, inner: stream.fuse(), ready: VecDeque::new(), - check_pending: Box::new(check_pending), + check_pending: Box::pin(check_pending), pending: HashMap::new(), identifier, } @@ -131,24 +133,27 @@ impl Stream for UntilImported, BSyncRequester: BlockSyncRequesterT, - I: Stream, + I: Stream, M: BlockUntilImported, { - type Item = M::Blocked; - type Error = Error; + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // We are using a `this` variable in order to allow multiple simultaneous mutable borrow + // to `self`. + let mut this = self.project(); - fn poll(&mut self) -> Poll, Error> { loop { - match self.inner.poll()? { - Async::Ready(None) => return Ok(Async::Ready(None)), - Async::Ready(Some(input)) => { + match Stream::poll_next(Pin::new(&mut this.inner), cx) { + Poll::Ready(None) => return Poll::Ready(None), + Poll::Ready(Some(input)) => { // new input: schedule wait of any parts which require // blocks to be known. - let ready = &mut self.ready; - let pending = &mut self.pending; + let ready = &mut this.ready; + let pending = &mut this.pending; M::schedule_wait( input, - &self.status_check, + this.status_check, |target_hash, target_number, wait| pending .entry(target_hash) .or_insert_with(|| (target_number, Instant::now(), Vec::new())) @@ -157,37 +162,36 @@ impl Stream for UntilImported break, + Poll::Pending => break, } } loop { - match self.import_notifications.poll() { - Err(_) => return Err(Error::Network(format!("Failed to get new message"))), - Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), - Ok(Async::Ready(Some(notification))) => { + match Stream::poll_next(Pin::new(&mut this.import_notifications), cx) { + Poll::Ready(None) => return Poll::Ready(None), + Poll::Ready(Some(notification)) => { // new block imported. queue up all messages tied to that hash. - if let Some((_, _, messages)) = self.pending.remove(¬ification.hash) { + if let Some((_, _, messages)) = this.pending.remove(¬ification.hash) { let canon_number = notification.header.number().clone(); let ready_messages = messages.into_iter() .filter_map(|m| m.wait_completed(canon_number)); - self.ready.extend(ready_messages); + this.ready.extend(ready_messages); } } - Ok(Async::NotReady) => break, + Poll::Pending => break, } } let mut update_interval = false; - while let Async::Ready(Some(_)) = self.check_pending.poll().map_err(Error::Timer)? { + while let Poll::Ready(Some(Ok(()))) = this.check_pending.poll_next_unpin(cx) { update_interval = true; } if update_interval { let mut known_keys = Vec::new(); - for (&block_hash, &mut (block_number, ref mut last_log, ref v)) in &mut self.pending { - if let Some(number) = self.status_check.block_number(block_hash)? { + for (&block_hash, &mut (block_number, ref mut last_log, ref v)) in this.pending.iter_mut() { + if let Some(number) = this.status_check.block_number(block_hash)? { known_keys.push((block_hash, number)); } else { let next_log = *last_log + LOG_PENDING_INTERVAL; @@ -199,13 +203,13 @@ impl Stream for UntilImported Stream for UntilImported { target_number: NumberFor, } +impl Unpin for BlockGlobalMessage {} + impl BlockUntilImported for BlockGlobalMessage { type Blocked = CommunicationIn; @@ -474,13 +480,12 @@ pub(crate) type UntilGlobalMessageBlocksImported( @@ -615,13 +619,13 @@ mod tests { let (chain_state, import_notifications) = TestChainState::new(); let block_status = chain_state.block_status(); - let (global_tx, global_rx) = futures::sync::mpsc::unbounded(); + let (global_tx, global_rx) = futures::channel::mpsc::unbounded(); let until_imported = UntilGlobalMessageBlocksImported::new( import_notifications, TestBlockSyncRequester::default(), block_status, - global_rx.map_err(|_| panic!("should never error")), + global_rx, "global", ); @@ -630,13 +634,10 @@ mod tests { // NOTE: needs to be cloned otherwise it is moved to the stream and // dropped too early. let inner_chain_state = chain_state.clone(); - let work = until_imported - .into_future() - .select2(Delay::new(Duration::from_millis(100)).unit_error().compat()) + let work = future::select(until_imported.into_future(), Delay::new(Duration::from_millis(100))) .then(move |res| match res { - Err(_) => panic!("neither should have had error"), - Ok(Either::A(_)) => panic!("timeout should have fired first"), - Ok(Either::B((_, until_imported))) => { + Either::Left(_) => panic!("timeout should have fired first"), + Either::Right((_, until_imported)) => { // timeout fired. push in the headers. enact_dependencies(&inner_chain_state); @@ -644,8 +645,7 @@ mod tests { } }); - let mut runtime = Runtime::new().unwrap(); - runtime.block_on(work).map_err(|(e, _)| e).unwrap().0.unwrap() + futures::executor::block_on(work).0.unwrap().unwrap() } #[test] @@ -871,7 +871,7 @@ mod tests { let (chain_state, import_notifications) = TestChainState::new(); let block_status = chain_state.block_status(); - let (global_tx, global_rx) = futures::sync::mpsc::unbounded(); + let (global_tx, global_rx) = futures::channel::mpsc::unbounded(); let block_sync_requester = TestBlockSyncRequester::default(); @@ -879,7 +879,7 @@ mod tests { import_notifications, block_sync_requester.clone(), block_status, - global_rx.map_err(|_| panic!("should never error")), + global_rx, "global", ); @@ -914,31 +914,31 @@ mod tests { // we send the commit message and spawn the until_imported stream global_tx.unbounded_send(unknown_commit()).unwrap(); - let mut runtime = Runtime::new().unwrap(); - runtime.spawn(until_imported.into_future().map(|_| ()).map_err(|_| ())); + let threads_pool = futures::executor::ThreadPool::new().unwrap(); + threads_pool.spawn_ok(until_imported.into_future().map(|_| ())); // assert that we will make sync requests - let assert = futures::future::poll_fn::<(), (), _>(|| { + let assert = futures::future::poll_fn(|_| { let block_sync_requests = block_sync_requester.requests.lock(); // we request blocks targeted by the precommits that aren't imported if block_sync_requests.contains(&(h2.hash(), *h2.number())) && block_sync_requests.contains(&(h3.hash(), *h3.number())) { - return Ok(Async::Ready(())); + return Poll::Ready(()); } - Ok(Async::NotReady) + Poll::Pending }); // the `until_imported` stream doesn't request the blocks immediately, // but it should request them after a small timeout - let timeout = Delay::new(Duration::from_secs(60)).unit_error().compat(); - let test = assert.select2(timeout).map(|res| match res { - Either::A(_) => {}, - Either::B(_) => panic!("timed out waiting for block sync request"), - }).map_err(|_| ()); + let timeout = Delay::new(Duration::from_secs(60)); + let test = future::select(assert, timeout).map(|res| match res { + Either::Left(_) => {}, + Either::Right(_) => panic!("timed out waiting for block sync request"), + }).map(drop); - runtime.block_on(test).unwrap(); + futures::executor::block_on(test); } } diff --git a/client/informant/Cargo.toml b/client/informant/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..197f320889c0d556bc475d1fb3f410ce31e6c9b4 --- /dev/null +++ b/client/informant/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sc-informant" +version = "0.8.0" +authors = ["Parity Technologies "] +description = "Substrate informant." +edition = "2018" +license = "GPL-3.0" + +[dependencies] +ansi_term = "0.12.1" +futures = "0.3.1" +log = "0.4.8" +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } +wasm-timer = "0.2" +sc-client-api = { version = "2.0.0", path = "../api" } +sc-network = { version = "0.8", path = "../network" } +sc-service = { version = "0.8", default-features = false, path = "../service" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } diff --git a/client/cli/src/informant/display.rs b/client/informant/src/display.rs similarity index 76% rename from client/cli/src/informant/display.rs rename to client/informant/src/display.rs index 199635e7c78833e46c5f076b1d19a060dbc528d4..53c9697868348cb9a73328d26a459601196cd240 100644 --- a/client/cli/src/informant/display.rs +++ b/client/informant/src/display.rs @@ -20,7 +20,9 @@ use log::info; use sc_network::SyncState; use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Zero, Saturating}; use sc_service::NetworkStatus; -use std::{convert::{TryFrom, TryInto}, fmt, time}; +use std::{convert::{TryFrom, TryInto}, fmt}; +use wasm_timer::Instant; +use crate::OutputFormat; /// State of the informant display system. /// @@ -40,15 +42,18 @@ pub struct InformantDisplay { /// `None` if `display` has never been called. last_number: Option>, /// The last time `display` or `new` has been called. - last_update: time::Instant, + last_update: Instant, + /// The format to print output in. + format: OutputFormat, } impl InformantDisplay { /// Builds a new informant display system. - pub fn new() -> InformantDisplay { + pub fn new(format: OutputFormat) -> InformantDisplay { InformantDisplay { last_number: None, - last_update: time::Instant::now(), + last_update: Instant::now(), + format, } } @@ -56,8 +61,10 @@ impl InformantDisplay { pub fn display(&mut self, info: &ClientInfo, net_status: NetworkStatus) { let best_number = info.chain.best_number; let best_hash = info.chain.best_hash; + let finalized_number = info.chain.finalized_number; + let num_connected_peers = net_status.num_connected_peers; let speed = speed::(best_number, self.last_number, self.last_update); - self.last_update = time::Instant::now(); + self.last_update = Instant::now(); self.last_number = Some(best_number); let (status, target) = match (net_status.sync_state, net_status.best_seen_block) { @@ -66,19 +73,35 @@ impl InformantDisplay { (SyncState::Downloading, Some(n)) => (format!("Syncing{}", speed), format!(", target=#{}", n)), }; - info!( - target: "substrate", - "{}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}", - Colour::White.bold().paint(&status), - target, - Colour::White.bold().paint(format!("{}", net_status.num_connected_peers)), - Colour::White.paint(format!("{}", best_number)), - best_hash, - Colour::White.paint(format!("{}", info.chain.finalized_number)), - info.chain.finalized_hash, - TransferRateFormat(net_status.average_download_per_sec), - TransferRateFormat(net_status.average_upload_per_sec), - ); + if self.format == OutputFormat::Coloured { + info!( + target: "substrate", + "{}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}", + Colour::White.bold().paint(&status), + target, + Colour::White.bold().paint(format!("{}", num_connected_peers)), + Colour::White.paint(format!("{}", best_number)), + best_hash, + Colour::White.paint(format!("{}", finalized_number)), + info.chain.finalized_hash, + TransferRateFormat(net_status.average_download_per_sec), + TransferRateFormat(net_status.average_upload_per_sec), + ); + } else { + info!( + target: "substrate", + "{}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}", + status, + target, + num_connected_peers, + best_number, + best_hash, + finalized_number, + info.chain.finalized_hash, + TransferRateFormat(net_status.average_download_per_sec), + TransferRateFormat(net_status.average_upload_per_sec), + ); + } } } @@ -87,7 +110,7 @@ impl InformantDisplay { fn speed( best_number: NumberFor, last_number: Option>, - last_update: time::Instant + last_update: Instant ) -> String { // Number of milliseconds elapsed since last time. let elapsed_ms = { diff --git a/client/cli/src/informant.rs b/client/informant/src/lib.rs similarity index 84% rename from client/cli/src/informant.rs rename to client/informant/src/lib.rs index 312e4017d5ff0624eafb134764577fad49e90fa3..699dcfdd7425e2b7b9e0f82eba481df2e81bf951 100644 --- a/client/cli/src/informant.rs +++ b/client/informant/src/lib.rs @@ -25,11 +25,19 @@ use std::time::Duration; mod display; +/// The format to print telemetry output in. +#[derive(PartialEq)] +pub enum OutputFormat { + Coloured, + Plain, +} + /// Creates an informant in the form of a `Future` that must be polled regularly. -pub fn build(service: &impl AbstractService) -> impl futures::Future { +pub fn build(service: &impl AbstractService, format: OutputFormat) -> impl futures::Future { let client = service.client(); + let pool = service.transaction_pool(); - let mut display = display::InformantDisplay::new(); + let mut display = display::InformantDisplay::new(format); let display_notifications = service .network_status(Duration::from_millis(5000)) @@ -40,6 +48,12 @@ pub fn build(service: &impl AbstractService) -> impl futures::Future"] edition = "2018" +license = "GPL-3.0" [dependencies] derive_more = "0.99.2" @@ -12,7 +13,7 @@ hex = "0.4.0" rand = "0.7.2" serde_json = "1.0.41" subtle = "2.1.1" -parking_lot = "0.9.0" +parking_lot = "0.10.0" [dev-dependencies] tempfile = "3.1.0" diff --git a/client/keystore/src/lib.rs b/client/keystore/src/lib.rs index f8794aa05e8f679d2e2462b04ac5366405736675..ef81c40c10f51f91a6aea3f17797e38ef29141ae 100644 --- a/client/keystore/src/lib.rs +++ b/client/keystore/src/lib.rs @@ -72,7 +72,8 @@ impl std::error::Error for Error { /// Every pair that is being generated by a `seed`, will be placed in memory. pub struct Store { path: Option, - additional: HashMap<(KeyTypeId, Vec), Vec>, + /// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed` + additional: HashMap<(KeyTypeId, Vec), String>, password: Option>, } @@ -97,25 +98,22 @@ impl Store { })) } - /// Get the public/private key pair for the given public key and key type. - fn get_additional_pair( + /// Get the key phrase for the given public key and key type from the in-memory store. + fn get_additional_pair( &self, - public: &Pair::Public, + public: &[u8], key_type: KeyTypeId, - ) -> Result> { - let key = (key_type, public.to_raw_vec()); - self.additional - .get(&key) - .map(|bytes| Pair::from_seed_slice(bytes).map_err(|_| Error::InvalidSeed)) - .transpose() + ) -> Option<&String> { + let key = (key_type, public.to_vec()); + self.additional.get(&key) } /// Insert the given public/private key pair with the given key type. /// /// Does not place it into the file system store. - fn insert_ephemeral_pair(&mut self, pair: &Pair, key_type: KeyTypeId) { + fn insert_ephemeral_pair(&mut self, pair: &Pair, seed: &str, key_type: KeyTypeId) { let key = (key_type, pair.public().to_raw_vec()); - self.additional.insert(key, pair.to_raw_vec()); + self.additional.insert(key, seed.into()); } /// Insert a new key with anonymous crypto. @@ -179,7 +177,7 @@ impl Store { key_type: KeyTypeId, ) -> Result { let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?; - self.insert_ephemeral_pair(&pair, key_type); + self.insert_ephemeral_pair(&pair, seed, key_type); Ok(pair) } @@ -190,20 +188,24 @@ impl Store { self.insert_ephemeral_from_seed_by_type::(seed, Pair::ID).map(Into::into) } + /// Get the key phrase for a given public key and key type. + fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result { + if let Some(phrase) = self.get_additional_pair(public, key_type) { + return Ok(phrase.clone()) + } + + let path = self.key_file_path(public, key_type).ok_or_else(|| Error::Unavailable)?; + let file = File::open(path)?; + + serde_json::from_reader(&file).map_err(Into::into) + } + /// Get a key pair for the given public key and key type. pub fn key_pair_by_type(&self, public: &Pair::Public, key_type: KeyTypeId, ) -> Result { - if let Some(pair) = self.get_additional_pair(public, key_type)? { - return Ok(pair) - } - - let path = self.key_file_path(public.as_slice(), key_type) - .ok_or_else(|| Error::Unavailable)?; - let file = File::open(path)?; - - let phrase: String = serde_json::from_reader(&file)?; + let phrase = self.key_phrase_by_type(public.as_slice(), key_type)?; let pair = Pair::from_string( &phrase, self.password.as_ref().map(|p| &***p), @@ -328,6 +330,10 @@ impl BareCryptoStore for Store { fn password(&self) -> Option<&str> { self.password.as_ref().map(|x| x.as_str()) } + + fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { + public_keys.iter().all(|(p, t)| self.key_phrase_by_type(&p, *t).is_ok()) + } } #[cfg(test)] diff --git a/client/network-gossip/Cargo.toml b/client/network-gossip/Cargo.toml index 91c80e2213e00e15e37c9cbb183d1319f7527e87..7258963e2af32048d4654b7848f109d4de5458b8 100644 --- a/client/network-gossip/Cargo.toml +++ b/client/network-gossip/Cargo.toml @@ -8,11 +8,12 @@ edition = "2018" [dependencies] log = "0.4.8" -futures01 = { package = "futures", version = "0.1.29" } futures = { version = "0.3.1", features = ["compat"] } -futures-timer = "0.4.0" +wasm-timer = "0.2" +futures-timer = "3.0.1" +futures01 = { package = "futures", version = "0.1.29" } +libp2p = { version = "0.15.0", default-features = false, features = ["libp2p-websocket"] } lru = "0.1.2" -libp2p = { version = "0.14.0-alpha.1", default-features = false, features = ["libp2p-websocket"] } +parking_lot = "0.10.0" sc-network = { version = "0.8", path = "../network" } -parking_lot = "0.9.0" sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } diff --git a/client/network-gossip/src/bridge.rs b/client/network-gossip/src/bridge.rs index 2b0b19b8761660dec26ac3cff408712e9b4069d2..87958cbc14563dab3c41e3d9e84a6941c8468447 100644 --- a/client/network-gossip/src/bridge.rs +++ b/client/network-gossip/src/bridge.rs @@ -15,16 +15,17 @@ // along with Substrate. If not, see . use crate::{Network, Validator}; -use crate::state_machine::{ConsensusGossip, TopicNotification}; +use crate::state_machine::{ConsensusGossip, TopicNotification, PERIODIC_MAINTENANCE_INTERVAL}; use sc_network::message::generic::ConsensusMessage; use sc_network::{Event, ReputationChange}; -use futures::{prelude::*, channel::mpsc, compat::Compat01As03, task::SpawnExt as _}; +use futures::{prelude::*, channel::mpsc, compat::Compat01As03}; +use futures01::stream::Stream as Stream01; use libp2p::PeerId; use parking_lot::Mutex; use sp_runtime::{traits::Block as BlockT, ConsensusEngineId}; -use std::{sync::Arc, time::Duration}; +use std::{pin::Pin, sync::Arc, task::{Context, Poll}}; /// Wraps around an implementation of the `Network` crate and provides gossiping capabilities on /// top of it. @@ -36,13 +37,17 @@ pub struct GossipEngine { struct GossipEngineInner { state_machine: ConsensusGossip, network: Box + Send>, + periodic_maintenance_interval: futures_timer::Delay, + network_event_stream: Compat01As03 + Send>>, + engine_id: ConsensusEngineId, } +impl Unpin for GossipEngineInner {} + impl GossipEngine { /// Create a new instance. pub fn new + Send + Clone + 'static>( mut network: N, - executor: &impl futures::task::Spawn, engine_id: ConsensusEngineId, validator: Arc>, ) -> Self where B: 'static { @@ -50,7 +55,7 @@ impl GossipEngine { // We grab the event stream before registering the notifications protocol, otherwise we // might miss events. - let event_stream = network.event_stream(); + let network_event_stream = network.event_stream(); network.register_notifications_protocol(engine_id); state_machine.register_validator(&mut network, engine_id, validator); @@ -58,6 +63,9 @@ impl GossipEngine { let inner = Arc::new(Mutex::new(GossipEngineInner { state_machine, network: Box::new(network), + periodic_maintenance_interval: futures_timer::Delay::new(PERIODIC_MAINTENANCE_INTERVAL), + network_event_stream: Compat01As03::new(network_event_stream), + engine_id, })); let gossip_engine = GossipEngine { @@ -65,72 +73,6 @@ impl GossipEngine { engine_id, }; - let res = executor.spawn({ - let inner = Arc::downgrade(&inner); - async move { - loop { - let _ = futures_timer::Delay::new(Duration::from_millis(1100)).await; - if let Some(inner) = inner.upgrade() { - let mut inner = inner.lock(); - let inner = &mut *inner; - inner.state_machine.tick(&mut *inner.network); - } else { - // We reach this branch if the `Arc` has no reference - // left. We can now let the task end. - break; - } - } - } - }); - - // Note: we consider the chances of an error to spawn a background task almost null. - if res.is_err() { - log::error!(target: "gossip", "Failed to spawn background task"); - } - - let res = executor.spawn(async move { - let mut stream = Compat01As03::new(event_stream); - while let Some(Ok(event)) = stream.next().await { - match event { - Event::NotificationStreamOpened { remote, engine_id: msg_engine_id, roles } => { - if msg_engine_id != engine_id { - continue; - } - let mut inner = inner.lock(); - let inner = &mut *inner; - inner.state_machine.new_peer(&mut *inner.network, remote, roles); - } - Event::NotificationsStreamClosed { remote, engine_id: msg_engine_id } => { - if msg_engine_id != engine_id { - continue; - } - let mut inner = inner.lock(); - let inner = &mut *inner; - inner.state_machine.peer_disconnected(&mut *inner.network, remote); - }, - Event::NotificationsReceived { remote, messages } => { - let mut inner = inner.lock(); - let inner = &mut *inner; - inner.state_machine.on_incoming( - &mut *inner.network, - remote, - messages.into_iter() - .filter_map(|(engine, data)| if engine == engine_id { - Some(ConsensusMessage { engine_id: engine, data: data.to_vec() }) - } else { None }) - .collect() - ); - }, - Event::Dht(_) => {} - } - } - }); - - // Note: we consider the chances of an error to spawn a background task almost null. - if res.is_err() { - log::error!(target: "gossip", "Failed to spawn background task"); - } - gossip_engine } @@ -222,6 +164,59 @@ impl GossipEngine { } } +impl Future for GossipEngine { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.inner.lock().poll_unpin(cx) + } +} + +impl Future for GossipEngineInner { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = &mut *self; + + while let Poll::Ready(Some(Ok(event))) = this.network_event_stream.poll_next_unpin(cx) { + match event { + Event::NotificationStreamOpened { remote, engine_id: msg_engine_id, roles } => { + if msg_engine_id != this.engine_id { + continue; + } + this.state_machine.new_peer(&mut *this.network, remote, roles); + } + Event::NotificationStreamClosed { remote, engine_id: msg_engine_id } => { + if msg_engine_id != this.engine_id { + continue; + } + this.state_machine.peer_disconnected(&mut *this.network, remote); + }, + Event::NotificationsReceived { remote, messages } => { + let engine_id = this.engine_id.clone(); + this.state_machine.on_incoming( + &mut *this.network, + remote, + messages.into_iter() + .filter_map(|(engine, data)| if engine == engine_id { + Some(ConsensusMessage { engine_id: engine, data: data.to_vec() }) + } else { None }) + .collect() + ); + }, + Event::Dht(_) => {} + } + } + + while let Poll::Ready(()) = this.periodic_maintenance_interval.poll_unpin(cx) { + this.periodic_maintenance_interval.reset(PERIODIC_MAINTENANCE_INTERVAL); + this.state_machine.tick(&mut *this.network); + } + + Poll::Pending + } +} + impl Clone for GossipEngine { fn clone(&self) -> Self { GossipEngine { diff --git a/client/network-gossip/src/state_machine.rs b/client/network-gossip/src/state_machine.rs index d1931b1bd29d81411a53a83b8d2de6a976f19d20..2acfdc37851894bc1e128f7b7faae3be954b98c8 100644 --- a/client/network-gossip/src/state_machine.rs +++ b/client/network-gossip/src/state_machine.rs @@ -28,12 +28,15 @@ use sp_runtime::traits::{Block as BlockT, Hash, HashFor}; use sp_runtime::ConsensusEngineId; pub use sc_network::message::generic::{Message, ConsensusMessage}; use sc_network::config::Roles; +use wasm_timer::Instant; // FIXME: Add additional spam/DoS attack protection: https://github.com/paritytech/substrate/issues/1115 const KNOWN_MESSAGES_CACHE_SIZE: usize = 4096; const REBROADCAST_INTERVAL: time::Duration = time::Duration::from_secs(30); +pub(crate) const PERIODIC_MAINTENANCE_INTERVAL: time::Duration = time::Duration::from_millis(1100); + mod rep { use sc_network::ReputationChange as Rep; /// Reputation change when a peer sends us a gossip message that we didn't know about. @@ -165,7 +168,7 @@ pub struct ConsensusGossip { messages: Vec>, known_messages: LruCache, validators: HashMap>>, - next_broadcast: time::Instant, + next_broadcast: Instant, } impl ConsensusGossip { @@ -177,7 +180,7 @@ impl ConsensusGossip { messages: Default::default(), known_messages: LruCache::new(KNOWN_MESSAGES_CACHE_SIZE), validators: Default::default(), - next_broadcast: time::Instant::now() + REBROADCAST_INTERVAL, + next_broadcast: Instant::now() + REBROADCAST_INTERVAL, } } @@ -260,9 +263,9 @@ impl ConsensusGossip { /// Perform periodic maintenance pub fn tick(&mut self, network: &mut dyn Network) { self.collect_garbage(); - if time::Instant::now() >= self.next_broadcast { + if Instant::now() >= self.next_broadcast { self.rebroadcast(network); - self.next_broadcast = time::Instant::now() + REBROADCAST_INTERVAL; + self.next_broadcast = Instant::now() + REBROADCAST_INTERVAL; } } diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index f1d89f71b0ed198c3cfe90717d09d56d4ff42d37..f6ff3bcb7338fda0efa697dd86a51caaba08277a 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -6,54 +6,66 @@ license = "GPL-3.0" authors = ["Parity Technologies "] edition = "2018" +[build-dependencies] +prost-build = "0.6.1" + [dependencies] +bitflags = "1.2.0" bytes = "0.5.0" +codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } derive_more = "0.99.2" either = "1.5.3" -log = "0.4.8" -parking_lot = "0.9.0" -bitflags = "1.2.0" +erased-serde = "0.3.9" fnv = "1.0.6" +fork-tree = { version = "2.0.0", path = "../../utils/fork-tree" } futures = "0.3.1" futures_codec = "0.3.3" -futures-timer = "0.4.0" +futures-timer = "3.0.1" +wasm-timer = "0.2" +libp2p = { version = "0.15.0", default-features = false, features = ["libp2p-websocket"] } linked-hash-map = "0.5.2" linked_hash_set = "0.1.3" +log = "0.4.8" lru = "0.4.0" -rustc-hex = "2.0.1" +nohash-hasher = "0.1.3" +parking_lot = "0.10.0" +prost = "0.6.1" rand = "0.7.2" -libp2p = { version = "0.14.0-alpha.1", default-features = false, features = ["libp2p-websocket"] } -fork-tree = { version = "2.0.0", path = "../../utils/fork-tree" } -sp-consensus = { version = "0.8", path = "../../primitives/consensus/common" } +rustc-hex = "2.0.1" +sc-block-builder = { version = "0.8", path = "../block-builder" } sc-client = { version = "0.8", path = "../" } sc-client-api = { version = "2.0.0", path = "../api" } -sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } -sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } -sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" } -sp-core = { version = "2.0.0", path = "../../primitives/core" } -sc-block-builder = { version = "0.8", path = "../block-builder" } -codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } sc-peerset = { version = "2.0.0", path = "../peerset" } serde = { version = "1.0.101", features = ["derive"] } serde_json = "1.0.41" slog = { version = "2.5.2", features = ["nested-values"] } slog_derive = "0.2.0" smallvec = "0.6.10" -unsigned-varint = { version = "0.3.0", features = ["codec"] } +sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-consensus = { version = "0.8", path = "../../primitives/consensus/common" } +sp-consensus-babe = { version = "0.8", path = "../../primitives/consensus/babe" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } substrate-test-client = { version = "2.0.0", optional = true, path = "../../test-utils/client" } substrate-test-runtime-client = { version = "2.0.0", optional = true, path = "../../test-utils/runtime/client" } -erased-serde = "0.3.9" +thiserror = "1" +unsigned-varint = { version = "0.3.0", features = ["codec"] } void = "1.0.2" zeroize = "1.0.0" -sp-consensus-babe = { version = "0.8", path = "../../primitives/consensus/babe" } +yamux = "0.4.2" [dev-dependencies] -sp-test-primitives = { version = "2.0.0", path = "../../primitives/test-primitives" } +async-std = "1.5" +assert_matches = "1.3" env_logger = "0.7.0" -sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } quickcheck = "0.9.0" rand = "0.7.2" +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +sp-test-primitives = { version = "2.0.0", path = "../../primitives/test-primitives" } +substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } tempfile = "3.1.0" [features] diff --git a/client/network/build.rs b/client/network/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..0fd1f128660e9aaec36c4ac14e405f77e9588096 --- /dev/null +++ b/client/network/build.rs @@ -0,0 +1,8 @@ +const PROTOS: &[&str] = &[ + "src/protocol/schema/api.v1.proto", + "src/protocol/schema/light.v1.proto" +]; + +fn main() { + prost_build::compile_protos(PROTOS, &["src/protocol"]).unwrap(); +} diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index 7cc9eee74d6d5906b2ec978ec8152f93a6594dca..63cbea4da594cce26193033b41156995a2a115af 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -19,13 +19,13 @@ use crate::{ Event, protocol::event::DhtEvent }; use crate::{ExHashT, specialization::NetworkSpecialization}; -use crate::protocol::{CustomMessageOutcome, Protocol}; +use crate::protocol::{self, light_client_handler, CustomMessageOutcome, Protocol}; use libp2p::NetworkBehaviour; use libp2p::core::{Multiaddr, PeerId, PublicKey}; use libp2p::kad::record; use libp2p::swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}; use libp2p::core::{nodes::Substream, muxing::StreamMuxerBox}; -use log::{debug, warn}; +use log::debug; use sp_consensus::{BlockOrigin, import_queue::{IncomingBlock, Origin}}; use sp_runtime::{traits::{Block as BlockT, NumberFor}, Justification}; use std::{iter, task::Context, task::Poll}; @@ -42,7 +42,10 @@ pub struct Behaviour, H: ExHashT> { debug_info: debug_info::DebugInfoBehaviour>, /// Discovers nodes of the network. discovery: DiscoveryBehaviour>, - + /// Block request handling. + block_requests: protocol::BlockRequests, B>, + /// Light client request handling. + light_client_handler: protocol::LightClientHandler, B>, /// Queue of events to produce for the outside. #[behaviour(ignore)] events: Vec>, @@ -65,6 +68,9 @@ impl, H: ExHashT> Behaviour { known_addresses: Vec<(PeerId, Multiaddr)>, enable_mdns: bool, allow_private_ipv4: bool, + discovery_only_if_under_num: u64, + block_requests: protocol::BlockRequests, B>, + light_client_handler: protocol::LightClientHandler, B>, ) -> Self { Behaviour { substrate, @@ -73,9 +79,12 @@ impl, H: ExHashT> Behaviour { local_public_key, known_addresses, enable_mdns, - allow_private_ipv4 + allow_private_ipv4, + discovery_only_if_under_num, ).await, - events: Vec::new(), + block_requests, + light_client_handler, + events: Vec::new() } } @@ -117,6 +126,12 @@ impl, H: ExHashT> Behaviour { pub fn put_value(&mut self, key: record::Key, value: Vec) { self.discovery.put_value(key, value); } + + /// Issue a light client request. + #[allow(unused)] + pub fn light_client_request(&mut self, r: light_client_handler::Request) -> Result<(), light_client_handler::Error> { + self.light_client_handler.request(r) + } } impl, H: ExHashT> NetworkBehaviourEventProcess for @@ -144,9 +159,9 @@ Behaviour { roles, })); }, - CustomMessageOutcome::NotificationsStreamClosed { remote, protocols } => + CustomMessageOutcome::NotificationStreamClosed { remote, protocols } => for engine_id in protocols { - self.events.push(BehaviourOut::Event(Event::NotificationsStreamClosed { + self.events.push(BehaviourOut::Event(Event::NotificationStreamClosed { remote: remote.clone(), engine_id, })); @@ -164,9 +179,6 @@ impl, H: ExHashT> NetworkBehaviourEventPr for Behaviour { fn inject_event(&mut self, event: debug_info::DebugInfoEvent) { let debug_info::DebugInfoEvent::Identified { peer_id, mut info } = event; - if !info.protocol_version.contains("substrate") { - warn!(target: "sub-libp2p", "Connected to a non-Substrate node: {:?}", info); - } if info.listen_addrs.len() > 30 { debug!(target: "sub-libp2p", "Node {:?} has reported more than 30 addresses; \ it is identified by {:?} and {:?}", peer_id, info.protocol_version, diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 6a2062ddbf253da685e8473e4008332ef1ee03a0..87c77fee9f0fcf5502de7b65e6fd0107b7964298 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -32,6 +32,7 @@ use libp2p::identity::{Keypair, ed25519}; use libp2p::wasm_ext; use libp2p::{PeerId, Multiaddr, multiaddr}; use core::{fmt, iter}; +use std::{future::Future, pin::Pin}; use std::{error::Error, fs, io::{self, Write}, net::Ipv4Addr, path::{Path, PathBuf}, sync::Arc}; use zeroize::Zeroize; @@ -40,6 +41,10 @@ pub struct Params { /// Assigned roles for our node (full, light, ...). pub roles: Roles, + /// How to spawn background tasks. If you pass `None`, then a threads pool will be used by + /// default. + pub executor: Option + Send>>) + Send>>, + /// Network layer configuration. pub network_config: NetworkConfiguration, @@ -287,6 +292,7 @@ impl Default for NetworkConfiguration { enable_mdns: false, allow_private_ipv4: true, wasm_external_transport: None, + use_yamux_flow_control: false, }, max_parallel_downloads: 5, } @@ -343,6 +349,8 @@ pub enum TransportConfig { /// This parameter exists whatever the target platform is, but it is expected to be set to /// `Some` only when compiling for WASM. wasm_external_transport: Option, + /// Use flow control for yamux streams if set to true. + use_yamux_flow_control: bool, }, /// Only allow connections within the same process. diff --git a/client/network/src/debug_info.rs b/client/network/src/debug_info.rs index 9cc39baae6237216723fbedfd67ca363a326c5c4..a3d63333fa5fe30e152713213a85cd16729d4667 100644 --- a/client/network/src/debug_info.rs +++ b/client/network/src/debug_info.rs @@ -17,16 +17,19 @@ use fnv::FnvHashMap; use futures::prelude::*; use libp2p::Multiaddr; +use libp2p::core::nodes::listeners::ListenerId; use libp2p::core::{ConnectedPoint, either::EitherOutput, PeerId, PublicKey}; use libp2p::swarm::{IntoProtocolsHandler, IntoProtocolsHandlerSelect, ProtocolsHandler}; use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use libp2p::identify::{Identify, IdentifyEvent, IdentifyInfo}; use libp2p::ping::{Ping, PingConfig, PingEvent, PingSuccess}; use log::{debug, trace, error}; +use std::error; use std::collections::hash_map::Entry; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; +use std::time::Duration; +use wasm_timer::Instant; use crate::utils::interval; /// Time after we disconnect from a node before we purge its information from the cache. @@ -251,6 +254,16 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static { self.identify.inject_new_external_addr(addr); } + fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn error::Error + 'static)) { + self.ping.inject_listener_error(id, err); + self.identify.inject_listener_error(id, err); + } + + fn inject_listener_closed(&mut self, id: ListenerId) { + self.ping.inject_listener_closed(id); + self.identify.inject_listener_closed(id); + } + fn poll( &mut self, cx: &mut Context, diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 11f687616e6491603522169f4a74dad26d51ed9f..2da69e189445726006363f35b328eb057c7f99df 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -47,7 +47,7 @@ use futures::prelude::*; use futures_timer::Delay; -use libp2p::core::{ConnectedPoint, Multiaddr, PeerId, PublicKey}; +use libp2p::core::{nodes::listeners::ListenerId, ConnectedPoint, Multiaddr, PeerId, PublicKey}; use libp2p::swarm::{ProtocolsHandler, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use libp2p::kad::{Kademlia, KademliaEvent, Quorum, Record}; use libp2p::kad::GetClosestPeersError; @@ -59,7 +59,7 @@ use libp2p::core::{nodes::Substream, muxing::StreamMuxerBox}; #[cfg(not(target_os = "unknown"))] use libp2p::mdns::{Mdns, MdnsEvent}; use libp2p::multiaddr::Protocol; -use log::{debug, info, trace, warn}; +use log::{debug, info, trace, warn, error}; use std::{cmp, collections::VecDeque, time::Duration}; use std::task::{Context, Poll}; use sp_core::hexdisplay::HexDisplay; @@ -87,6 +87,8 @@ pub struct DiscoveryBehaviour { /// If false, `addresses_of_peer` won't return any private IPv4 address, except for the ones /// stored in `user_defined`. allow_private_ipv4: bool, + /// Number of active connections over which we interrupt the discovery process. + discovery_only_if_under_num: u64, } impl DiscoveryBehaviour { @@ -98,6 +100,7 @@ impl DiscoveryBehaviour { user_defined: Vec<(PeerId, Multiaddr)>, enable_mdns: bool, allow_private_ipv4: bool, + discovery_only_if_under_num: u64, ) -> Self { if enable_mdns { #[cfg(target_os = "unknown")] @@ -120,9 +123,10 @@ impl DiscoveryBehaviour { local_peer_id: local_public_key.into_peer_id(), num_connections: 0, allow_private_ipv4, + discovery_only_if_under_num, #[cfg(not(target_os = "unknown"))] mdns: if enable_mdns { - match Mdns::new().await { + match Mdns::new() { Ok(mdns) => Some(mdns).into(), Err(err) => { warn!(target: "sub-libp2p", "Failed to initialize mDNS: {:?}", err); @@ -266,6 +270,15 @@ where NetworkBehaviour::inject_replaced(&mut self.kademlia, peer_id, closed, opened) } + fn inject_addr_reach_failure( + &mut self, + peer_id: Option<&PeerId>, + addr: &Multiaddr, + error: &dyn std::error::Error + ) { + NetworkBehaviour::inject_addr_reach_failure(&mut self.kademlia, peer_id, addr, error) + } + fn inject_node_event( &mut self, peer_id: PeerId, @@ -278,10 +291,30 @@ where let new_addr = addr.clone() .with(Protocol::P2p(self.local_peer_id.clone().into())); info!(target: "sub-libp2p", "Discovered new external address for our node: {}", new_addr); + NetworkBehaviour::inject_new_external_addr(&mut self.kademlia, addr) } fn inject_expired_listen_addr(&mut self, addr: &Multiaddr) { info!(target: "sub-libp2p", "No longer listening on {}", addr); + NetworkBehaviour::inject_expired_listen_addr(&mut self.kademlia, addr) + } + + fn inject_dial_failure(&mut self, peer_id: &PeerId) { + NetworkBehaviour::inject_dial_failure(&mut self.kademlia, peer_id) + } + + fn inject_new_listen_addr(&mut self, addr: &Multiaddr) { + NetworkBehaviour::inject_new_listen_addr(&mut self.kademlia, addr) + } + + fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) { + error!(target: "sub-libp2p", "Error on libp2p listener {:?}: {}", id, err); + NetworkBehaviour::inject_listener_error(&mut self.kademlia, id, err); + } + + fn inject_listener_closed(&mut self, id: ListenerId) { + error!(target: "sub-libp2p", "Libp2p listener {:?} closed", id); + NetworkBehaviour::inject_listener_closed(&mut self.kademlia, id); } fn poll( @@ -302,11 +335,19 @@ where // Poll the stream that fires when we need to start a random Kademlia query. while let Poll::Ready(_) = self.next_kad_random_query.poll_unpin(cx) { - let random_peer_id = PeerId::random(); - debug!(target: "sub-libp2p", "Libp2p <= Starting random Kademlia request for \ - {:?}", random_peer_id); + if self.num_connections < self.discovery_only_if_under_num { + let random_peer_id = PeerId::random(); + debug!(target: "sub-libp2p", "Libp2p <= Starting random Kademlia request for \ + {:?}", random_peer_id); - self.kademlia.get_closest_peers(random_peer_id); + self.kademlia.get_closest_peers(random_peer_id); + } else { + debug!( + target: "sub-libp2p", + "Kademlia paused due to high number of connections ({})", + self.num_connections + ); + } // Schedule the next random query with exponentially increasing delay, // capped at 60 seconds. @@ -406,6 +447,10 @@ where NetworkBehaviourAction::GenerateEvent(event) => { match event { MdnsEvent::Discovered(list) => { + if self.num_connections >= self.discovery_only_if_under_num { + continue; + } + self.discoveries.extend(list.into_iter().map(|(peer_id, _)| peer_id)); if let Some(peer_id) = self.discoveries.pop_front() { let ev = DiscoveryOut::Discovered(peer_id); @@ -473,7 +518,7 @@ mod tests { let user_defined = user_defined.clone(); let keypair_public = keypair.public(); async move { - DiscoveryBehaviour::new(keypair_public, user_defined, false, true).await + DiscoveryBehaviour::new(keypair_public, user_defined, false, true, 50).await } }); let mut swarm = Swarm::new(transport, behaviour, keypair.public().into_peer_id()); diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 6914ea9efe138427b437574aff1a050c4eb59916..8e5f9d6e7bc8f4b89d1cdca5c1d4d730a892efdf 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -20,7 +20,7 @@ use crate::utils::interval; use bytes::{Bytes, BytesMut}; use futures::prelude::*; use libp2p::{Multiaddr, PeerId}; -use libp2p::core::{ConnectedPoint, nodes::Substream, muxing::StreamMuxerBox}; +use libp2p::core::{ConnectedPoint, nodes::{listeners::ListenerId, Substream}, muxing::StreamMuxerBox}; use libp2p::swarm::{ProtocolsHandler, IntoProtocolsHandler}; use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use sp_core::storage::{StorageKey, ChildInfo}; @@ -52,16 +52,32 @@ use crate::chain::{Client, FinalityProofProvider}; use sc_client_api::{FetchChecker, ChangesProof, StorageProof}; use crate::error; use util::LruHashSet; +use wasm_timer::Instant; + +// Include sources generated from protobuf definitions. +pub mod api { + pub mod v1 { + include!(concat!(env!("OUT_DIR"), "/api.v1.rs")); + pub mod light { + include!(concat!(env!("OUT_DIR"), "/api.v1.light.rs")); + } + } +} mod legacy_proto; mod util; +pub mod block_requests; pub mod message; pub mod event; +pub mod light_client_handler; pub mod light_dispatch; pub mod specialization; pub mod sync; +pub use block_requests::BlockRequests; +pub use light_client_handler::LightClientHandler; + const REQUEST_TIMEOUT_SEC: u64 = 40; /// Interval at which we perform time based maintenance const TICK_TIMEOUT: time::Duration = time::Duration::from_millis(1100); @@ -158,7 +174,7 @@ struct PacketStats { /// A peer that we are connected to /// and from whom we have not yet received a Status message. struct HandshakingPeer { - timestamp: time::Instant, + timestamp: Instant, } /// Peer information @@ -166,9 +182,9 @@ struct HandshakingPeer { struct Peer { info: PeerInfo, /// Current block request, if any. - block_request: Option<(time::Instant, message::BlockRequest)>, + block_request: Option<(Instant, message::BlockRequest)>, /// Requests we are no longer insterested in. - obsolete_requests: HashMap, + obsolete_requests: HashMap, /// Holds a set of transactions known to this peer. known_extrinsics: LruHashSet, /// Holds a set of blocks known to this peer. @@ -701,7 +717,7 @@ impl, H: ExHashT> Protocol { /// Called when a new peer is connected pub fn on_peer_connected(&mut self, who: PeerId) { trace!(target: "sync", "Connecting {}", who); - self.handshaking_peers.insert(who.clone(), HandshakingPeer { timestamp: time::Instant::now() }); + self.handshaking_peers.insert(who.clone(), HandshakingPeer { timestamp: Instant::now() }); self.send_status(who); } @@ -890,7 +906,7 @@ impl, H: ExHashT> Protocol { } fn maintain_peers(&mut self) { - let tick = time::Instant::now(); + let tick = Instant::now(); let mut aborting = Vec::new(); { for (who, peer) in self.context_data.peers.iter() { @@ -1135,18 +1151,26 @@ impl, H: ExHashT> Protocol { } } - /// Call when we must propagate ready extrinsics to peers. - pub fn propagate_extrinsics( + /// Propagate one extrinsic. + pub fn propagate_extrinsic( &mut self, + hash: &H, ) { - debug!(target: "sync", "Propagating extrinsics"); - + debug!(target: "sync", "Propagating extrinsic [{:?}]", hash); // Accept transactions only when fully synced if self.sync.status().state != SyncState::Idle { return; } + if let Some(extrinsic) = self.transaction_pool.transaction(hash) { + let propagated_to = self.do_propagate_extrinsics(&[(hash.clone(), extrinsic)]); + self.transaction_pool.on_broadcasted(propagated_to); + } + } - let extrinsics = self.transaction_pool.transactions(); + fn do_propagate_extrinsics( + &mut self, + extrinsics: &[(H, B::Extrinsic)], + ) -> HashMap> { let mut propagated_to = HashMap::new(); for (who, peer) in self.context_data.peers.iter_mut() { // never send extrinsics to the light node @@ -1177,6 +1201,18 @@ impl, H: ExHashT> Protocol { } } + propagated_to + } + + /// Call when we must propagate ready extrinsics to peers. + pub fn propagate_extrinsics(&mut self) { + debug!(target: "sync", "Propagating extrinsics"); + // Accept transactions only when fully synced + if self.sync.status().state != SyncState::Idle { + return; + } + let extrinsics = self.transaction_pool.transactions(); + let propagated_to = self.do_propagate_extrinsics(&extrinsics); self.transaction_pool.on_broadcasted(propagated_to); } @@ -1792,7 +1828,7 @@ pub enum CustomMessageOutcome { /// Notification protocols have been opened with a remote. NotificationStreamOpened { remote: PeerId, protocols: Vec, roles: Roles }, /// Notification protocols have been closed with a remote. - NotificationsStreamClosed { remote: PeerId, protocols: Vec }, + NotificationStreamClosed { remote: PeerId, protocols: Vec }, /// Messages have been received on one or more notifications protocols. NotificationsReceived { remote: PeerId, messages: Vec<(ConsensusEngineId, Bytes)> }, None, @@ -1813,7 +1849,7 @@ fn send_request( trace!(target: "sync", "Request {} for {} is now obsolete.", request.id, who); peer.obsolete_requests.insert(request.id, timestamp); } - peer.block_request = Some((time::Instant::now(), r.clone())); + peer.block_request = Some((Instant::now(), r.clone())); } } send_message::(behaviour, stats, who, message) @@ -1931,7 +1967,7 @@ Protocol { LegacyProtoOut::CustomProtocolClosed { peer_id, .. } => { self.on_peer_disconnected(peer_id.clone()); // Notify all the notification protocols as closed. - CustomMessageOutcome::NotificationsStreamClosed { + CustomMessageOutcome::NotificationStreamClosed { remote: peer_id, protocols: self.registered_notif_protocols.iter().cloned().collect(), } @@ -1984,6 +2020,14 @@ Protocol { fn inject_new_external_addr(&mut self, addr: &Multiaddr) { self.behaviour.inject_new_external_addr(addr) } + + fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) { + self.behaviour.inject_listener_error(id, err); + } + + fn inject_listener_closed(&mut self, id: ListenerId) { + self.behaviour.inject_listener_closed(id); + } } impl, H: ExHashT> DiscoveryNetBehaviour for Protocol { diff --git a/client/network/src/protocol/block_requests.rs b/client/network/src/protocol/block_requests.rs new file mode 100644 index 0000000000000000000000000000000000000000..f8a905c288b9d470adc8b2625daea87ac195a5fa --- /dev/null +++ b/client/network/src/protocol/block_requests.rs @@ -0,0 +1,354 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. +// +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! `NetworkBehaviour` implementation which handles incoming block requests. +//! +//! Every request is coming in on a separate connection substream which gets +//! closed after we have sent the response back. Incoming requests are encoded +//! as protocol buffers (cf. `api.v1.proto`). + +#![allow(unused)] + +use bytes::Bytes; +use codec::{Encode, Decode}; +use crate::{ + chain::Client, + config::ProtocolId, + protocol::{api, message::BlockAttributes} +}; +use futures::{future::BoxFuture, prelude::*, stream::FuturesUnordered}; +use libp2p::{ + core::{ + ConnectedPoint, + Multiaddr, + PeerId, + upgrade::{InboundUpgrade, ReadOneError, UpgradeInfo, Negotiated}, + upgrade::{DeniedUpgrade, read_one, write_one} + }, + swarm::{NetworkBehaviour, NetworkBehaviourAction, OneShotHandler, PollParameters, SubstreamProtocol} +}; +use prost::Message; +use sp_runtime::{generic::BlockId, traits::{Block, Header, One, Zero}}; +use std::{ + cmp::min, + io, + iter, + sync::Arc, + time::Duration, + task::{Context, Poll} +}; +use void::{Void, unreachable}; + +// Type alias for convenience. +pub type Error = Box; + +/// Configuration options for `BlockRequests`. +#[derive(Debug, Clone)] +pub struct Config { + max_block_data_response: u32, + max_request_len: usize, + inactivity_timeout: Duration, + protocol: Bytes, +} + +impl Config { + /// Create a fresh configuration with the following options: + /// + /// - max. block data in response = 128 + /// - max. request size = 1 MiB + /// - inactivity timeout = 15s + pub fn new(id: &ProtocolId) -> Self { + let mut c = Config { + max_block_data_response: 128, + max_request_len: 1024 * 1024, + inactivity_timeout: Duration::from_secs(15), + protocol: Bytes::new(), + }; + c.set_protocol(id); + c + } + + /// Limit the max. number of block data in a response. + pub fn set_max_block_data_response(&mut self, v: u32) -> &mut Self { + self.max_block_data_response = v; + self + } + + /// Limit the max. length of incoming block request bytes. + pub fn set_max_request_len(&mut self, v: usize) -> &mut Self { + self.max_request_len = v; + self + } + + /// Limit the max. duration the substream may remain inactive before closing it. + pub fn set_inactivity_timeout(&mut self, v: Duration) -> &mut Self { + self.inactivity_timeout = v; + self + } + + /// Set protocol to use for upgrade negotiation. + pub fn set_protocol(&mut self, id: &ProtocolId) -> &mut Self { + let mut v = Vec::new(); + v.extend_from_slice(b"/"); + v.extend_from_slice(id.as_bytes()); + v.extend_from_slice(b"/sync/1"); + self.protocol = v.into(); + self + } +} + +/// The block request handling behaviour. +pub struct BlockRequests { + /// This behaviour's configuration. + config: Config, + /// Blockchain client. + chain: Arc>, + /// Futures sending back the block request response. + outgoing: FuturesUnordered>, + /// Type witness term. + _marker: std::marker::PhantomData +} + +impl BlockRequests +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static, + B: Block, +{ + pub fn new(cfg: Config, chain: Arc>) -> Self { + BlockRequests { + config: cfg, + chain, + outgoing: FuturesUnordered::new(), + _marker: std::marker::PhantomData + } + } + + /// Callback, invoked when a new block request has been received from remote. + fn on_block_request + ( &mut self + , peer: &PeerId + , request: &api::v1::BlockRequest + ) -> Result + { + log::trace!("block request {} from peer {}: from block {:?} to block {:?}, max blocks {:?}", + request.id, + peer, + request.from_block, + request.to_block, + request.max_blocks); + + let from_block_id = + match request.from_block { + Some(api::v1::block_request::FromBlock::Hash(ref h)) => { + let h = Decode::decode(&mut h.as_ref())?; + BlockId::::Hash(h) + } + Some(api::v1::block_request::FromBlock::Number(ref n)) => { + let n = Decode::decode(&mut n.as_ref())?; + BlockId::::Number(n) + } + None => { + let msg = "missing `BlockRequest::from_block` field"; + return Err(io::Error::new(io::ErrorKind::Other, msg).into()) + } + }; + + let max_blocks = + if request.max_blocks == 0 { + self.config.max_block_data_response + } else { + min(request.max_blocks, self.config.max_block_data_response) + }; + + let direction = + if request.direction == api::v1::Direction::Ascending as i32 { + api::v1::Direction::Ascending + } else if request.direction == api::v1::Direction::Descending as i32 { + api::v1::Direction::Descending + } else { + let msg = format!("invalid `BlockRequest::direction` value: {}", request.direction); + return Err(io::Error::new(io::ErrorKind::Other, msg).into()) + }; + + let attributes = BlockAttributes::decode(&mut request.fields.to_be_bytes().as_ref())?; + let get_header = attributes.contains(BlockAttributes::HEADER); + let get_body = attributes.contains(BlockAttributes::BODY); + let get_justification = attributes.contains(BlockAttributes::JUSTIFICATION); + + let mut blocks = Vec::new(); + let mut block_id = from_block_id; + while let Some(header) = self.chain.header(&block_id).unwrap_or(None) { + if blocks.len() >= max_blocks as usize { + break + } + + let number = header.number().clone(); + let hash = header.hash(); + let parent_hash = header.parent_hash().clone(); + + let block_data = api::v1::BlockData { + hash: hash.encode(), + header: if get_header { + header.encode() + } else { + Vec::new() + }, + body: if get_body { + self.chain.body(&BlockId::Hash(hash))? + .unwrap_or(Vec::new()) + .iter_mut() + .map(|extrinsic| extrinsic.encode()) + .collect() + } else { + Vec::new() + }, + receipt: Vec::new(), + message_queue: Vec::new(), + justification: if get_justification { + self.chain.justification(&BlockId::Hash(hash))?.unwrap_or(Vec::new()) + } else { + Vec::new() + } + }; + + blocks.push(block_data); + + match direction { + api::v1::Direction::Ascending => { + block_id = BlockId::Number(number + One::one()) + } + api::v1::Direction::Descending => { + if number.is_zero() { + break + } + block_id = BlockId::Hash(parent_hash) + } + } + } + + Ok(api::v1::BlockResponse { id: request.id, blocks }) + } +} + +impl NetworkBehaviour for BlockRequests +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static, + B: Block +{ + type ProtocolsHandler = OneShotHandler>>; + type OutEvent = Void; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + let p = Protocol { + max_request_len: self.config.max_request_len, + protocol: self.config.protocol.clone(), + }; + OneShotHandler::new(SubstreamProtocol::new(p), self.config.inactivity_timeout) + } + + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + Vec::new() + } + + fn inject_connected(&mut self, _peer: PeerId, _info: ConnectedPoint) { + } + + fn inject_disconnected(&mut self, _peer: &PeerId, _info: ConnectedPoint) { + } + + fn inject_node_event(&mut self, peer: PeerId, Request(request, mut stream): Request>) { + match self.on_block_request(&peer, &request) { + Ok(res) => { + log::trace!("enqueueing block response {} for peer {} with {} blocks", res.id, peer, res.blocks.len()); + let mut data = Vec::with_capacity(res.encoded_len()); + if let Err(e) = res.encode(&mut data) { + log::debug!("error encoding block response {} for peer {}: {}", res.id, peer, e) + } else { + let future = async move { + if let Err(e) = write_one(&mut stream, data).await { + log::debug!("error writing block response: {}", e) + } + }; + self.outgoing.push(future.boxed()) + } + } + Err(e) => log::debug!("error handling block request {} from peer {}: {}", request.id, peer, e) + } + } + + fn poll(&mut self, cx: &mut Context, _: &mut impl PollParameters) -> Poll> { + while let Poll::Ready(Some(_)) = self.outgoing.poll_next_unpin(cx) {} + Poll::Pending + } +} + +/// The incoming block request. +/// +/// Holds the protobuf value and the connection substream which made the +/// request and over which to send the response. +#[derive(Debug)] +pub struct Request(api::v1::BlockRequest, T); + +impl From for Request { + fn from(v: Void) -> Self { + unreachable(v) + } +} + +/// Substream upgrade protocol. +/// +/// We attempt to parse an incoming protobuf encoded request (cf. `Request`) +/// which will be handled by the `BlockRequests` behaviour, i.e. the request +/// will become visible via `inject_node_event` which then dispatches to the +/// relevant callback to process the message and prepare a response. +#[derive(Debug, Clone)] +pub struct Protocol { + /// The max. request length in bytes. + max_request_len: usize, + /// The protocol to use during upgrade negotiation. + protocol: Bytes, +} + +impl UpgradeInfo for Protocol { + type Info = Bytes; + type InfoIter = iter::Once; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(self.protocol.clone()) + } +} + +impl InboundUpgrade for Protocol +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static +{ + type Output = Request; + type Error = ReadOneError; + type Future = BoxFuture<'static, Result>; + + fn upgrade_inbound(self, mut s: T, _: Self::Info) -> Self::Future { + let future = async move { + let len = self.max_request_len; + let vec = read_one(&mut s, len).await?; + match api::v1::BlockRequest::decode(&vec[..]) { + Ok(r) => Ok(Request(r, s)), + Err(e) => Err(ReadOneError::Io(io::Error::new(io::ErrorKind::Other, e))) + } + }; + future.boxed() + } +} + diff --git a/client/network/src/protocol/event.rs b/client/network/src/protocol/event.rs index 47bf057505db7c5a77e716ca2c1c8c372493ef60..e239e9d9831821d524434d311a26ed47d02d0d4d 100644 --- a/client/network/src/protocol/event.rs +++ b/client/network/src/protocol/event.rs @@ -61,7 +61,7 @@ pub enum Event { /// Closed a substream with the given node. Always matches a corresponding previous /// `NotificationStreamOpened` message. - NotificationsStreamClosed { + NotificationStreamClosed { /// Node we closed the substream with. remote: PeerId, /// The concerned protocol. Each protocol uses a different substream. diff --git a/client/network/src/protocol/legacy_proto/behaviour.rs b/client/network/src/protocol/legacy_proto/behaviour.rs index b4047f320ac2a2228cbedde7b6f7fcef41f8440a..1eb6b157ddc65abcd1827ffcbb75183423431209 100644 --- a/client/network/src/protocol/legacy_proto/behaviour.rs +++ b/client/network/src/protocol/legacy_proto/behaviour.rs @@ -26,7 +26,8 @@ use log::{debug, error, trace, warn}; use rand::distributions::{Distribution as _, Uniform}; use smallvec::SmallVec; use std::{borrow::Cow, collections::hash_map::Entry, cmp, error, marker::PhantomData, mem, pin::Pin}; -use std::time::{Duration, Instant}; +use std::time::Duration; +use wasm_timer::Instant; use std::task::{Context, Poll}; /// Network behaviour that handles opening substreams for custom protocols with other nodes. @@ -387,7 +388,7 @@ impl LegacyProto { debug!(target: "sub-libp2p", "PSM => Connect({:?}): Will start to connect at \ until {:?}", occ_entry.key(), until); *occ_entry.into_mut() = PeerState::PendingRequest { - timer: futures_timer::Delay::new_at(until.clone()), + timer: futures_timer::Delay::new(until.clone() - Instant::now()), timer_deadline: until.clone(), }; }, @@ -406,7 +407,7 @@ impl LegacyProto { *occ_entry.into_mut() = PeerState::DisabledPendingEnable { connected_point: connected_point.clone(), open, - timer: futures_timer::Delay::new_at(banned.clone()), + timer: futures_timer::Delay::new(banned.clone() - Instant::now()), timer_deadline: banned.clone(), }; }, diff --git a/client/network/src/protocol/legacy_proto/handler.rs b/client/network/src/protocol/legacy_proto/handler.rs index f042d1b88925f409981146995995884304849d5e..66fb0dca13a226bddf0b3d2e53d05a1408e302a3 100644 --- a/client/network/src/protocol/legacy_proto/handler.rs +++ b/client/network/src/protocol/legacy_proto/handler.rs @@ -18,7 +18,7 @@ use super::upgrade::{RegisteredProtocol, RegisteredProtocolEvent, RegisteredProt use bytes::BytesMut; use futures::prelude::*; use futures_timer::Delay; -use libp2p::core::{ConnectedPoint, PeerId, Endpoint}; +use libp2p::core::{ConnectedPoint, Negotiated, PeerId, Endpoint}; use libp2p::core::upgrade::{InboundUpgrade, OutboundUpgrade}; use libp2p::swarm::{ ProtocolsHandler, ProtocolsHandlerEvent, @@ -159,7 +159,7 @@ enum ProtocolState { /// Waiting for the behaviour to tell the handler whether it is enabled or disabled. Init { /// List of substreams opened by the remote but that haven't been processed yet. - substreams: SmallVec<[RegisteredProtocolSubstream; 6]>, + substreams: SmallVec<[RegisteredProtocolSubstream>; 6]>, /// Deadline after which the initialization is abnormally long. init_deadline: Delay, }, @@ -175,9 +175,9 @@ enum ProtocolState { /// If we are in this state, we have sent a `CustomProtocolOpen` message to the outside. Normal { /// The substreams where bidirectional communications happen. - substreams: SmallVec<[RegisteredProtocolSubstream; 4]>, + substreams: SmallVec<[RegisteredProtocolSubstream>; 4]>, /// Contains substreams which are being shut down. - shutdown: SmallVec<[RegisteredProtocolSubstream; 4]>, + shutdown: SmallVec<[RegisteredProtocolSubstream>; 4]>, }, /// We are disabled. Contains substreams that are being closed. @@ -185,7 +185,7 @@ enum ProtocolState { /// outside or we have never sent any `CustomProtocolOpen` in the first place. Disabled { /// List of substreams to shut down. - shutdown: SmallVec<[RegisteredProtocolSubstream; 6]>, + shutdown: SmallVec<[RegisteredProtocolSubstream>; 6]>, /// If true, we should reactivate the handler after all the substreams in `shutdown` have /// been closed. @@ -348,13 +348,12 @@ where ProtocolState::Init { substreams, mut init_deadline } => { match Pin::new(&mut init_deadline).poll(cx) { - Poll::Ready(Ok(())) => { + Poll::Ready(()) => { init_deadline = Delay::new(Duration::from_secs(60)); error!(target: "sub-libp2p", "Handler initialization process is too long \ with {:?}", self.remote_peer_id) }, Poll::Pending => {} - Poll::Ready(Err(_)) => error!(target: "sub-libp2p", "Tokio timer has errored") } self.state = ProtocolState::Init { substreams, init_deadline }; @@ -363,7 +362,7 @@ where ProtocolState::Opening { mut deadline } => { match Pin::new(&mut deadline).poll(cx) { - Poll::Ready(Ok(())) => { + Poll::Ready(()) => { deadline = Delay::new(Duration::from_secs(60)); let event = CustomProtoHandlerOut::ProtocolError { is_severe: true, @@ -376,12 +375,6 @@ where self.state = ProtocolState::Opening { deadline }; None }, - Poll::Ready(Err(_)) => { - error!(target: "sub-libp2p", "Tokio timer has errored"); - deadline = Delay::new(Duration::from_secs(60)); - self.state = ProtocolState::Opening { deadline }; - None - }, } } @@ -466,7 +459,7 @@ where /// Called by `inject_fully_negotiated_inbound` and `inject_fully_negotiated_outbound`. fn inject_fully_negotiated( &mut self, - mut substream: RegisteredProtocolSubstream + mut substream: RegisteredProtocolSubstream> ) { self.state = match mem::replace(&mut self.state, ProtocolState::Poisoned) { ProtocolState::Poisoned => { @@ -538,14 +531,14 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin { fn inject_fully_negotiated_inbound( &mut self, - proto: >::Output + proto: >>::Output ) { self.inject_fully_negotiated(proto); } fn inject_fully_negotiated_outbound( &mut self, - proto: >::Output, + proto: >>::Output, _: Self::OutboundOpenInfo ) { self.inject_fully_negotiated(proto); @@ -621,7 +614,7 @@ where /// Given a list of substreams, tries to shut them down. The substreams that have been successfully /// shut down are removed from the list. fn shutdown_list - (list: &mut SmallVec>>, + (list: &mut SmallVec>>>, cx: &mut Context) where TSubstream: AsyncRead + AsyncWrite + Unpin { 'outer: for n in (0..list.len()).rev() { diff --git a/client/network/src/protocol/legacy_proto/tests.rs b/client/network/src/protocol/legacy_proto/tests.rs index 6a2174f30c9370bc3b26410a86a9ae264fb2b797..4f523fa31dc4863ecb937a4026c35e541c4ce5fa 100644 --- a/client/network/src/protocol/legacy_proto/tests.rs +++ b/client/network/src/protocol/legacy_proto/tests.rs @@ -18,13 +18,13 @@ use futures::{prelude::*, ready}; use codec::{Encode, Decode}; -use libp2p::core::nodes::Substream; +use libp2p::core::nodes::{Substream, listeners::ListenerId}; use libp2p::core::{ConnectedPoint, transport::boxed::Boxed, muxing::StreamMuxerBox}; use libp2p::swarm::{Swarm, ProtocolsHandler, IntoProtocolsHandler}; use libp2p::swarm::{PollParameters, NetworkBehaviour, NetworkBehaviourAction}; use libp2p::{PeerId, Multiaddr, Transport}; use rand::seq::SliceRandom; -use std::{io, task::Context, task::Poll, time::Duration}; +use std::{error, io, task::Context, task::Poll, time::Duration}; use crate::message::Message; use crate::protocol::legacy_proto::{LegacyProto, LegacyProtoOut}; use sp_test_primitives::Block; @@ -204,6 +204,14 @@ impl NetworkBehaviour for CustomProtoWithAddr { fn inject_new_external_addr(&mut self, addr: &Multiaddr) { self.inner.inject_new_external_addr(addr) } + + fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn error::Error + 'static)) { + self.inner.inject_listener_error(id, err); + } + + fn inject_listener_closed(&mut self, id: ListenerId) { + self.inner.inject_listener_closed(id); + } } #[test] @@ -401,7 +409,7 @@ fn reconnect_after_disconnect() { _ => panic!() } - if let Poll::Ready(Ok(_)) = delay.poll_unpin(cx) { + if let Poll::Ready(()) = delay.poll_unpin(cx) { Poll::Ready(Ok(())) } else { Poll::Pending diff --git a/client/network/src/protocol/legacy_proto/upgrade.rs b/client/network/src/protocol/legacy_proto/upgrade.rs index c021d5917f4137910e02cc826169e7f0e0a9ff35..311e0b04f9724072535cad8dc429d2c1989c4c5d 100644 --- a/client/network/src/protocol/legacy_proto/upgrade.rs +++ b/client/network/src/protocol/legacy_proto/upgrade.rs @@ -18,7 +18,7 @@ use crate::config::ProtocolId; use bytes::BytesMut; use futures::prelude::*; use futures_codec::Framed; -use libp2p::core::{Negotiated, Endpoint, UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::ProtocolName}; +use libp2p::core::{Endpoint, UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::ProtocolName}; use std::{collections::VecDeque, io, pin::Pin, vec::IntoIter as VecIntoIter}; use std::task::{Context, Poll}; use unsigned_varint::codec::UviBytes; @@ -82,7 +82,7 @@ pub struct RegisteredProtocolSubstream { /// If true, we should call `poll_complete` on the inner sink. requires_poll_flush: bool, /// The underlying substream. - inner: stream::Fuse, UviBytes>>, + inner: stream::Fuse>>, /// Version of the protocol that was negotiated. protocol_version: u8, /// If true, we have sent a "remote is clogged" event recently and shouldn't send another one @@ -250,7 +250,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin, fn upgrade_inbound( self, - socket: Negotiated, + socket: TSubstream, info: Self::Info, ) -> Self::Future { let framed = { @@ -280,7 +280,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin, fn upgrade_outbound( self, - socket: Negotiated, + socket: TSubstream, info: Self::Info, ) -> Self::Future { let framed = Framed::new(socket, UviBytes::default()); diff --git a/client/network/src/protocol/light_client_handler.rs b/client/network/src/protocol/light_client_handler.rs new file mode 100644 index 0000000000000000000000000000000000000000..f5be23c0d4d49970b54bb79c0f2021fe484b12ee --- /dev/null +++ b/client/network/src/protocol/light_client_handler.rs @@ -0,0 +1,1793 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. +// +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! [`NetworkBehaviour`] implementation which handles light client requests. +//! +//! Every request is coming in on a separate connection substream which gets +//! closed after we have sent the response back. Requests and responses are +//! encoded as protocol buffers (cf. `api.v1.proto`). +//! +//! For every outgoing request we likewise open a separate substream. + +#![allow(unused)] + +use bytes::Bytes; +use codec::{self, Encode, Decode}; +use crate::{ + chain::Client, + config::ProtocolId, + protocol::{api, light_dispatch::TIMEOUT_REPUTATION_CHANGE} +}; +use futures::{channel::oneshot, future::BoxFuture, prelude::*, stream::FuturesUnordered}; +use libp2p::{ + core::{ + ConnectedPoint, + Multiaddr, + PeerId, + upgrade::{InboundUpgrade, ReadOneError, UpgradeInfo, Negotiated}, + upgrade::{OutboundUpgrade, read_one, write_one} + }, + swarm::{NetworkBehaviour, NetworkBehaviourAction, OneShotHandler, PollParameters, SubstreamProtocol} +}; +use nohash_hasher::IntMap; +use prost::Message; +use rustc_hex::ToHex; +use sc_client::light::fetcher; +use sc_client_api::StorageProof; +use sc_peerset::ReputationChange; +use sp_core::storage::{ChildInfo, StorageKey}; +use sp_blockchain::{Error as ClientError}; +use sp_runtime::traits::{Block, Header, NumberFor, Zero}; +use std::{ + collections::{BTreeMap, VecDeque, HashMap}, + iter, + io, + sync::Arc, + time::{Duration, Instant}, + task::{Context, Poll} +}; +use void::Void; + +/// Configuration options for `LightClientHandler` behaviour. +#[derive(Debug, Clone)] +pub struct Config { + max_data_size: usize, + max_pending_requests: usize, + inactivity_timeout: Duration, + request_timeout: Duration, + protocol: Bytes, +} + +impl Config { + /// Create a fresh configuration with the following options: + /// + /// - max. data size = 1 MiB + /// - max. pending requests = 128 + /// - inactivity timeout = 15s + /// - request timeout = 15s + pub fn new(id: &ProtocolId) -> Self { + let mut c = Config { + max_data_size: 1024 * 1024, + max_pending_requests: 128, + inactivity_timeout: Duration::from_secs(15), + request_timeout: Duration::from_secs(15), + protocol: Bytes::new(), + }; + c.set_protocol(id); + c + } + + /// Limit the max. length of incoming request bytes. + pub fn set_max_data_size(&mut self, v: usize) -> &mut Self { + self.max_data_size = v; + self + } + + /// Limit the max. number of pending requests. + pub fn set_max_pending_requests(&mut self, v: usize) -> &mut Self { + self.max_pending_requests = v; + self + } + + /// Limit the max. duration the connection may remain inactive before closing it. + pub fn set_inactivity_timeout(&mut self, v: Duration) -> &mut Self { + self.inactivity_timeout = v; + self + } + + /// Limit the max. request duration. + pub fn set_request_timeout(&mut self, v: Duration) -> &mut Self { + self.request_timeout = v; + self + } + + /// Set protocol to use for upgrade negotiation. + pub fn set_protocol(&mut self, id: &ProtocolId) -> &mut Self { + let mut v = Vec::new(); + v.extend_from_slice(b"/"); + v.extend_from_slice(id.as_bytes()); + v.extend_from_slice(b"/light/1"); + self.protocol = v.into(); + self + } +} + +/// Possible errors while handling light clients. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// There are currently too many pending request. + #[error("too many pending requests")] + TooManyRequests, + /// The response type does not correspond to the issued request. + #[error("unexpected response")] + UnexpectedResponse, + /// A bad request has been received. + #[error("bad request: {0}")] + BadRequest(&'static str), + /// The chain client errored. + #[error("client error: {0}")] + Client(#[from] ClientError), + /// Encoding or decoding of some data failed. + #[error("codec error: {0}")] + Codec(#[from] codec::Error), +} + +/// The possible light client requests we support. +/// +/// The associated `oneshot::Sender` will be used to convey the result of +/// their request back to them (cf. `Reply`). +// +// This is modeled after light_dispatch.rs's `RequestData` which is not +// used because we currently only support a subset of those. +#[derive(Debug)] +pub enum Request { + Header { + request: fetcher::RemoteHeaderRequest, + sender: oneshot::Sender> + }, + Read { + request: fetcher::RemoteReadRequest, + sender: oneshot::Sender, Option>>, ClientError>> + }, + ReadChild { + request: fetcher::RemoteReadChildRequest, + sender: oneshot::Sender, Option>>, ClientError>> + }, + Call { + request: fetcher::RemoteCallRequest, + sender: oneshot::Sender, ClientError>> + }, + Changes { + request: fetcher::RemoteChangesRequest, + sender: oneshot::Sender, u32)>, ClientError>> + } +} + +/// The data to send back to the light client over the oneshot channel. +// +// It is unified here in order to be able to return it as a function +// result instead of delivering it to the client as a side effect of +// response processing. +#[derive(Debug)] +enum Reply { + VecU8(Vec), + VecNumberU32(Vec<(::Number, u32)>), + MapVecU8OptVecU8(HashMap, Option>>), + Header(B::Header) +} + +/// Augments a light client request with metadata. +#[derive(Debug)] +struct RequestWrapper { + /// Time when this value was created. + timestamp: Instant, + /// Remaining retries. + retries: usize, + /// The actual request. + request: Request, + /// Peer information, e.g. `PeerId`. + peer: P +} + +/// Information we have about some peer. +#[derive(Debug)] +struct PeerInfo { + address: Multiaddr, + best_block: Option>, + status: PeerStatus, +} + +/// A peer is either idle or busy processing a request from us. +#[derive(Debug, Clone, PartialEq, Eq)] +enum PeerStatus { + /// The peer is available. + Idle, + /// We wait for the peer to return us a response for the given request ID. + BusyWith(u64), +} + +/// The light client handler behaviour. +pub struct LightClientHandler { + /// This behaviour's configuration. + config: Config, + /// Blockchain client. + chain: Arc>, + /// Verifies that received responses are correct. + checker: Arc>, + /// Peer information (addresses, their best block, etc.) + peers: HashMap>, + /// Futures sending back response to remote clients. + responses: FuturesUnordered>, + /// Pending (local) requests. + pending_requests: VecDeque>, + /// Requests on their way to remote peers. + outstanding: IntMap>, + /// (Local) Request ID counter + next_request_id: u64, + /// Handle to use for reporting misbehaviour of peers. + peerset: sc_peerset::PeersetHandle, + /// Type witness term. + _marker: std::marker::PhantomData +} + +impl LightClientHandler +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static, + B: Block, +{ + /// Construct a new light client handler. + pub fn new + ( cfg: Config + , chain: Arc> + , checker: Arc> + , peerset: sc_peerset::PeersetHandle + ) -> Self + { + LightClientHandler { + config: cfg, + chain, + checker, + peers: HashMap::new(), + responses: FuturesUnordered::new(), + pending_requests: VecDeque::new(), + outstanding: IntMap::default(), + next_request_id: 1, + peerset, + _marker: std::marker::PhantomData + } + } + + /// We rely on external information about peers best blocks as we lack the + /// means to determine it ourselves. + pub fn update_best_block(&mut self, peer: &PeerId, num: NumberFor) { + if let Some(info) = self.peers.get_mut(peer) { + info.best_block = Some(num) + } + } + + /// Issue a new light client request. + pub fn request(&mut self, req: Request) -> Result<(), Error> { + if self.pending_requests.len() >= self.config.max_pending_requests { + return Err(Error::TooManyRequests) + } + let rw = RequestWrapper { + timestamp: Instant::now(), + retries: retries(&req), + request: req, + peer: (), // we do not know the peer yet + }; + self.pending_requests.push_back(rw); + Ok(()) + } + + fn next_request_id(&mut self) -> u64 { + let id = self.next_request_id; + self.next_request_id += 1; + id + } + + // Iterate over peers known to possess a certain block. + fn idle_peers_with_block(&mut self, num: NumberFor) -> impl Iterator + '_ { + self.peers.iter() + .filter(move |(_, info)| { + info.status == PeerStatus::Idle && info.best_block >= Some(num) + }) + .map(|(peer, _)| peer.clone()) + } + + // Iterate over peers without a known block. + fn idle_peers_with_unknown_block(&mut self) -> impl Iterator + '_ { + self.peers.iter() + .filter(|(_, info)| { + info.status == PeerStatus::Idle && info.best_block.is_none() + }) + .map(|(peer, _)| peer.clone()) + } + + /// Remove the given peer. + /// + /// If we have a request to this peer in flight, we move it back to + /// the pending requests queue. + fn remove_peer(&mut self, peer: &PeerId) { + if let Some(id) = self.outstanding.iter().find(|(_, rw)| &rw.peer == peer).map(|(k, _)| *k) { + let rw = self.outstanding.remove(&id).expect("key belongs to entry in this map"); + let rw = RequestWrapper { + timestamp: rw.timestamp, + retries: rw.retries, + request: rw.request, + peer: (), // need to find another peer + }; + self.pending_requests.push_back(rw); + } + self.peers.remove(peer); + } + + /// Process a local request's response from remote. + /// + /// If successful, this will give us the actual, checked data we should be + /// sending back to the client, otherwise an error. + fn on_response + ( &mut self + , peer: &PeerId + , request: &Request + , response: api::v1::light::Response + ) -> Result, Error> + { + log::trace!("response {} from {}", response.id, peer); + use api::v1::light::response::Response; + match response.response { + Some(Response::RemoteCallResponse(response)) => + if let Request::Call { request , .. } = request { + let proof = Decode::decode(&mut response.proof.as_ref())?; + let reply = self.checker.check_execution_proof(request, proof)?; + Ok(Reply::VecU8(reply)) + } else { + Err(Error::UnexpectedResponse) + } + Some(Response::RemoteReadResponse(response)) => + match request { + Request::Read { request, .. } => { + let proof = Decode::decode(&mut response.proof.as_ref())?; + let reply = self.checker.check_read_proof(&request, proof)?; + Ok(Reply::MapVecU8OptVecU8(reply)) + } + Request::ReadChild { request, .. } => { + let proof = Decode::decode(&mut response.proof.as_ref())?; + let reply = self.checker.check_read_child_proof(&request, proof)?; + Ok(Reply::MapVecU8OptVecU8(reply)) + } + _ => Err(Error::UnexpectedResponse) + } + Some(Response::RemoteChangesResponse(response)) => + if let Request::Changes { request, .. } = request { + let max_block = Decode::decode(&mut response.max.as_ref())?; + let roots_proof = Decode::decode(&mut response.roots_proof.as_ref())?; + let roots = { + let mut r = BTreeMap::new(); + for pair in response.roots { + let k = Decode::decode(&mut pair.fst.as_ref())?; + let v = Decode::decode(&mut pair.snd.as_ref())?; + r.insert(k, v); + } + r + }; + let reply = self.checker.check_changes_proof(&request, fetcher::ChangesProof { + max_block, + proof: response.proof, + roots, + roots_proof, + })?; + Ok(Reply::VecNumberU32(reply)) + } else { + Err(Error::UnexpectedResponse) + } + Some(Response::RemoteHeaderResponse(response)) => + if let Request::Header { request, .. } = request { + let header = + if response.header.is_empty() { + None + } else { + Some(Decode::decode(&mut response.header.as_ref())?) + }; + let proof = Decode::decode(&mut response.proof.as_ref())?; + let reply = self.checker.check_header_proof(&request, header, proof)?; + Ok(Reply::Header(reply)) + } else { + Err(Error::UnexpectedResponse) + } + None => Err(Error::UnexpectedResponse) + } + } + + fn on_remote_call_request + ( &mut self + , peer: &PeerId + , request_id: u64 + , request: &api::v1::light::RemoteCallRequest + ) -> Result + { + log::trace!("remote call request {} from {} ({} at {:?})", + request_id, + peer, + request.method, + request.block); + + let block = Decode::decode(&mut request.block.as_ref())?; + + let proof = match self.chain.execution_proof(&block, &request.method, &request.data) { + Ok((_, proof)) => proof, + Err(e) => { + log::trace!("remote call request {} from {} ({} at {:?}) failed with: {}", + request_id, + peer, + request.method, + request.block, + e); + StorageProof::empty() + } + }; + + let response = { + let r = api::v1::light::RemoteCallResponse { proof: proof.encode() }; + api::v1::light::response::Response::RemoteCallResponse(r) + }; + + Ok(api::v1::light::Response { id: request_id, response: Some(response) }) + } + + fn on_remote_read_request + ( &mut self + , peer: &PeerId + , request_id: u64 + , request: &api::v1::light::RemoteReadRequest + ) -> Result + { + if request.keys.is_empty() { + log::debug!("invalid remote read request sent by {}", peer); + return Err(Error::BadRequest("remote read request without keys")) + } + + log::trace!("remote read request {} from {} ({} at {:?})", + request_id, + peer, + fmt_keys(request.keys.first(), request.keys.last()), + request.block); + + let block = Decode::decode(&mut request.block.as_ref())?; + + let proof = match self.chain.read_proof(&block, &request.keys) { + Ok(proof) => proof, + Err(error) => { + log::trace!("remote read request {} from {} ({} at {:?}) failed with: {}", + request_id, + peer, + fmt_keys(request.keys.first(), request.keys.last()), + request.block, + error); + StorageProof::empty() + } + }; + + let response = { + let r = api::v1::light::RemoteReadResponse { proof: proof.encode() }; + api::v1::light::response::Response::RemoteReadResponse(r) + }; + + Ok(api::v1::light::Response { id: request_id, response: Some(response) }) + } + + fn on_remote_read_child_request + ( &mut self + , peer: &PeerId + , request_id: u64 + , request: &api::v1::light::RemoteReadChildRequest + ) -> Result + { + if request.keys.is_empty() { + log::debug!("invalid remote child read request sent by {}", peer); + return Err(Error::BadRequest("remove read child request without keys")) + } + + log::trace!("remote read child request {} from {} ({} {} at {:?})", + request_id, + peer, + request.storage_key.to_hex::(), + fmt_keys(request.keys.first(), request.keys.last()), + request.block); + + let block = Decode::decode(&mut request.block.as_ref())?; + + let proof = + if let Some(info) = ChildInfo::resolve_child_info(request.child_type, &request.child_info[..]) { + match self.chain.read_child_proof(&block, &request.storage_key, info, &request.keys) { + Ok(proof) => proof, + Err(error) => { + log::trace!("remote read child request {} from {} ({} {} at {:?}) failed with: {}", + request_id, + peer, + request.storage_key.to_hex::(), + fmt_keys(request.keys.first(), request.keys.last()), + request.block, + error); + StorageProof::empty() + } + } + } else { + log::trace!("remote read child request {} from {} ({} {} at {:?}) failed with: {}", + request_id, + peer, + request.storage_key.to_hex::(), + fmt_keys(request.keys.first(), request.keys.last()), + request.block, + "invalid child info and type" + ); + StorageProof::empty() + }; + + let response = { + let r = api::v1::light::RemoteReadResponse { proof: proof.encode() }; + api::v1::light::response::Response::RemoteReadResponse(r) + }; + + Ok(api::v1::light::Response { id: request_id, response: Some(response) }) + } + + fn on_remote_header_request + ( &mut self + , peer: &PeerId + , request_id: u64 + , request: &api::v1::light::RemoteHeaderRequest + ) -> Result + { + log::trace!("remote header proof request {} from {} ({:?})", request_id, peer, request.block); + + let block = Decode::decode(&mut request.block.as_ref())?; + + let (header, proof) = match self.chain.header_proof(block) { + Ok((header, proof)) => (header.encode(), proof), + Err(error) => { + log::trace!("remote header proof request {} from {} ({:?}) failed with: {}", + request_id, + peer, + request.block, + error); + (Default::default(), StorageProof::empty()) + } + }; + + let response = { + let r = api::v1::light::RemoteHeaderResponse { header, proof: proof.encode() }; + api::v1::light::response::Response::RemoteHeaderResponse(r) + }; + + Ok(api::v1::light::Response { id: request_id, response: Some(response) }) + } + + fn on_remote_changes_request + ( &mut self + , peer: &PeerId + , request_id: u64 + , request: &api::v1::light::RemoteChangesRequest + ) -> Result + { + log::trace!("remote changes proof request {} from {} for key {} ({:?}..{:?})", + request_id, + peer, + if !request.storage_key.is_empty() { + format!("{} : {}", request.storage_key.to_hex::(), request.key.to_hex::()) + } else { + request.key.to_hex::() + }, + request.first, + request.last); + + let first = Decode::decode(&mut request.first.as_ref())?; + let last = Decode::decode(&mut request.last.as_ref())?; + let min = Decode::decode(&mut request.min.as_ref())?; + let max = Decode::decode(&mut request.max.as_ref())?; + let key = StorageKey(request.key.clone()); + let storage_key = + if request.storage_key.is_empty() { + None + } else { + Some(StorageKey(request.storage_key.clone())) + }; + + let proof = match self.chain.key_changes_proof(first, last, min, max, storage_key.as_ref(), &key) { + Ok(proof) => proof, + Err(error) => { + log::trace!("remote changes proof request {} from {} for key {} ({:?}..{:?}) failed with: {}", + request_id, + peer, + if let Some(sk) = storage_key { + format!("{} : {}", sk.0.to_hex::(), key.0.to_hex::()) + } else { + key.0.to_hex::() + }, + request.first, + request.last, + error); + + fetcher::ChangesProof:: { + max_block: Zero::zero(), + proof: Vec::new(), + roots: BTreeMap::new(), + roots_proof: StorageProof::empty(), + } + } + }; + + let response = { + let r = api::v1::light::RemoteChangesResponse { + max: proof.max_block.encode(), + proof: proof.proof, + roots: proof.roots.into_iter() + .map(|(k, v)| api::v1::light::Pair { fst: k.encode(), snd: v.encode() }) + .collect(), + roots_proof: proof.roots_proof.encode(), + }; + api::v1::light::response::Response::RemoteChangesResponse(r) + }; + + Ok(api::v1::light::Response { id: request_id, response: Some(response) }) + } +} + +impl NetworkBehaviour for LightClientHandler +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static, + B: Block +{ + type ProtocolsHandler = OneShotHandler>>; + type OutEvent = Void; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + let p = InboundProtocol { + max_data_size: self.config.max_data_size, + protocol: self.config.protocol.clone(), + }; + OneShotHandler::new(SubstreamProtocol::new(p), self.config.inactivity_timeout) + } + + fn addresses_of_peer(&mut self, peer: &PeerId) -> Vec { + self.peers.get(peer) + .map(|info| vec![info.address.clone()]) + .unwrap_or_default() + } + + fn inject_connected(&mut self, peer: PeerId, info: ConnectedPoint) { + let peer_address = match info { + ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr, + ConnectedPoint::Dialer { address } => address + }; + + log::trace!("peer {} connected with address {}", peer, peer_address); + + let info = PeerInfo { + address: peer_address, + best_block: None, + status: PeerStatus::Idle, + }; + + self.peers.insert(peer, info); + } + + fn inject_disconnected(&mut self, peer: &PeerId, _: ConnectedPoint) { + log::trace!("peer {} disconnected", peer); + self.remove_peer(peer) + } + + fn inject_node_event(&mut self, peer: PeerId, event: Event>) { + match event { + // An incoming request from remote has been received. + Event::Request(request, mut stream) => { + log::trace!("incoming request {} from {}", peer, request.id); + let result = match &request.request { + Some(api::v1::light::request::Request::RemoteCallRequest(r)) => + self.on_remote_call_request(&peer, request.id, r), + Some(api::v1::light::request::Request::RemoteReadRequest(r)) => + self.on_remote_read_request(&peer, request.id, r), + Some(api::v1::light::request::Request::RemoteHeaderRequest(r)) => + self.on_remote_header_request(&peer, request.id, r), + Some(api::v1::light::request::Request::RemoteReadChildRequest(r)) => + self.on_remote_read_child_request(&peer, request.id, r), + Some(api::v1::light::request::Request::RemoteChangesRequest(r)) => + self.on_remote_changes_request(&peer, request.id, r), + None => { + log::debug!("ignoring request {} without request data from peer {}", request.id, peer); + return + } + }; + match result { + Ok(response) => { + log::trace!("enqueueing response {} for peer {}", response.id, peer); + let mut data = Vec::new(); + if let Err(e) = response.encode(&mut data) { + log::debug!("error encoding response {} for peer {}: {}", response.id, peer, e) + } else { + let future = async move { + if let Err(e) = write_one(&mut stream, data).await { + log::debug!("error writing response: {}", e) + } + }; + self.responses.push(future.boxed()) + } + } + Err(Error::BadRequest(_)) => { + self.remove_peer(&peer); + self.peerset.report_peer(peer, ReputationChange::new(-(1 << 12), "bad request")) + } + Err(e) => log::debug!("error handling request {} from peer {}: {}", request.id, peer, e) + } + } + // A response to one of our own requests has been received. + Event::Response(response) => { + let id = response.id; + if let Some(request) = self.outstanding.remove(&id) { + // We first just check if the response originates from the expected peer. + if request.peer != peer { + log::debug!("was expecting response {} from {} instead of {}", id, request.peer, peer); + self.outstanding.insert(id, request); + self.remove_peer(&peer); + self.peerset.report_peer(peer, ReputationChange::new_fatal("response from unexpected peer")); + return + } + + if let Some(info) = self.peers.get_mut(&peer) { + if info.status != PeerStatus::BusyWith(id) { + // If we get here, something is wrong with our internal handling of peer + // status information. At any time, a single peer processes at most one + // request from us and its status should contain the request ID we are + // expecting a response for. If a peer would send us a response with a + // random ID, we should not have an entry for it with this peer ID in + // our `outstanding` map, so a malicious peer should not be able to get + // us here. It is our own fault and must be fixed! + panic!("unexpected peer status {:?} for {}", info.status, peer); + } + + info.status = PeerStatus::Idle; // Make peer available again. + + match self.on_response(&peer, &request.request, response) { + Ok(reply) => send_reply(Ok(reply), request.request), + Err(Error::UnexpectedResponse) => { + log::debug!("unexpected response {} from peer {}", id, peer); + self.remove_peer(&peer); + self.peerset.report_peer(peer, ReputationChange::new_fatal("unexpected response from peer")); + let rw = RequestWrapper { + timestamp: request.timestamp, + retries: request.retries, + request: request.request, + peer: (), + }; + self.pending_requests.push_back(rw); + } + Err(other) => { + log::debug!("error handling response {} from peer {}: {}", id, peer, other); + self.remove_peer(&peer); + self.peerset.report_peer(peer, ReputationChange::new_fatal("invalid response from peer")); + if request.retries > 0 { + let rw = RequestWrapper { + timestamp: request.timestamp, + retries: request.retries - 1, + request: request.request, + peer: (), + }; + self.pending_requests.push_back(rw) + } else { + send_reply(Err(ClientError::RemoteFetchFailed), request.request) + } + } + } + } else { + // If we get here, something is wrong with our internal handling of peers. + // We apparently have an entry in our `outstanding` map and the peer is the one we + // expected. So, if we can not find an entry for it in our peer information table, + // then these two collections are out of sync which must not happen and is a clear + // programmer error that must be fixed! + panic!("missing peer information for {}; response {}", peer, id); + } + } else { + log::debug!("unexpected response {} from peer {}", id, peer); + self.remove_peer(&peer); + self.peerset.report_peer(peer, ReputationChange::new_fatal("response from unexpected peer")); + } + } + } + } + + fn poll(&mut self, cx: &mut Context, _: &mut impl PollParameters) -> Poll> { + // Process response sending futures. + while let Poll::Ready(Some(_)) = self.responses.poll_next_unpin(cx) {} + + // If we have a pending request to send, try to find an available peer and send it. + let now = Instant::now(); + while let Some(mut request) = self.pending_requests.pop_front() { + if now > request.timestamp + self.config.request_timeout { + if request.retries == 0 { + send_reply(Err(ClientError::RemoteFetchFailed), request.request); + continue + } + request.timestamp = Instant::now(); + request.retries -= 1 + } + let number = required_block(&request.request); + let available_peer = { + let p = self.idle_peers_with_block(number).next(); + if p.is_none() { + self.idle_peers_with_unknown_block().next() + } else { + p + } + }; + if let Some(peer) = available_peer { + let id = self.next_request_id(); + let rq = serialise_request(id, &request.request); + let mut buf = Vec::with_capacity(rq.encoded_len()); + if let Err(e) = rq.encode(&mut buf) { + log::debug!("failed to serialise request {}: {}", id, e); + send_reply(Err(ClientError::RemoteFetchFailed), request.request) + } else { + log::trace!("sending request {} to peer {}", id, peer); + let protocol = OutboundProtocol { + request: buf, + max_data_size: self.config.max_data_size, + protocol: self.config.protocol.clone(), + }; + self.peers.get_mut(&peer).map(|info| info.status = PeerStatus::BusyWith(id)); + let rw = RequestWrapper { + timestamp: request.timestamp, + retries: request.retries, + request: request.request, + peer: peer.clone(), + }; + self.outstanding.insert(id, rw); + return Poll::Ready(NetworkBehaviourAction::SendEvent { peer_id: peer, event: protocol }) + } + } else { + self.pending_requests.push_front(request); + log::debug!("no peer available to send request to"); + break + } + } + + // Look for ongoing requests that have timed out. + let mut expired = Vec::new(); + for (id, rw) in &self.outstanding { + if now > rw.timestamp + self.config.request_timeout { + log::debug!("request {} timed out", id); + expired.push(*id) + } + } + for id in expired { + if let Some(rw) = self.outstanding.remove(&id) { + self.remove_peer(&rw.peer); + self.peerset.report_peer(rw.peer.clone(), + ReputationChange::new(TIMEOUT_REPUTATION_CHANGE, "light request timeout")); + if rw.retries == 0 { + send_reply(Err(ClientError::RemoteFetchFailed), rw.request); + continue + } + let rw = RequestWrapper { + timestamp: Instant::now(), + retries: rw.retries - 1, + request: rw.request, + peer: (), + }; + self.pending_requests.push_back(rw) + } + } + + Poll::Pending + } +} + +fn required_block(request: &Request) -> NumberFor { + match request { + Request::Header { request, .. } => request.block, + Request::Read { request, .. } => *request.header.number(), + Request::ReadChild { request, .. } => *request.header.number(), + Request::Call { request, .. } => *request.header.number(), + Request::Changes { request, .. } => request.max_block.0, + } +} + +fn retries(request: &Request) -> usize { + let rc = match request { + Request::Header { request, .. } => request.retry_count, + Request::Read { request, .. } => request.retry_count, + Request::ReadChild { request, .. } => request.retry_count, + Request::Call { request, .. } => request.retry_count, + Request::Changes { request, .. } => request.retry_count, + }; + rc.unwrap_or(0) +} + +fn serialise_request(id: u64, request: &Request) -> api::v1::light::Request { + let request = match request { + Request::Header { request, .. } => { + let r = api::v1::light::RemoteHeaderRequest { block: request.block.encode() }; + api::v1::light::request::Request::RemoteHeaderRequest(r) + } + Request::Read { request, .. } => { + let r = api::v1::light::RemoteReadRequest { + block: request.block.encode(), + keys: request.keys.clone(), + }; + api::v1::light::request::Request::RemoteReadRequest(r) + } + Request::ReadChild { request, .. } => { + let r = api::v1::light::RemoteReadChildRequest { + block: request.block.encode(), + storage_key: request.storage_key.clone(), + child_type: request.child_type.clone(), + child_info: request.child_info.clone(), + keys: request.keys.clone(), + }; + api::v1::light::request::Request::RemoteReadChildRequest(r) + } + Request::Call { request, .. } => { + let r = api::v1::light::RemoteCallRequest { + block: request.block.encode(), + method: request.method.clone(), + data: request.call_data.clone(), + }; + api::v1::light::request::Request::RemoteCallRequest(r) + } + Request::Changes { request, .. } => { + let r = api::v1::light::RemoteChangesRequest { + first: request.first_block.1.encode(), + last: request.last_block.1.encode(), + min: request.tries_roots.1.encode(), + max: request.max_block.1.encode(), + storage_key: request.storage_key.clone().unwrap_or_default(), + key: request.key.clone(), + }; + api::v1::light::request::Request::RemoteChangesRequest(r) + } + }; + + api::v1::light::Request { id, request: Some(request) } +} + +fn send_reply(result: Result, ClientError>, request: Request) { + fn send(item: T, sender: oneshot::Sender) { + let _ = sender.send(item); // It is okay if the other end already hung up. + } + match request { + Request::Header { request, sender } => match result { + Err(e) => send(Err(e), sender), + Ok(Reply::Header(x)) => send(Ok(x), sender), + reply => log::error!("invalid reply for header request: {:?}, {:?}", reply, request), + } + Request::Read { request, sender } => match result { + Err(e) => send(Err(e), sender), + Ok(Reply::MapVecU8OptVecU8(x)) => send(Ok(x), sender), + reply => log::error!("invalid reply for read request: {:?}, {:?}", reply, request), + } + Request::ReadChild { request, sender } => match result { + Err(e) => send(Err(e), sender), + Ok(Reply::MapVecU8OptVecU8(x)) => send(Ok(x), sender), + reply => log::error!("invalid reply for read child request: {:?}, {:?}", reply, request), + } + Request::Call { request, sender } => match result { + Err(e) => send(Err(e), sender), + Ok(Reply::VecU8(x)) => send(Ok(x), sender), + reply => log::error!("invalid reply for call request: {:?}, {:?}", reply, request), + } + Request::Changes { request, sender } => match result { + Err(e) => send(Err(e), sender), + Ok(Reply::VecNumberU32(x)) => send(Ok(x), sender), + reply => log::error!("invalid reply for changes request: {:?}, {:?}", reply, request), + } + } +} + +/// Output type of inbound and outbound substream upgrades. +#[derive(Debug)] +pub enum Event { + /// Incoming request from remote and substream to use for the response. + Request(api::v1::light::Request, T), + /// Incoming response from remote. + Response(api::v1::light::Response), +} + +/// Substream upgrade protocol. +/// +/// Reads incoming requests from remote. +#[derive(Debug, Clone)] +pub struct InboundProtocol { + /// The max. request length in bytes. + max_data_size: usize, + /// The protocol to use for upgrade negotiation. + protocol: Bytes, +} + +impl UpgradeInfo for InboundProtocol { + type Info = Bytes; + type InfoIter = iter::Once; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(self.protocol.clone()) + } +} + +impl InboundUpgrade for InboundProtocol +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static +{ + type Output = Event; + type Error = ReadOneError; + type Future = BoxFuture<'static, Result>; + + fn upgrade_inbound(self, mut s: T, _: Self::Info) -> Self::Future { + let future = async move { + let vec = read_one(&mut s, self.max_data_size).await?; + match api::v1::light::Request::decode(&vec[..]) { + Ok(r) => Ok(Event::Request(r, s)), + Err(e) => Err(ReadOneError::Io(io::Error::new(io::ErrorKind::Other, e))) + } + }; + future.boxed() + } +} + +/// Substream upgrade protocol. +/// +/// Sends a request to remote and awaits the response. +#[derive(Debug, Clone)] +pub struct OutboundProtocol { + /// The serialised protobuf request. + request: Vec, + /// The max. request length in bytes. + max_data_size: usize, + /// The protocol to use for upgrade negotiation. + protocol: Bytes, +} + +impl UpgradeInfo for OutboundProtocol { + type Info = Bytes; + type InfoIter = iter::Once; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(self.protocol.clone()) + } +} + +impl OutboundUpgrade for OutboundProtocol +where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static +{ + type Output = Event; + type Error = ReadOneError; + type Future = BoxFuture<'static, Result>; + + fn upgrade_outbound(self, mut s: T, _: Self::Info) -> Self::Future { + let future = async move { + write_one(&mut s, &self.request).await?; + let vec = read_one(&mut s, self.max_data_size).await?; + api::v1::light::Response::decode(&vec[..]) + .map(Event::Response) + .map_err(|e| { + ReadOneError::Io(io::Error::new(io::ErrorKind::Other, e)) + }) + }; + future.boxed() + } +} + +fn fmt_keys(first: Option<&Vec>, last: Option<&Vec>) -> String { + if let (Some(first), Some(last)) = (first, last) { + if first == last { + first.to_hex::() + } else { + format!("{}..{}", first.to_hex::(), last.to_hex::()) + } + } else { + String::from("n/a") + } +} + +#[cfg(test)] +mod tests { + use async_std::task; + use assert_matches::assert_matches; + use codec::Encode; + use crate::{ + chain::Client, + config::ProtocolId, + protocol::{api, light_dispatch::tests::{DummyFetchChecker, dummy_header}} + }; + use futures::{channel::oneshot, prelude::*}; + use libp2p::{ + PeerId, + Multiaddr, + core::{ + ConnectedPoint, + identity, + muxing::{StreamMuxerBox, SubstreamRef}, + transport::{Transport, boxed::Boxed, memory::MemoryTransport}, + upgrade + }, + noise::{self, Keypair, X25519, NoiseConfig}, + swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}, + yamux + }; + use sc_client_api::StorageProof; + use sc_client::light::fetcher; + use sp_blockchain::{Error as ClientError}; + use sp_core::storage::ChildInfo; + use std::{ + collections::HashSet, + io, + iter::{self, FromIterator}, + pin::Pin, + sync::Arc, + task::{Context, Poll} + }; + use sp_runtime::{generic::Header, traits::BlakeTwo256}; + use super::{Event, LightClientHandler, Request, OutboundProtocol, PeerStatus}; + use void::Void; + + const CHILD_INFO: ChildInfo<'static> = ChildInfo::new_default(b"foobarbaz"); + + type Block = sp_runtime::generic::Block, substrate_test_runtime::Extrinsic>; + type Handler = LightClientHandler>, Block>; + type Swarm = libp2p::swarm::Swarm, Handler>; + + fn empty_proof() -> Vec { + StorageProof::empty().encode() + } + + fn make_swarm(ok: bool, ps: sc_peerset::PeersetHandle, cf: super::Config) -> Swarm { + let client = Arc::new(substrate_test_runtime_client::new()); + let checker = Arc::new(DummyFetchChecker::new(ok)); + let id_key = identity::Keypair::generate_ed25519(); + let dh_key = Keypair::::new().into_authentic(&id_key).unwrap(); + let local_peer = id_key.public().into_peer_id(); + let transport = MemoryTransport::default() + .upgrade(upgrade::Version::V1) + .authenticate(NoiseConfig::xx(dh_key).into_authenticated()) + .multiplex(yamux::Config::default()) + .map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer))) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + .boxed(); + Swarm::new(transport, LightClientHandler::new(cf, client, checker, ps), local_peer) + } + + fn make_config() -> super::Config { + super::Config::new(&ProtocolId::from(&b"foo"[..])) + } + + struct EmptyPollParams(PeerId); + + impl PollParameters for EmptyPollParams { + type SupportedProtocolsIter = iter::Empty>; + type ListenedAddressesIter = iter::Empty; + type ExternalAddressesIter = iter::Empty; + + fn supported_protocols(&self) -> Self::SupportedProtocolsIter { + iter::empty() + } + + fn listened_addresses(&self) -> Self::ListenedAddressesIter { + iter::empty() + } + + fn external_addresses(&self) -> Self::ExternalAddressesIter { + iter::empty() + } + + fn local_peer_id(&self) -> &PeerId { + &self.0 + } + } + + fn peerset() -> (sc_peerset::Peerset, sc_peerset::PeersetHandle) { + let cfg = sc_peerset::PeersetConfig { + in_peers: 128, + out_peers: 128, + bootnodes: Vec::new(), + reserved_only: false, + reserved_nodes: Vec::new(), + }; + sc_peerset::Peerset::from_config(cfg) + } + + fn make_behaviour + ( ok: bool + , ps: sc_peerset::PeersetHandle + , cf: super::Config + ) -> LightClientHandler>, Block> + { + let client = Arc::new(substrate_test_runtime_client::new()); + let checker = Arc::new(DummyFetchChecker::new(ok)); + LightClientHandler::new(cf, client, checker, ps) + } + + fn empty_dialer() -> ConnectedPoint { + ConnectedPoint::Dialer { address: Multiaddr::empty() } + } + + fn poll(mut b: &mut LightClientHandler) -> Poll> + where + T: AsyncRead + AsyncWrite + Unpin + Send + 'static + { + let mut p = EmptyPollParams(PeerId::random()); + match future::poll_fn(|cx| Pin::new(&mut b).poll(cx, &mut p)).now_or_never() { + Some(a) => Poll::Ready(a), + None => Poll::Pending + } + } + + #[test] + fn disconnects_from_peer_if_told() { + let peer = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(true, pset.1, make_config()); + + behaviour.inject_connected(peer.clone(), empty_dialer()); + assert_eq!(1, behaviour.peers.len()); + + behaviour.inject_disconnected(&peer, empty_dialer()); + assert_eq!(0, behaviour.peers.len()) + } + + #[test] + fn disconnects_from_peer_if_request_times_out() { + let peer0 = PeerId::random(); + let peer1 = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(true, pset.1, make_config()); + + behaviour.inject_connected(peer0.clone(), empty_dialer()); + behaviour.inject_connected(peer1.clone(), empty_dialer()); + + // We now know about two peers. + assert_eq!(HashSet::from_iter(&[peer0.clone(), peer1.clone()]), behaviour.peers.keys().collect::>()); + + // No requests have been made yet. + assert!(behaviour.pending_requests.is_empty()); + assert!(behaviour.outstanding.is_empty()); + + // Issue our first request! + let chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(1), + }; + behaviour.request(Request::Call { request, sender: chan.0 }).unwrap(); + assert_eq!(1, behaviour.pending_requests.len()); + + // The behaviour should now attempt to send the request. + assert_matches!(poll(&mut behaviour), Poll::Ready(NetworkBehaviourAction::SendEvent { peer_id, .. }) => { + assert!(peer_id == peer0 || peer_id == peer1) + }); + + // And we should have one busy peer. + assert!({ + let (idle, busy): (Vec<_>, Vec<_>) = + behaviour.peers.iter().partition(|(_, info)| info.status == PeerStatus::Idle); + + idle.len() == 1 && busy.len() == 1 + && (idle[0].0 == &peer0 || busy[0].0 == &peer0) + && (idle[0].0 == &peer1 || busy[0].0 == &peer1) + }); + + // No more pending requests, but one should be outstanding. + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(1, behaviour.outstanding.len()); + + // We now set back the timestamp of the outstanding request to make it expire. + let request = behaviour.outstanding.values_mut().next().unwrap(); + request.timestamp -= make_config().request_timeout; + + // Make progress, but do not expect some action. + assert_matches!(poll(&mut behaviour), Poll::Pending); + + // The request should have timed out by now and the corresponding peer be removed. + assert_eq!(1, behaviour.peers.len()); + // Since we asked for one retry, the request should be back in the pending queue. + assert_eq!(1, behaviour.pending_requests.len()); + // No other request should be ongoing. + assert_eq!(0, behaviour.outstanding.len()); + } + + #[test] + fn disconnects_from_peer_on_response_with_wrong_id() { + let peer = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(true, pset.1, make_config()); + + behaviour.inject_connected(peer.clone(), empty_dialer()); + assert_eq!(1, behaviour.peers.len()); + + let chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(1), + }; + behaviour.request(Request::Call { request, sender: chan.0 }).unwrap(); + + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + poll(&mut behaviour); // Make progress + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(1, behaviour.outstanding.len()); + + // Construct response with bogus ID + let response = { + let r = api::v1::light::RemoteCallResponse { proof: empty_proof() }; + api::v1::light::Response { + id: 2365789, + response: Some(api::v1::light::response::Response::RemoteCallResponse(r)), + } + }; + + // Make sure our bogus ID is really not used. + assert!(!behaviour.outstanding.keys().any(|id| id == &response.id)); + + behaviour.inject_node_event(peer.clone(), Event::Response(response)); + assert!(behaviour.peers.is_empty()); + + poll(&mut behaviour); // More progress + + // The request should be back in the pending queue + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + } + + #[test] + fn disconnects_from_peer_on_incorrect_response() { + let peer = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(false, pset.1, make_config()); + // ^--- Making sure the response data check fails. + + behaviour.inject_connected(peer.clone(), empty_dialer()); + assert_eq!(1, behaviour.peers.len()); + + let chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(1), + }; + behaviour.request(Request::Call { request, sender: chan.0 }).unwrap(); + + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + poll(&mut behaviour); // Make progress + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(1, behaviour.outstanding.len()); + + let request_id = *behaviour.outstanding.keys().next().unwrap(); + + let response = { + let r = api::v1::light::RemoteCallResponse { proof: empty_proof() }; + api::v1::light::Response { + id: request_id, + response: Some(api::v1::light::response::Response::RemoteCallResponse(r)), + } + }; + + behaviour.inject_node_event(peer.clone(), Event::Response(response)); + assert!(behaviour.peers.is_empty()); + + poll(&mut behaviour); // More progress + + // The request should be back in the pending queue + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + } + + #[test] + fn disconnects_from_peer_on_unexpected_response() { + let peer = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(true, pset.1, make_config()); + + behaviour.inject_connected(peer.clone(), empty_dialer()); + assert_eq!(1, behaviour.peers.len()); + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + + // Some unsolicited response + let response = { + let r = api::v1::light::RemoteCallResponse { proof: empty_proof() }; + api::v1::light::Response { + id: 2347895932, + response: Some(api::v1::light::response::Response::RemoteCallResponse(r)), + } + }; + + behaviour.inject_node_event(peer.clone(), Event::Response(response)); + + assert!(behaviour.peers.is_empty()); + poll(&mut behaviour); + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + } + + #[test] + fn disconnects_from_peer_on_wrong_response_type() { + let peer = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(true, pset.1, make_config()); + + behaviour.inject_connected(peer.clone(), empty_dialer()); + assert_eq!(1, behaviour.peers.len()); + + let chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(1), + }; + behaviour.request(Request::Call { request, sender: chan.0 }).unwrap(); + + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + poll(&mut behaviour); // Make progress + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(1, behaviour.outstanding.len()); + + let request_id = *behaviour.outstanding.keys().next().unwrap(); + + let response = { + let r = api::v1::light::RemoteReadResponse { proof: empty_proof() }; // Not a RemoteCallResponse! + api::v1::light::Response { + id: request_id, + response: Some(api::v1::light::response::Response::RemoteReadResponse(r)), + } + }; + + behaviour.inject_node_event(peer.clone(), Event::Response(response)); + assert!(behaviour.peers.is_empty()); + + poll(&mut behaviour); // More progress + + // The request should be back in the pending queue + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + } + + #[test] + fn receives_remote_failure_after_retry_count_failures() { + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let peer3 = PeerId::random(); + let peer4 = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(false, pset.1, make_config()); + // ^--- Making sure the response data check fails. + + behaviour.inject_connected(peer1.clone(), empty_dialer()); + behaviour.inject_connected(peer2.clone(), empty_dialer()); + behaviour.inject_connected(peer3.clone(), empty_dialer()); + behaviour.inject_connected(peer4.clone(), empty_dialer()); + assert_eq!(4, behaviour.peers.len()); + + let mut chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: Some(3), // Attempt up to three retries. + }; + behaviour.request(Request::Call { request, sender: chan.0 }).unwrap(); + + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + assert_matches!(poll(&mut behaviour), Poll::Ready(NetworkBehaviourAction::SendEvent { .. })); + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(1, behaviour.outstanding.len()); + + for _ in 0 .. 3 { + // Construct an invalid response + let request_id = *behaviour.outstanding.keys().next().unwrap(); + let responding_peer = behaviour.outstanding.values().next().unwrap().peer.clone(); + let response = { + let r = api::v1::light::RemoteCallResponse { proof: empty_proof() }; + api::v1::light::Response { + id: request_id, + response: Some(api::v1::light::response::Response::RemoteCallResponse(r)) + } + }; + behaviour.inject_node_event(responding_peer, Event::Response(response.clone())); + assert_matches!(poll(&mut behaviour), Poll::Ready(NetworkBehaviourAction::SendEvent { .. })); + assert_matches!(chan.1.try_recv(), Ok(None)) + } + // Final invalid response + let request_id = *behaviour.outstanding.keys().next().unwrap(); + let responding_peer = behaviour.outstanding.values().next().unwrap().peer.clone(); + let response = { + let r = api::v1::light::RemoteCallResponse { proof: empty_proof() }; + api::v1::light::Response { + id: request_id, + response: Some(api::v1::light::response::Response::RemoteCallResponse(r)), + } + }; + behaviour.inject_node_event(responding_peer, Event::Response(response)); + assert_matches!(poll(&mut behaviour), Poll::Pending); + assert_matches!(chan.1.try_recv(), Ok(Some(Err(ClientError::RemoteFetchFailed)))) + } + + fn issue_request(request: Request) { + let peer = PeerId::random(); + let pset = peerset(); + let mut behaviour = make_behaviour(true, pset.1, make_config()); + + behaviour.inject_connected(peer.clone(), empty_dialer()); + assert_eq!(1, behaviour.peers.len()); + + let response = match request { + Request::Header{..} => { + let r = api::v1::light::RemoteHeaderResponse { + header: dummy_header().encode(), + proof: empty_proof() + }; + api::v1::light::Response { + id: 1, + response: Some(api::v1::light::response::Response::RemoteHeaderResponse(r)), + } + } + Request::Read{..} => { + let r = api::v1::light::RemoteReadResponse { proof: empty_proof() }; + api::v1::light::Response { + id: 1, + response: Some(api::v1::light::response::Response::RemoteReadResponse(r)), + } + } + Request::ReadChild{..} => { + let r = api::v1::light::RemoteReadResponse { proof: empty_proof() }; + api::v1::light::Response { + id: 1, + response: Some(api::v1::light::response::Response::RemoteReadResponse(r)), + } + } + Request::Call{..} => { + let r = api::v1::light::RemoteCallResponse { proof: empty_proof() }; + api::v1::light::Response { + id: 1, + response: Some(api::v1::light::response::Response::RemoteCallResponse(r)), + } + } + Request::Changes{..} => { + let r = api::v1::light::RemoteChangesResponse { + max: iter::repeat(1).take(32).collect(), + proof: Vec::new(), + roots: Vec::new(), + roots_proof: empty_proof() + }; + api::v1::light::Response { + id: 1, + response: Some(api::v1::light::response::Response::RemoteChangesResponse(r)), + } + } + }; + + behaviour.request(request).unwrap(); + + assert_eq!(1, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()); + assert_matches!(poll(&mut behaviour), Poll::Ready(NetworkBehaviourAction::SendEvent { .. })); + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(1, behaviour.outstanding.len()); + assert_eq!(1, *behaviour.outstanding.keys().next().unwrap()); + + behaviour.inject_node_event(peer.clone(), Event::Response(response)); + + poll(&mut behaviour); + + assert_eq!(0, behaviour.pending_requests.len()); + assert_eq!(0, behaviour.outstanding.len()) + } + + #[test] + fn receives_remote_call_response() { + let mut chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: None, + }; + issue_request(Request::Call { request, sender: chan.0 }); + assert_matches!(chan.1.try_recv(), Ok(Some(Ok(_)))) + } + + #[test] + fn receives_remote_read_response() { + let mut chan = oneshot::channel(); + let request = fetcher::RemoteReadRequest { + header: dummy_header(), + block: Default::default(), + keys: vec![b":key".to_vec()], + retry_count: None, + }; + issue_request(Request::Read { request, sender: chan.0 }); + assert_matches!(chan.1.try_recv(), Ok(Some(Ok(_)))) + } + + #[test] + fn receives_remote_read_child_response() { + let info = CHILD_INFO.info(); + let mut chan = oneshot::channel(); + let request = fetcher::RemoteReadChildRequest { + header: dummy_header(), + block: Default::default(), + storage_key: b":child_storage:sub".to_vec(), + keys: vec![b":key".to_vec()], + child_info: info.0.to_vec(), + child_type: info.1, + retry_count: None, + }; + issue_request(Request::ReadChild { request, sender: chan.0 }); + assert_matches!(chan.1.try_recv(), Ok(Some(Ok(_)))) + } + + #[test] + fn receives_remote_header_response() { + let mut chan = oneshot::channel(); + let request = fetcher::RemoteHeaderRequest { + cht_root: Default::default(), + block: 1, + retry_count: None, + }; + issue_request(Request::Header { request, sender: chan.0 }); + assert_matches!(chan.1.try_recv(), Ok(Some(Ok(_)))) + } + + #[test] + fn receives_remote_changes_response() { + let mut chan = oneshot::channel(); + let request = fetcher::RemoteChangesRequest { + changes_trie_configs: vec![sp_core::ChangesTrieConfigurationRange { + zero: (0, Default::default()), + end: None, + config: Some(sp_core::ChangesTrieConfiguration::new(4, 2)), + }], + first_block: (1, Default::default()), + last_block: (100, Default::default()), + max_block: (100, Default::default()), + tries_roots: (1, Default::default(), Vec::new()), + key: Vec::new(), + storage_key: None, + retry_count: None, + }; + issue_request(Request::Changes { request, sender: chan.0 }); + assert_matches!(chan.1.try_recv(), Ok(Some(Ok(_)))) + } + + fn send_receive(request: Request) { + // We start a swarm on the listening side which awaits incoming requests and answers them: + let local_pset = peerset(); + let local_listen_addr: libp2p::Multiaddr = libp2p::multiaddr::Protocol::Memory(rand::random()).into(); + let mut local_swarm = make_swarm(true, local_pset.1, make_config()); + Swarm::listen_on(&mut local_swarm, local_listen_addr.clone()).unwrap(); + + // We also start a swarm that makes requests and awaits responses: + let remote_pset = peerset(); + let mut remote_swarm = make_swarm(true, remote_pset.1, make_config()); + + // We now schedule a request, dial the remote and let the two swarm work it out: + remote_swarm.request(request).unwrap(); + Swarm::dial_addr(&mut remote_swarm, local_listen_addr).unwrap(); + + let future = { + let a = local_swarm.for_each(|_| future::ready(())); + let b = remote_swarm.for_each(|_| future::ready(())); + future::join(a, b).map(|_| ()) + }; + + task::spawn(future); + } + + #[test] + fn send_receive_call() { + let chan = oneshot::channel(); + let request = fetcher::RemoteCallRequest { + block: Default::default(), + header: dummy_header(), + method: "test".into(), + call_data: vec![], + retry_count: None, + }; + send_receive(Request::Call { request, sender: chan.0 }); + assert_eq!(vec![42], task::block_on(chan.1).unwrap().unwrap()); + // ^--- from `DummyFetchChecker::check_execution_proof` + } + + #[test] + fn send_receive_read() { + let chan = oneshot::channel(); + let request = fetcher::RemoteReadRequest { + header: dummy_header(), + block: Default::default(), + keys: vec![b":key".to_vec()], + retry_count: None + }; + send_receive(Request::Read { request, sender: chan.0 }); + assert_eq!(Some(vec![42]), task::block_on(chan.1).unwrap().unwrap().remove(&b":key"[..]).unwrap()); + // ^--- from `DummyFetchChecker::check_read_proof` + } + + #[test] + fn send_receive_read_child() { + let info = CHILD_INFO.info(); + let chan = oneshot::channel(); + let request = fetcher::RemoteReadChildRequest { + header: dummy_header(), + block: Default::default(), + storage_key: b":child_storage:sub".to_vec(), + keys: vec![b":key".to_vec()], + child_info: info.0.to_vec(), + child_type: info.1, + retry_count: None, + }; + send_receive(Request::ReadChild { request, sender: chan.0 }); + assert_eq!(Some(vec![42]), task::block_on(chan.1).unwrap().unwrap().remove(&b":key"[..]).unwrap()); + // ^--- from `DummyFetchChecker::check_read_child_proof` + } + + #[test] + fn send_receive_header() { + let _ = env_logger::try_init(); + let chan = oneshot::channel(); + let request = fetcher::RemoteHeaderRequest { + cht_root: Default::default(), + block: 1, + retry_count: None, + }; + send_receive(Request::Header { request, sender: chan.0 }); + // The remote does not know block 1: + assert_matches!(task::block_on(chan.1).unwrap(), Err(ClientError::RemoteFetchFailed)); + } + + #[test] + fn send_receive_changes() { + let chan = oneshot::channel(); + let request = fetcher::RemoteChangesRequest { + changes_trie_configs: vec![sp_core::ChangesTrieConfigurationRange { + zero: (0, Default::default()), + end: None, + config: Some(sp_core::ChangesTrieConfiguration::new(4, 2)), + }], + first_block: (1, Default::default()), + last_block: (100, Default::default()), + max_block: (100, Default::default()), + tries_roots: (1, Default::default(), Vec::new()), + key: Vec::new(), + storage_key: None, + retry_count: None, + }; + send_receive(Request::Changes { request, sender: chan.0 }); + assert_eq!(vec![(100, 2)], task::block_on(chan.1).unwrap().unwrap()); + // ^--- from `DummyFetchChecker::check_changes_proof` + } +} diff --git a/client/network/src/protocol/light_dispatch.rs b/client/network/src/protocol/light_dispatch.rs index bfa8daa181ca15f175a5c11fb2bf531da86f719a..2eab0a5a3ffe0a88ef5b7a759a043b94e29a5ba4 100644 --- a/client/network/src/protocol/light_dispatch.rs +++ b/client/network/src/protocol/light_dispatch.rs @@ -21,7 +21,8 @@ use std::collections::{HashMap, VecDeque}; use std::sync::Arc; -use std::time::{Instant, Duration}; +use std::time::Duration; +use wasm_timer::Instant; use log::{trace, info}; use futures::channel::oneshot::{Sender as OneShotSender}; use linked_hash_map::{Entry, LinkedHashMap}; @@ -40,7 +41,7 @@ const REQUEST_TIMEOUT: Duration = Duration::from_secs(15); /// Default request retry count. const RETRY_COUNT: usize = 1; /// Reputation change for a peer when a request timed out. -const TIMEOUT_REPUTATION_CHANGE: i32 = -(1 << 8); +pub(crate) const TIMEOUT_REPUTATION_CHANGE: i32 = -(1 << 8); /// Trait used by the `LightDispatch` service to communicate messages back to the network. pub trait LightDispatchNetwork { @@ -691,17 +692,26 @@ pub mod tests { use crate::message::{self, BlockAttributes, Direction, FromBlock, RequestId}; use libp2p::PeerId; use super::{REQUEST_TIMEOUT, LightDispatch, LightDispatchNetwork, RequestData, StorageProof}; - use sp_test_primitives::{Block, Extrinsic, Header}; + use sp_test_primitives::{Block, Header}; - struct DummyFetchChecker { ok: bool } + pub(crate) struct DummyFetchChecker { + pub(crate) ok: bool, + _mark: std::marker::PhantomData + } + + impl DummyFetchChecker { + pub(crate) fn new(ok: bool) -> Self { + DummyFetchChecker { ok, _mark: std::marker::PhantomData } + } + } - impl FetchChecker for DummyFetchChecker { + impl FetchChecker for DummyFetchChecker { fn check_header_proof( &self, - _request: &RemoteHeaderRequest
, - header: Option
, + _request: &RemoteHeaderRequest, + header: Option, _remote_proof: StorageProof, - ) -> ClientResult
{ + ) -> ClientResult { match self.ok { true if header.is_some() => Ok(header.unwrap()), _ => Err(ClientError::Backend("Test error".into())), @@ -710,7 +720,7 @@ pub mod tests { fn check_read_proof( &self, - request: &RemoteReadRequest
, + request: &RemoteReadRequest, _: StorageProof, ) -> ClientResult, Option>>> { match self.ok { @@ -726,7 +736,7 @@ pub mod tests { fn check_read_child_proof( &self, - request: &RemoteReadChildRequest
, + request: &RemoteReadChildRequest, _: StorageProof, ) -> ClientResult, Option>>> { match self.ok { @@ -740,7 +750,7 @@ pub mod tests { } } - fn check_execution_proof(&self, _: &RemoteCallRequest
, _: StorageProof) -> ClientResult> { + fn check_execution_proof(&self, _: &RemoteCallRequest, _: StorageProof) -> ClientResult> { match self.ok { true => Ok(vec![42]), false => Err(ClientError::Backend("Test error".into())), @@ -749,20 +759,20 @@ pub mod tests { fn check_changes_proof( &self, - _: &RemoteChangesRequest
, - _: ChangesProof
- ) -> ClientResult, u32)>> { + _: &RemoteChangesRequest, + _: ChangesProof + ) -> ClientResult, u32)>> { match self.ok { - true => Ok(vec![(100, 2)]), + true => Ok(vec![(100.into(), 2)]), false => Err(ClientError::Backend("Test error".into())), } } fn check_body_proof( &self, - _: &RemoteBodyRequest
, - body: Vec - ) -> ClientResult> { + _: &RemoteBodyRequest, + body: Vec + ) -> ClientResult> { match self.ok { true => Ok(body), false => Err(ClientError::Backend("Test error".into())), @@ -771,7 +781,7 @@ pub mod tests { } fn dummy(ok: bool) -> LightDispatch { - LightDispatch::new(Arc::new(DummyFetchChecker { ok })) + LightDispatch::new(Arc::new(DummyFetchChecker::new(ok))) } fn total_peers(light_dispatch: &LightDispatch) -> usize { @@ -790,7 +800,7 @@ pub mod tests { }); } - fn dummy_header() -> Header { + pub(crate) fn dummy_header() -> Header { Header { parent_hash: Default::default(), number: 0, diff --git a/client/network/src/protocol/schema/api.v1.proto b/client/network/src/protocol/schema/api.v1.proto new file mode 100644 index 0000000000000000000000000000000000000000..e4c32ec585d9cc60e63382ee3ff094ef1ed2acbd --- /dev/null +++ b/client/network/src/protocol/schema/api.v1.proto @@ -0,0 +1,59 @@ +// Schema definition for block request/response messages. + +syntax = "proto3"; + +package api.v1; + +// Block enumeration direction. +enum Direction { + // Enumerate in ascending order (from child to parent). + Ascending = 0; + // Enumerate in descendfing order (from parent to canonical child). + Descending = 1; +} + +// Request block data from a peer. +message BlockRequest { + // Unique request id. + uint64 id = 1; + // Bits of block data to request. + uint32 fields = 2; + // Start from this block. + oneof from_block { + // Start with given hash. + bytes hash = 3; + // Start with given block number. + bytes number = 4; + } + // End at this block. An implementation defined maximum is used when unspecified. + bytes to_block = 5; // optional + // Sequence direction. + Direction direction = 6; + // Maximum number of blocks to return. An implementation defined maximum is used when unspecified. + uint32 max_blocks = 7; // optional +} + +// Response to `BlockRequest` +message BlockResponse { + // Id of a request this response was made for. + uint64 id = 1; + // Block data for the requested sequence. + repeated BlockData blocks = 2; +} + +// Block data sent in the response. +message BlockData { + // Block header hash. + bytes hash = 1; + // Block header if requested. + bytes header = 2; // optional + // Block body if requested. + repeated bytes body = 3; // optional + // Block receipt if requested. + bytes receipt = 4; // optional + // Block message queue if requested. + bytes message_queue = 5; // optional + // Justification if requested. + bytes justification = 6; // optional +} + diff --git a/client/network/src/protocol/schema/light.v1.proto b/client/network/src/protocol/schema/light.v1.proto new file mode 100644 index 0000000000000000000000000000000000000000..b9aee67b5ee24ff6614e3db2a0c711988a8d03c1 --- /dev/null +++ b/client/network/src/protocol/schema/light.v1.proto @@ -0,0 +1,128 @@ +// Schema definition for light client messages. + +syntax = "proto3"; + +package api.v1.light; + +// A pair of arbitrary bytes. +message Pair { + // The first element of the pair. + bytes fst = 1; + // The second element of the pair. + bytes snd = 2; +} + +// Enumerate all possible light client request messages. +message Request { + // Unique request id. + uint64 id = 1; + oneof request { + RemoteCallRequest remote_call_request = 2; + RemoteReadRequest remote_read_request = 3; + RemoteHeaderRequest remote_header_request = 4; + RemoteReadChildRequest remote_read_child_request = 5; + RemoteChangesRequest remote_changes_request = 6; + } +} + +// Enumerate all possible light client response messages. +message Response { + /// Id of a request this response was made for. + uint64 id = 1; + oneof response { + RemoteCallResponse remote_call_response = 2; + RemoteReadResponse remote_read_response = 3; + RemoteHeaderResponse remote_header_response = 4; + RemoteChangesResponse remote_changes_response = 6; + } +} + +// Remote call request. +message RemoteCallRequest { + // Block at which to perform call. + bytes block = 2; + // Method name. + string method = 3; + // Call data. + bytes data = 4; +} + +// Remote call response. +message RemoteCallResponse { + // Execution proof. + bytes proof = 2; +} + +// Remote storage read request. +message RemoteReadRequest { + // Block at which to perform call. + bytes block = 2; + // Storage keys. + repeated bytes keys = 3; +} + +// Remote read response. +message RemoteReadResponse { + // Read proof. + bytes proof = 2; +} + +// Remote storage read child request. +message RemoteReadChildRequest { + // Block at which to perform call. + bytes block = 2; + // Child Storage key. + bytes storage_key = 3; + // Child trie source information. + bytes child_info = 4; + /// Child type, its required to resolve `child_info` + /// content and choose child implementation. + uint32 child_type = 5; + // Storage keys. + repeated bytes keys = 6; +} + +// Remote header request. +message RemoteHeaderRequest { + // Block number to request header for. + bytes block = 2; +} + +// Remote header response. +message RemoteHeaderResponse { + // Header. None if proof generation has failed (e.g. header is unknown). + bytes header = 2; // optional + // Header proof. + bytes proof = 3; +} + +/// Remote changes request. +message RemoteChangesRequest { + // Hash of the first block of the range (including first) where changes are requested. + bytes first = 2; + // Hash of the last block of the range (including last) where changes are requested. + bytes last = 3; + // Hash of the first block for which the requester has the changes trie root. All other + // affected roots must be proved. + bytes min = 4; + // Hash of the last block that we can use when querying changes. + bytes max = 5; + // Storage child node key which changes are requested. + bytes storage_key = 6; // optional + // Storage key which changes are requested. + bytes key = 7; +} + +// Remote changes response. +message RemoteChangesResponse { + // Proof has been generated using block with this number as a max block. Should be + // less than or equal to the RemoteChangesRequest::max block number. + bytes max = 2; + // Changes proof. + repeated bytes proof = 3; + // Changes tries roots missing on the requester' node. + repeated Pair roots = 4; + // Missing changes tries roots proof. + bytes roots_proof = 5; +} + diff --git a/client/network/src/protocol/sync/extra_requests.rs b/client/network/src/protocol/sync/extra_requests.rs index 44b42b154fff2724d78abf36b0d6d5247023f49d..a5fb232b8d1592bbb6e6afe678f82e0404d3e60c 100644 --- a/client/network/src/protocol/sync/extra_requests.rs +++ b/client/network/src/protocol/sync/extra_requests.rs @@ -21,7 +21,8 @@ use libp2p::PeerId; use log::{debug, trace, warn}; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; use std::collections::{HashMap, HashSet, VecDeque}; -use std::time::{Duration, Instant}; +use std::time::Duration; +use wasm_timer::Instant; // Time to wait before trying to get the same extra data from the same peer. const EXTRA_RETRY_WAIT: Duration = Duration::from_secs(10); diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 9c9a87bb67e017ea0be8274622d9557ca6ba5ce3..156e2e3102b01576a084cf748015c0255f3b610a 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -36,7 +36,7 @@ use futures::{prelude::*, channel::mpsc}; use log::{warn, error, info, trace}; use libp2p::{PeerId, Multiaddr, kad::record}; use libp2p::core::{transport::boxed::Boxed, muxing::StreamMuxerBox}; -use libp2p::swarm::{NetworkBehaviour, SwarmEvent}; +use libp2p::swarm::{NetworkBehaviour, SwarmBuilder, SwarmEvent}; use parking_lot::Mutex; use sc_peerset::PeersetHandle; use sp_runtime::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId}; @@ -77,6 +77,8 @@ pub trait TransactionPool: Send + Sync { ); /// Notify the pool about transactions broadcast. fn on_broadcasted(&self, propagations: HashMap>); + /// Get transaction by hash. + fn transaction(&self, hash: &H) -> Option; } /// A cloneable handle for reporting cost/benefits of peers. @@ -115,7 +117,7 @@ pub struct NetworkService, H: E /// nodes it should be connected to or not. peerset: PeersetHandle, /// Channel that sends messages to the actual worker. - to_worker: mpsc::UnboundedSender>, + to_worker: mpsc::UnboundedSender>, /// Marker to pin the `H` generic. Serves no purpose except to not break backwards /// compatibility. _marker: PhantomData, @@ -191,6 +193,10 @@ impl, H: ExHashT> NetworkWorker let local_peer_id = local_public.clone().into_peer_id(); info!(target: "sub-libp2p", "Local node identity is: {}", local_peer_id.to_base58()); + let checker = params.on_demand.as_ref() + .map(|od| od.checker().clone()) + .unwrap_or(Arc::new(AlwaysBadChecker)); + let num_connected = Arc::new(AtomicUsize::new(0)); let is_major_syncing = Arc::new(AtomicBool::new(false)); let (protocol, peerset_handle) = Protocol::new( @@ -198,25 +204,32 @@ impl, H: ExHashT> NetworkWorker roles: params.roles, max_parallel_downloads: params.network_config.max_parallel_downloads, }, - params.chain, - params.on_demand.as_ref().map(|od| od.checker().clone()) - .unwrap_or(Arc::new(AlwaysBadChecker)), + params.chain.clone(), + checker.clone(), params.specialization, params.transaction_pool, - params.finality_proof_provider, + params.finality_proof_provider.clone(), params.finality_proof_request_builder, - params.protocol_id, + params.protocol_id.clone(), peerset_config, params.block_announce_validator )?; // Build the swarm. - let (mut swarm, bandwidth) = { + let (mut swarm, bandwidth): (Swarm::, _) = { let user_agent = format!( "{} ({})", params.network_config.client_version, params.network_config.node_name ); + let block_requests = { + let config = protocol::block_requests::Config::new(¶ms.protocol_id); + protocol::BlockRequests::new(config, params.chain.clone()) + }; + let light_client_handler = { + let config = protocol::light_client_handler::Config::new(¶ms.protocol_id); + protocol::LightClientHandler::new(config, params.chain, checker, peerset_handle.clone()) + }; let behaviour = futures::executor::block_on(Behaviour::new( protocol, user_agent, @@ -230,16 +243,23 @@ impl, H: ExHashT> NetworkWorker TransportConfig::MemoryOnly => false, TransportConfig::Normal { allow_private_ipv4, .. } => allow_private_ipv4, }, + u64::from(params.network_config.out_peers) + 15, + block_requests, + light_client_handler )); let (transport, bandwidth) = { - let (config_mem, config_wasm) = match params.network_config.transport { - TransportConfig::MemoryOnly => (true, None), - TransportConfig::Normal { wasm_external_transport, .. } => - (false, wasm_external_transport) + let (config_mem, config_wasm, flowctrl) = match params.network_config.transport { + TransportConfig::MemoryOnly => (true, None, false), + TransportConfig::Normal { wasm_external_transport, use_yamux_flow_control, .. } => + (false, wasm_external_transport, use_yamux_flow_control) }; - transport::build_transport(local_identity, config_mem, config_wasm) + transport::build_transport(local_identity, config_mem, config_wasm, flowctrl) }; - (Swarm::::new(transport, behaviour, local_peer_id.clone()), bandwidth) + let mut builder = SwarmBuilder::new(transport, behaviour, local_peer_id.clone()); + if let Some(spawner) = params.executor { + builder = builder.executor_fn(spawner); + } + (builder.build(), bandwidth) }; // Listen on multiaddresses. @@ -477,14 +497,22 @@ impl, H: ExHashT> NetworkServic }); } - /// You must call this when new transactons are imported by the transaction pool. + /// You may call this when new transactons are imported by the transaction pool. /// - /// The latest transactions will be fetched from the `TransactionPool` that was passed at - /// initialization as part of the configuration. + /// All transactions will be fetched from the `TransactionPool` that was passed at + /// initialization as part of the configuration and propagated to peers. pub fn trigger_repropagate(&self) { let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateExtrinsics); } + /// You must call when new transaction is imported by the transaction pool. + /// + /// This transaction will be fetched from the `TransactionPool` that was passed at + /// initialization as part of the configuration and propagated to peers. + pub fn propagate_extrinsic(&self, hash: H) { + let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateExtrinsic(hash)); + } + /// Make sure an important block is propagated to peers. /// /// In chain-based consensus, we often need to make sure non-best forks are @@ -665,7 +693,8 @@ impl NetworkStateInfo for NetworkService /// Messages sent from the `NetworkService` to the `NetworkWorker`. /// /// Each entry corresponds to a method of `NetworkService`. -enum ServiceToWorkerMsg> { +enum ServiceToWorkerMsg> { + PropagateExtrinsic(H), PropagateExtrinsics, RequestJustification(B::Hash, NumberFor), AnnounceBlock(B::Hash, Vec), @@ -704,7 +733,7 @@ pub struct NetworkWorker, H: Ex /// The import queue that was passed as initialization. import_queue: Box>, /// Messages from the `NetworkService` and that must be processed. - from_worker: mpsc::UnboundedReceiver>, + from_worker: mpsc::UnboundedReceiver>, /// Receiver for queries from the light client that must be processed. light_client_rqs: Option>>, /// Senders for events that happen on the network. @@ -747,6 +776,8 @@ impl, H: ExHashT> Future for Ne this.network_service.user_protocol_mut().announce_block(hash, data), ServiceToWorkerMsg::RequestJustification(hash, number) => this.network_service.user_protocol_mut().request_justification(&hash, number), + ServiceToWorkerMsg::PropagateExtrinsic(hash) => + this.network_service.user_protocol_mut().propagate_extrinsic(&hash), ServiceToWorkerMsg::PropagateExtrinsics => this.network_service.user_protocol_mut().propagate_extrinsics(), ServiceToWorkerMsg::GetValue(key) => diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index d632f9b75c0c9d23d143c83c6e0eee975998fbcf..e2c95824f83204d6c24c1f96a8f394c4b98b0d21 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -17,12 +17,10 @@ use futures::prelude::*; use libp2p::{ InboundUpgradeExt, OutboundUpgradeExt, PeerId, Transport, - mplex, identity, secio, yamux, bandwidth, wasm_ext + mplex, identity, bandwidth, wasm_ext }; #[cfg(not(target_os = "unknown"))] use libp2p::{tcp, dns, websocket, noise}; -#[cfg(not(target_os = "unknown"))] -use libp2p::core::{either::EitherError, either::EitherOutput}; use libp2p::core::{self, upgrade, transport::boxed::Boxed, transport::OptionalTransport, muxing::StreamMuxerBox}; use std::{io, sync::Arc, time::Duration, usize}; @@ -38,7 +36,8 @@ pub use self::bandwidth::BandwidthSinks; pub fn build_transport( keypair: identity::Keypair, memory_only: bool, - wasm_external_transport: Option + wasm_external_transport: Option, + use_yamux_flow_control: bool ) -> (Boxed<(PeerId, StreamMuxerBox), io::Error>, Arc) { // Build configuration objects for encryption mechanisms. #[cfg(not(target_os = "unknown"))] @@ -52,13 +51,23 @@ pub fn build_transport( rare panic here is basically zero"); noise::NoiseConfig::ix(noise_keypair) }; - let secio_config = secio::SecioConfig::new(keypair); // Build configuration objects for multiplexing mechanisms. let mut mplex_config = mplex::MplexConfig::new(); mplex_config.max_buffer_len_behaviour(mplex::MaxBufferBehaviour::Block); mplex_config.max_buffer_len(usize::MAX); - let yamux_config = yamux::Config::default(); + + let yamux_config = { + let mut c = yamux::Config::default(); + // Only set SYN flag on first data frame sent to the remote. + c.set_lazy_open(true); + if use_yamux_flow_control { + // Enable proper flow-control: window updates are only sent when + // buffered data has been consumed. + c.set_window_update_mode(yamux::WindowUpdateMode::OnRead); + } + libp2p::yamux::Config::new(c) + }; // Build the base layer of the transport. let transport = if let Some(t) = wasm_external_transport { @@ -93,28 +102,23 @@ pub fn build_transport( // For non-WASM, we support both secio and noise. #[cfg(not(target_os = "unknown"))] let transport = transport.and_then(move |stream, endpoint| { - let upgrade = core::upgrade::SelectUpgrade::new(noise_config, secio_config); - core::upgrade::apply(stream, upgrade, endpoint, upgrade::Version::V1) - .map(|out| match out? { - // We negotiated noise - EitherOutput::First((remote_id, out)) => { - let remote_key = match remote_id { - noise::RemoteIdentity::IdentityKey(key) => key, - _ => return Err(upgrade::UpgradeError::Apply(EitherError::A(noise::NoiseError::InvalidKey))) - }; - Ok((EitherOutput::First(out), remote_key.into_peer_id())) - } - // We negotiated secio - EitherOutput::Second((remote_id, out)) => - Ok((EitherOutput::Second(out), remote_id)) + core::upgrade::apply(stream, noise_config, endpoint, upgrade::Version::V1) + .and_then(|(remote_id, out)| async move { + let remote_key = match remote_id { + noise::RemoteIdentity::IdentityKey(key) => key, + _ => return Err(upgrade::UpgradeError::Apply(noise::NoiseError::InvalidKey)) + }; + Ok((out, remote_key.into_peer_id())) }) }); - // For WASM, we only support secio for now. + // We refuse all WASM connections for now. It is intended that we negotiate noise in the + // future. See https://github.com/libp2p/rust-libp2p/issues/1414 #[cfg(target_os = "unknown")] - let transport = transport.and_then(move |stream, endpoint| { - core::upgrade::apply(stream, secio_config, endpoint, upgrade::Version::V1) - .map_ok(|(id, stream)| ((stream, id))) + let transport = transport.and_then(move |_, _| async move { + let r: Result<(wasm_ext::Connection, PeerId), _> = + Err(io::Error::new(io::ErrorKind::Other, format!("No encryption protocol supported"))); + r }); // Multiplexing @@ -126,11 +130,18 @@ pub fn build_transport( core::upgrade::apply(stream, upgrade, endpoint, upgrade::Version::V1) .map_ok(|(id, muxer)| (id, core::muxing::StreamMuxerBox::new(muxer))) - }) + }); - .timeout(Duration::from_secs(20)) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) - .boxed(); + let transport = if cfg!(not(target_os = "unknown")) { + transport + .timeout(Duration::from_secs(20)) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + .boxed() + } else { + transport + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + .boxed() + }; (transport, sinks) } diff --git a/client/network/test/Cargo.toml b/client/network/test/Cargo.toml index 036607df921bab5207466663d4c7a3864a423deb..ca4b0237cf2eb21bc7c5f23a33617eb9b1092480 100644 --- a/client/network/test/Cargo.toml +++ b/client/network/test/Cargo.toml @@ -9,12 +9,12 @@ edition = "2018" [dependencies] sc-network = { version = "0.8", path = "../" } log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" futures = "0.1.29" futures03 = { package = "futures", version = "0.3.1", features = ["compat"] } -futures-timer = "0.4.0" +futures-timer = "3.0.1" rand = "0.7.2" -libp2p = { version = "0.14.0-alpha.1", default-features = false, features = ["libp2p-websocket"] } +libp2p = { version = "0.15.0", default-features = false, features = ["libp2p-websocket"] } sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" } sc-client = { version = "0.8", path = "../../" } sc-client-api = { version = "2.0.0", path = "../../api" } diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index 1b13e83343e5801cc33cb57db9a290b0b1182f1c..22e340a2a81b39f220c2f4cf49c44c86f7f0f63e 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -52,7 +52,7 @@ use sc_network::config::{NetworkConfiguration, TransportConfig, BoxFinalityProof use libp2p::PeerId; use parking_lot::Mutex; use sp_core::H256; -use sc_network::{Context, ProtocolConfig}; +use sc_network::ProtocolConfig; use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use sp_runtime::Justification; @@ -95,7 +95,8 @@ impl Verifier for PassThroughVerifier { justification, post_digests: vec![], auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }, maybe_keys)) @@ -390,6 +391,8 @@ impl TransactionPool for EmptyTransactionPool { ) {} fn on_broadcasted(&self, _: HashMap>) {} + + fn transaction(&self, _h: &Hash) -> Option { None } } pub trait SpecializationFactory { @@ -610,6 +613,7 @@ pub trait TestNetFactory: Sized { let network = NetworkWorker::new(sc_network::config::Params { roles: config.roles, + executor: None, network_config: NetworkConfiguration { listen_addresses: vec![listen_addr.clone()], transport: TransportConfig::MemoryOnly, @@ -685,6 +689,7 @@ pub trait TestNetFactory: Sized { let network = NetworkWorker::new(sc_network::config::Params { roles: config.roles, + executor: None, network_config: NetworkConfiguration { listen_addresses: vec![listen_addr.clone()], transport: TransportConfig::MemoryOnly, diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index 2140de0973449a677abe4547a4830f1f8378626a..210a4fb38bb68949edfd44925e8feb0aca13b898 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -405,7 +405,7 @@ fn blocks_are_not_announced_by_light_nodes() { net.peers.remove(0); // Poll for a few seconds and make sure 1 and 2 (now 0 and 1) don't sync together. - let mut delay = futures_timer::Delay::new(Duration::from_secs(5)).compat(); + let mut delay = futures_timer::Delay::new(Duration::from_secs(5)).unit_error().compat(); runtime.block_on(futures::future::poll_fn::<(), (), _>(|| { net.poll(); delay.poll().map_err(|_| ()) @@ -504,7 +504,7 @@ fn can_not_sync_from_light_peer() { net.peers.remove(0); // ensure that the #2 (now #1) fails to sync block #1 even after 5 seconds - let mut test_finished = futures_timer::Delay::new(Duration::from_secs(5)).compat(); + let mut test_finished = futures_timer::Delay::new(Duration::from_secs(5)).unit_error().compat(); runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { net.poll(); test_finished.poll().map_err(|_| ()) diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 3ef98f1ab9004ca2f00bcf1d4a4670ce06115198..ed5db3f2c74e5db24a871e9305c867d9976d56b9 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -13,13 +13,13 @@ sp-api = { version = "2.0.0", path = "../../primitives/api" } fnv = "1.0.6" futures01 = { package = "futures", version = "0.1" } futures = "0.3.1" -futures-timer = "2.0" +futures-timer = "3.0.1" log = "0.4.8" threadpool = "1.7" num_cpus = "1.10" sp-offchain = { version = "2.0.0", path = "../../primitives/offchain" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } -parking_lot = "0.9.0" +parking_lot = "0.10.0" sp-core = { version = "2.0.0", path = "../../primitives/core" } rand = "0.7.2" sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 78760fb011c1c8c8935770795b80faf4619a4f56..fadfaa01349e0ad4c9de6b1d015a6dc3c3cfa382 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -97,7 +97,7 @@ impl OffchainWorkers< is_validator: bool, ) -> impl Future { let runtime = self.client.runtime_api(); - let at = BlockId::number(*header.number()); + let at = BlockId::hash(header.hash()); let has_api_v1 = runtime.has_api_with::, _>( &at, |v| v == 1 ); @@ -107,7 +107,11 @@ impl OffchainWorkers< let version = match (has_api_v1, has_api_v2) { (_, Ok(true)) => 2, (Ok(true), _) => 1, - _ => 0, + err => { + let help = "Consider turning off offchain workers if they are not part of your runtime."; + log::error!("Unsupported Offchain Worker API version: {:?}. {}.", err, help); + 0 + } }; debug!("Checking offchain workers at {:?}: version:{}", at, version); if version > 0 { @@ -140,8 +144,6 @@ impl OffchainWorkers< }); futures::future::Either::Left(runner.process()) } else { - let help = "Consider turning off offchain workers if they are not part of your runtime."; - log::error!("Unsupported Offchain Worker API version: {}. {}", version, help); futures::future::Either::Right(futures::future::ready(())) } } @@ -167,7 +169,6 @@ mod tests { use substrate_test_runtime_client::runtime::Block; use sc_transaction_pool::{BasicPool, FullChainApi}; use sp_transaction_pool::{TransactionPool, InPoolTransaction}; - use sp_runtime::{generic::Header, traits::Header as _}; struct MockNetworkStateInfo(); @@ -200,18 +201,15 @@ mod tests { // given let _ = env_logger::try_init(); let client = Arc::new(substrate_test_runtime_client::new()); - let pool = Arc::new(TestPool(BasicPool::new(Default::default(), FullChainApi::new(client.clone())))); + let pool = Arc::new(TestPool(BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + ))); client.execution_extensions() .register_transaction_pool(Arc::downgrade(&pool.clone()) as _); let db = sc_client_db::offchain::LocalStorage::new_test(); let network_state = Arc::new(MockNetworkStateInfo()); - let header = Header::new( - 0u64, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); + let header = client.header(&BlockId::number(0)).unwrap().unwrap(); // when let offchain = OffchainWorkers::new(client, db); diff --git a/client/peerset/Cargo.toml b/client/peerset/Cargo.toml index 90f44418c72e4c9377f7c154ce3244a99cafa2bb..2527b867959cf569c38f6245e0dd580eda8ccb8f 100644 --- a/client/peerset/Cargo.toml +++ b/client/peerset/Cargo.toml @@ -9,9 +9,10 @@ edition = "2018" [dependencies] futures = "0.3.1" -libp2p = { version = "0.14.0-alpha.1", default-features = false } +libp2p = { version = "0.15.0", default-features = false } log = "0.4.8" serde_json = "1.0.41" +wasm-timer = "0.2" [dev-dependencies] rand = "0.7.2" diff --git a/client/peerset/src/lib.rs b/client/peerset/src/lib.rs index c9589127270071ebfede9a67faf68a982e8c2823..bd2b6bb11088964499904eaad2fe32dcf1b0a0f0 100644 --- a/client/peerset/src/lib.rs +++ b/client/peerset/src/lib.rs @@ -19,17 +19,18 @@ mod peersstate; -use std::{collections::{HashSet, HashMap}, collections::VecDeque, time::Instant}; +use std::{collections::{HashSet, HashMap}, collections::VecDeque}; use futures::{prelude::*, channel::mpsc}; use libp2p::PeerId; use log::{debug, error, trace}; use serde_json::json; use std::{pin::Pin, task::Context, task::Poll}; +use wasm_timer::Instant; /// We don't accept nodes whose reputation is under this value. const BANNED_THRESHOLD: i32 = 82 * (i32::min_value() / 100); /// Reputation change for a node when we get disconnected from it. -const DISCONNECT_REPUTATION_CHANGE: i32 = -10; +const DISCONNECT_REPUTATION_CHANGE: i32 = -256; /// Reserved peers group ID const RESERVED_NODES: &'static str = "reserved"; diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index 7f1a12f80e80dbc94b67e682ac1291adfdc22062..4781c9d35096ca02e68f0e6d59b2ca963bed6e24 100644 --- a/client/rpc-api/Cargo.toml +++ b/client/rpc-api/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-rpc-api" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } @@ -13,7 +14,7 @@ jsonrpc-core-client = "14.0.3" jsonrpc-derive = "14.0.3" jsonrpc-pubsub = "14.0.3" log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" sp-core = { version = "2.0.0", path = "../../primitives/core" } sp-version = { version = "2.0.0", path = "../../primitives/version" } serde = { version = "1.0.101", features = ["derive"] } diff --git a/client/rpc-api/src/author/error.rs b/client/rpc-api/src/author/error.rs index 8ebd06f8de430a5f908fc6337f1237d0e7bad5f9..f1b56910086b5cb671689d612d9d2062d4722141 100644 --- a/client/rpc-api/src/author/error.rs +++ b/client/rpc-api/src/author/error.rs @@ -54,6 +54,9 @@ pub enum Error { /// Some random issue with the key store. Shouldn't happen. #[display(fmt="The key store is unavailable")] KeyStoreUnavailable, + /// Invalid session keys encoding. + #[display(fmt="Session keys are not encoded correctly")] + InvalidSessionKeys, } impl std::error::Error for Error { diff --git a/client/rpc-api/src/author/mod.rs b/client/rpc-api/src/author/mod.rs index cbdbd38154490b88324826f46b429c5988ee29b7..4fbf0c73a366696109b4efc4043f7171479991c9 100644 --- a/client/rpc-api/src/author/mod.rs +++ b/client/rpc-api/src/author/mod.rs @@ -39,7 +39,8 @@ pub trait AuthorApi { /// Insert a key into the keystore. #[rpc(name = "author_insertKey")] - fn insert_key(&self, + fn insert_key( + &self, key_type: String, suri: String, public: Bytes, @@ -49,6 +50,20 @@ pub trait AuthorApi { #[rpc(name = "author_rotateKeys")] fn rotate_keys(&self) -> Result; + /// Checks if the keystore has private keys for the given session public keys. + /// + /// `session_keys` is the SCALE encoded session keys object from the runtime. + /// + /// Returns `true` iff all private keys could be found. + #[rpc(name = "author_hasSessionKeys")] + fn has_session_keys(&self, session_keys: Bytes) -> Result; + + /// Checks if the keystore has private keys for the given public key and key type. + /// + /// Returns `true` if a private key could be found. + #[rpc(name = "author_hasKey")] + fn has_key(&self, public_key: Bytes, key_type: String) -> Result; + /// Returns all pending extrinsics, potentially grouped by sender. #[rpc(name = "author_pendingExtrinsics")] fn pending_extrinsics(&self) -> Result>; diff --git a/client/rpc-api/src/lib.rs b/client/rpc-api/src/lib.rs index c9306f2cddb79498975b7d953492165063de4aa1..8ad2d94bfd271ea0732ba811f2285e3f585b1554 100644 --- a/client/rpc-api/src/lib.rs +++ b/client/rpc-api/src/lib.rs @@ -30,5 +30,6 @@ pub use helpers::Receiver; pub mod author; pub mod chain; +pub mod offchain; pub mod state; pub mod system; diff --git a/client/rpc-api/src/offchain/error.rs b/client/rpc-api/src/offchain/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..c28a2a2f3911d7e5cd9bedc9e0e84cb60905d6af --- /dev/null +++ b/client/rpc-api/src/offchain/error.rs @@ -0,0 +1,51 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Offchain RPC errors. + +use jsonrpc_core as rpc; + +/// Offchain RPC Result type. +pub type Result = std::result::Result; + +/// Offchain RPC errors. +#[derive(Debug, derive_more::Display, derive_more::From)] +pub enum Error { + /// Unavailable storage kind error. + #[display(fmt="This storage kind is not available yet.")] + UnavailableStorageKind, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} + +/// Base error code for all offchain errors. +const BASE_ERROR: i64 = 5000; + +impl From for rpc::Error { + fn from(e: Error) -> Self { + match e { + Error::UnavailableStorageKind => rpc::Error { + code: rpc::ErrorCode::ServerError(BASE_ERROR + 1), + message: "This storage kind is not available yet" .into(), + data: None, + }, + } + } +} diff --git a/client/rpc-api/src/offchain/mod.rs b/client/rpc-api/src/offchain/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..bbe466ff5994d450164c47e005e0266e83e52440 --- /dev/null +++ b/client/rpc-api/src/offchain/mod.rs @@ -0,0 +1,37 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate offchain API. + +pub mod error; + +use jsonrpc_derive::rpc; +use self::error::Result; +use sp_core::{Bytes, offchain::StorageKind}; + +pub use self::gen_client::Client as OffchainClient; + +/// Substrate offchain RPC API +#[rpc] +pub trait OffchainApi { + /// Set offchain local storage under given key and prefix. + #[rpc(name = "offchain_localStorageSet")] + fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<()>; + + /// Get offchain local storage under given key and prefix. + #[rpc(name = "offchain_localStorageGet")] + fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result>; +} diff --git a/client/rpc-api/src/state/error.rs b/client/rpc-api/src/state/error.rs index 4997a728fc4fb7b5adcd08663df64e56b2282580..c9c2cf4e454d403dd63baaf383d6e85467996306 100644 --- a/client/rpc-api/src/state/error.rs +++ b/client/rpc-api/src/state/error.rs @@ -41,6 +41,14 @@ pub enum Error { /// Details of the error message. details: String, }, + /// Provided count exceeds maximum value. + #[display(fmt = "count exceeds maximum value. value: {}, max: {}", value, max)] + InvalidCount { + /// Provided value + value: u32, + /// Maximum allowed value + max: u32, + }, } impl std::error::Error for Error { @@ -63,6 +71,11 @@ impl From for rpc::Error { message: format!("{}", e), data: None, }, + Error::InvalidCount { .. } => rpc::Error { + code: rpc::ErrorCode::ServerError(BASE_ERROR + 2), + message: format!("{}", e), + data: None, + }, e => errors::internal(e), } } diff --git a/client/rpc-api/src/state/mod.rs b/client/rpc-api/src/state/mod.rs index b22bacbe8706ac576c086f34f69a22726c86ac61..b2cf8ce909b20fead05a3be7bdb9d0d93d6fade1 100644 --- a/client/rpc-api/src/state/mod.rs +++ b/client/rpc-api/src/state/mod.rs @@ -39,10 +39,27 @@ pub trait StateApi { #[rpc(name = "state_call", alias("state_callAt"))] fn call(&self, name: String, bytes: Bytes, hash: Option) -> FutureResult; - /// Returns the keys with prefix, leave empty to get all the keys + /// DEPRECATED: Please use `state_getKeysPaged` with proper paging support. + /// Returns the keys with prefix, leave empty to get all the keys. #[rpc(name = "state_getKeys")] fn storage_keys(&self, prefix: StorageKey, hash: Option) -> FutureResult>; + /// Returns the keys with prefix, leave empty to get all the keys + #[rpc(name = "state_getPairs")] + fn storage_pairs(&self, prefix: StorageKey, hash: Option) -> FutureResult>; + + /// Returns the keys with prefix with pagination support. + /// Up to `count` keys will be returned. + /// If `start_key` is passed, return next keys in storage in lexicographic order. + #[rpc(name = "state_getKeysPaged", alias("state_getKeysPagedAt"))] + fn storage_keys_paged( + &self, + prefix: Option, + count: u32, + start_key: Option, + hash: Option, + ) -> FutureResult>; + /// Returns a storage entry at a specific block's state. #[rpc(name = "state_getStorage", alias("state_getStorageAt"))] fn storage(&self, key: StorageKey, hash: Option) -> FutureResult>; diff --git a/client/rpc-servers/Cargo.toml b/client/rpc-servers/Cargo.toml index e540274e254144f747f1d6d5d17ad23767798c42..6b0d3f4adacf8bc3d5d07b28082455916080df07 100644 --- a/client/rpc-servers/Cargo.toml +++ b/client/rpc-servers/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-rpc-server" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] jsonrpc-core = "14.0.3" diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 6530bff4ed6523f11848496b7f9204c55c5ea336..d09617ebc1658405e87fa127827987d6ddee9d94 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-rpc" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-rpc-api = { version = "0.8", path = "../rpc-api" } @@ -18,6 +19,7 @@ rpc = { package = "jsonrpc-core", version = "14.0.3" } sp-version = { version = "2.0.0", path = "../../primitives/version" } serde_json = "1.0.41" sp-session = { version = "2.0.0", path = "../../primitives/session" } +sp-offchain = { version = "2.0.0", path = "../../primitives/offchain" } sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } sp-rpc = { version = "2.0.0", path = "../../primitives/rpc" } sp-state-machine = { version = "0.8", path = "../../primitives/state-machine" } @@ -26,7 +28,7 @@ sc-keystore = { version = "2.0.0", path = "../keystore" } sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } hash-db = { version = "0.15.2", default-features = false } -parking_lot = { version = "0.9.0" } +parking_lot = "0.10.0" [dev-dependencies] assert_matches = "1.3.0" diff --git a/client/rpc/src/author/mod.rs b/client/rpc/src/author/mod.rs index 9891abc47d03049de791f0b656a1e0f17ba89a3f..06bdcf883c6891548717cd4d44824e0816ba0fd9 100644 --- a/client/rpc/src/author/mod.rs +++ b/client/rpc/src/author/mod.rs @@ -112,6 +112,22 @@ where ).map(Into::into).map_err(|e| Error::Client(Box::new(e))) } + fn has_session_keys(&self, session_keys: Bytes) -> Result { + let best_block_hash = self.client.chain_info().best_hash; + let keys = self.client.runtime_api().decode_session_keys( + &generic::BlockId::Hash(best_block_hash), + session_keys.to_vec(), + ).map_err(|e| Error::Client(Box::new(e)))? + .ok_or_else(|| Error::InvalidSessionKeys)?; + + Ok(self.keystore.read().has_keys(&keys)) + } + + fn has_key(&self, public_key: Bytes, key_type: String) -> Result { + let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; + Ok(self.keystore.read().has_keys(&[(public_key.to_vec(), key_type)])) + } + fn submit_extrinsic(&self, ext: Bytes) -> FutureResult> { let xt = match Decode::decode(&mut &ext[..]) { Ok(xt) => xt, diff --git a/client/rpc/src/author/tests.rs b/client/rpc/src/author/tests.rs index 9fe51d62f51157f2f665b2376d0059c3ec39e100..ba9b9d344c2a2a80f3f287004470f7f52a493bcb 100644 --- a/client/rpc/src/author/tests.rs +++ b/client/rpc/src/author/tests.rs @@ -16,12 +16,12 @@ use super::*; -use std::sync::Arc; +use std::{mem, sync::Arc}; use assert_matches::assert_matches; use codec::Encode; use sp_core::{ - H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore}, traits::BareCryptoStorePtr, ed25519, - crypto::Pair, + H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore}, + traits::BareCryptoStorePtr, ed25519, crypto::{Pair, Public}, }; use rpc::futures::Stream as _; use substrate_test_runtime_client::{ @@ -56,8 +56,15 @@ struct TestSetup { impl Default for TestSetup { fn default() -> Self { let keystore = KeyStore::new(); - let client = Arc::new(substrate_test_runtime_client::TestClientBuilder::new().set_keystore(keystore.clone()).build()); - let pool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))); + let client = Arc::new( + substrate_test_runtime_client::TestClientBuilder::new() + .set_keystore(keystore.clone()) + .build() + ); + let pool = Arc::new(BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + )); TestSetup { runtime: runtime::Runtime::new().expect("Failed to create runtime in test setup"), client, @@ -237,3 +244,59 @@ fn should_rotate_keys() { assert_eq!(session_keys.ed25519, ed25519_key_pair.public().into()); assert_eq!(session_keys.sr25519, sr25519_key_pair.public().into()); } + +#[test] +fn test_has_session_keys() { + let setup = TestSetup::default(); + let p = setup.author(); + + let non_existent_public_keys = TestSetup::default() + .author() + .rotate_keys() + .expect("Rotates the keys"); + + let public_keys = p.rotate_keys().expect("Rotates the keys"); + let test_vectors = vec![ + (public_keys, Ok(true)), + (vec![1, 2, 3].into(), Err(Error::InvalidSessionKeys)), + (non_existent_public_keys, Ok(false)), + ]; + + for (keys, result) in test_vectors { + assert_eq!( + result.map_err(|e| mem::discriminant(&e)), + p.has_session_keys(keys).map_err(|e| mem::discriminant(&e)), + ); + } +} + +#[test] +fn test_has_key() { + let setup = TestSetup::default(); + let p = setup.author(); + + let suri = "//Alice"; + let alice_key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair"); + p.insert_key( + String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"), + suri.to_string(), + alice_key_pair.public().0.to_vec().into(), + ).expect("Insert key"); + let bob_key_pair = ed25519::Pair::from_string("//Bob", None).expect("Generates keypair"); + + let test_vectors = vec![ + (alice_key_pair.public().to_raw_vec().into(), ED25519, Ok(true)), + (alice_key_pair.public().to_raw_vec().into(), SR25519, Ok(false)), + (bob_key_pair.public().to_raw_vec().into(), ED25519, Ok(false)), + ]; + + for (key, key_type, result) in test_vectors { + assert_eq!( + result.map_err(|e| mem::discriminant(&e)), + p.has_key( + key, + String::from_utf8(key_type.0.to_vec()).expect("Keytype is a valid string"), + ).map_err(|e| mem::discriminant(&e)), + ); + } +} diff --git a/client/rpc/src/lib.rs b/client/rpc/src/lib.rs index 3a226a6fb3f9bbf06ec95ab36cf40640b8278c5d..ea65785c20a87c827220bbc6affecafe7ecb9937 100644 --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -28,5 +28,6 @@ pub use rpc::IoHandlerExtension as RpcExtension; pub mod author; pub mod chain; +pub mod offchain; pub mod state; pub mod system; diff --git a/client/rpc/src/offchain/mod.rs b/client/rpc/src/offchain/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..61984d4845a7ab68852dd7da0cef214a37758515 --- /dev/null +++ b/client/rpc/src/offchain/mod.rs @@ -0,0 +1,67 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate offchain API. + +#[cfg(test)] +mod tests; + +/// Re-export the API for backward compatibility. +pub use sc_rpc_api::offchain::*; +use self::error::{Error, Result}; +use sp_core::{ + Bytes, + offchain::{OffchainStorage, StorageKind}, +}; +use parking_lot::RwLock; +use std::sync::Arc; + +/// Offchain API +#[derive(Debug)] +pub struct Offchain { + /// Offchain storage + storage: Arc>, +} + +impl Offchain { + /// Create new instance of Offchain API. + pub fn new(storage: T) -> Self { + Offchain { + storage: Arc::new(RwLock::new(storage)), + } + } +} + +impl OffchainApi for Offchain { + /// Set offchain local storage under given key and prefix. + fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<()> { + let prefix = match kind { + StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, + StorageKind::LOCAL => return Err(Error::UnavailableStorageKind), + }; + self.storage.write().set(prefix, &*key, &*value); + Ok(()) + } + + /// Get offchain local storage under given key and prefix. + fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result> { + let prefix = match kind { + StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, + StorageKind::LOCAL => return Err(Error::UnavailableStorageKind), + }; + Ok(self.storage.read().get(prefix, &*key).map(Into::into)) + } +} diff --git a/client/rpc/src/offchain/tests.rs b/client/rpc/src/offchain/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..ac1a6a4de31bcc95bb1f385c3f7d78a75a83fbe1 --- /dev/null +++ b/client/rpc/src/offchain/tests.rs @@ -0,0 +1,36 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use super::*; +use assert_matches::assert_matches; +use sp_core::{Bytes, offchain::storage::InMemOffchainStorage}; + +#[test] +fn local_storage_should_work() { + let storage = InMemOffchainStorage::default(); + let offchain = Offchain::new(storage); + let key = Bytes(b"offchain_storage".to_vec()); + let value = Bytes(b"offchain_value".to_vec()); + + assert_matches!( + offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()), + Ok(()) + ); + assert_matches!( + offchain.get_local_storage(StorageKind::PERSISTENT, key), + Ok(Some(ref v)) if *v == value + ); +} diff --git a/client/rpc/src/state/mod.rs b/client/rpc/src/state/mod.rs index f8f8fe4223a51b2f3df0e279ce8d4fb83e89cab6..8f621cc8afc9667fed9c15d79db7d9ffa69248cd 100644 --- a/client/rpc/src/state/mod.rs +++ b/client/rpc/src/state/mod.rs @@ -24,7 +24,7 @@ mod tests; use std::sync::Arc; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; -use rpc::{Result as RpcResult, futures::Future}; +use rpc::{Result as RpcResult, futures::{Future, future::result}}; use sc_rpc_api::Subscriptions; use sc_client::{Client, CallExecutor, light::{blockchain::RemoteBlockchain, fetcher::Fetcher}}; @@ -38,6 +38,8 @@ use self::error::{Error, FutureResult}; pub use sc_rpc_api::state::*; +const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000; + /// State backend API. pub trait StateBackend: Send + Sync + 'static where @@ -61,6 +63,22 @@ pub trait StateBackend: Send + Sync + 'static prefix: StorageKey, ) -> FutureResult>; + /// Returns the keys with prefix along with their values, leave empty to get all the pairs. + fn storage_pairs( + &self, + block: Option, + prefix: StorageKey, + ) -> FutureResult>; + + /// Returns the keys with prefix with pagination support. + fn storage_keys_paged( + &self, + block: Option, + prefix: Option, + count: u32, + start_key: Option, + ) -> FutureResult>; + /// Returns a storage entry at a specific block's state. fn storage( &self, @@ -244,6 +262,32 @@ impl StateApi for State self.backend.storage_keys(block, key_prefix) } + fn storage_pairs( + &self, + key_prefix: StorageKey, + block: Option, + ) -> FutureResult> { + self.backend.storage_pairs(block, key_prefix) + } + + fn storage_keys_paged( + &self, + prefix: Option, + count: u32, + start_key: Option, + block: Option, + ) -> FutureResult> { + if count > STORAGE_KEYS_PAGED_MAX_COUNT { + return Box::new(result(Err( + Error::InvalidCount { + value: count, + max: STORAGE_KEYS_PAGED_MAX_COUNT, + } + ))); + } + self.backend.storage_keys_paged(block, prefix, count, start_key) + } + fn storage(&self, key: StorageKey, block: Option) -> FutureResult> { self.backend.storage(block, key) } diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index e42e6b722e49fd03ea504b57c81239f1dc5313f5..3d5613626e0447f28b4ced86a2f3138daed5737c 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -30,13 +30,12 @@ use sp_blockchain::{ Result as ClientResult, Error as ClientError, HeaderMetadata, CachedHeaderMetadata }; use sc_client::{ - Client, CallExecutor, BlockchainEvents, + Client, CallExecutor, BlockchainEvents }; use sp_core::{ Bytes, storage::{well_known_keys, StorageKey, StorageData, StorageChangeSet, ChildInfo}, }; use sp_version::RuntimeVersion; -use sp_state_machine::ExecutionStrategy; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, NumberFor, SaturatedConversion}, }; @@ -213,15 +212,14 @@ impl FullState } } -impl StateBackend for FullState - where - Block: BlockT + 'static, - B: Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + 'static + Clone, - RA: Send + Sync + 'static, - Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: - Metadata, +impl StateBackend for FullState where + Block: BlockT + 'static, + B: Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + 'static + Clone, + RA: Send + Sync + 'static, + Client: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: + Metadata, { fn call( &self, @@ -229,31 +227,59 @@ impl StateBackend for FullState FutureResult { + let r = self.block_or_best(block) + .and_then(|block| self + .client + .executor() + .call( + &BlockId::Hash(block), + &method, + &*call_data, + self.client.execution_extensions().strategies().other, + None, + ) + .map(Into::into) + ).map_err(client_err); + Box::new(result(r)) + } + + fn storage_keys( + &self, + block: Option, + prefix: StorageKey, + ) -> FutureResult> { Box::new(result( self.block_or_best(block) - .and_then(|block| - self - .client - .executor() - .call( - &BlockId::Hash(block), - &method, - &*call_data, - ExecutionStrategy::NativeElseWasm, - None, - ) - .map(Into::into)) + .and_then(|block| self.client.storage_keys(&BlockId::Hash(block), &prefix)) .map_err(client_err))) } - fn storage_keys( + fn storage_pairs( &self, block: Option, prefix: StorageKey, + ) -> FutureResult> { + Box::new(result( + self.block_or_best(block) + .and_then(|block| self.client.storage_pairs(&BlockId::Hash(block), &prefix)) + .map_err(client_err))) + } + + fn storage_keys_paged( + &self, + block: Option, + prefix: Option, + count: u32, + start_key: Option, ) -> FutureResult> { Box::new(result( self.block_or_best(block) - .and_then(|block| self.client.storage_keys(&BlockId::Hash(block), &prefix)) + .and_then(|block| + self.client.storage_keys_iter( + &BlockId::Hash(block), prefix.as_ref(), start_key.as_ref() + ) + ) + .map(|v| v.take(count as usize).collect()) .map_err(client_err))) } diff --git a/client/rpc/src/state/state_light.rs b/client/rpc/src/state/state_light.rs index 482eb0723ff80040b9063d52da96e9a10b400a1e..7b2455a8fce3878fe875d378049178e938ac5f08 100644 --- a/client/rpc/src/state/state_light.rs +++ b/client/rpc/src/state/state_light.rs @@ -199,6 +199,24 @@ impl StateBackend for LightState, + _prefix: StorageKey, + ) -> FutureResult> { + Box::new(result(Err(client_err(ClientError::NotAvailableOnLightClient)))) + } + + fn storage_keys_paged( + &self, + _block: Option, + _prefix: Option, + _count: u32, + _start_key: Option, + ) -> FutureResult> { + Box::new(result(Err(client_err(ClientError::NotAvailableOnLightClient)))) + } + fn storage( &self, block: Option, @@ -708,7 +726,7 @@ fn ignore_error(future: F) -> impl std::future::Future"] edition = "2018" +license = "GPL-3.0" [features] default = ["rocksdb"] @@ -17,12 +18,14 @@ wasmtime = [ derive_more = "0.99.2" futures01 = { package = "futures", version = "0.1.29" } futures = "0.3.1" -parking_lot = "0.9.0" +futures-diagnose = "1.0" +parking_lot = "0.10.0" lazy_static = "1.4.0" log = "0.4.8" slog = { version = "2.5.2", features = ["nested-values"] } tokio-executor = "0.1.8" -futures-timer = "2" +futures-timer = "3.0.1" +wasm-timer = "0.2" exit-future = "0.2.0" serde = "1.0.101" serde_json = "1.0.41" @@ -54,6 +57,7 @@ parity-multiaddr = { package = "parity-multiaddr", version = "0.5.0" } prometheus-exporter = { path = "../../utils/prometheus" } sc-tracing = { version = "2.0.0", path = "../tracing" } tracing = "0.1.10" +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 51112367a6f62a28ec64b2052f7e8e4a3177b9fe..6aae854497d031224431990f2a3606721e9c2011 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{Service, NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID}; +use crate::{Service, NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm}; use crate::{SpawnTaskHandle, start_rpc_servers, build_network_future, TransactionPoolAdapter}; use crate::status_sinks; use crate::config::{Configuration, DatabaseConfig, KeystoreConfig}; @@ -44,12 +44,14 @@ use sp_runtime::traits::{ use sp_api::ProvideRuntimeApi; use sc_executor::{NativeExecutor, NativeExecutionDispatch}; use std::{ + borrow::Cow, io::{Read, Write, Seek}, - marker::PhantomData, sync::Arc, time::SystemTime, pin::Pin + marker::PhantomData, sync::Arc, pin::Pin }; +use wasm_timer::SystemTime; use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; -use sp_transaction_pool::{TransactionPool, TransactionPoolMaintainer}; +use sp_transaction_pool::MaintainedTransactionPool; use sp_blockchain; use prometheus_exporter::{register, Gauge, U64, F64, Registry, PrometheusError, Opts, GaugeVec}; @@ -107,10 +109,10 @@ impl ServiceMetrics { /// The order in which the `with_*` methods are called doesn't matter, as the correct binding of /// generics is done when you call `build`. /// -pub struct ServiceBuilder { - config: Configuration, + config: Configuration, pub (crate) client: Arc, backend: Arc, keystore: Arc>, @@ -180,8 +182,8 @@ type TFullParts = ( ); /// Creates a new full client for the given config. -pub fn new_full_client( - config: &Configuration, +pub fn new_full_client( + config: &Configuration, ) -> Result, Error> where TBl: BlockT, TExecDisp: NativeExecutionDispatch + 'static, @@ -191,8 +193,8 @@ pub fn new_full_client( new_full_parts(config).map(|parts| parts.0) } -fn new_full_parts( - config: &Configuration, +fn new_full_parts( + config: &Configuration, ) -> Result, Error> where TBl: BlockT, TExecDisp: NativeExecutionDispatch + 'static, @@ -213,13 +215,14 @@ fn new_full_parts( config.default_heap_pages, ); - let fork_blocks = config.chain_spec + let chain_spec = config.expect_chain_spec(); + let fork_blocks = chain_spec .extensions() .get::>() .cloned() .unwrap_or_default(); - let bad_blocks = config.chain_spec + let bad_blocks = chain_spec .extensions() .get::>() .cloned() @@ -231,7 +234,7 @@ fn new_full_parts( state_cache_child_ratio: config.state_cache_child_ratio.map(|v| (v, 100)), pruning: config.pruning.clone(), - source: match &config.database { + source: match config.expect_database() { DatabaseConfig::Path { path, cache_size } => sc_client_db::DatabaseSettingsSrc::Path { path: path.clone(), @@ -250,7 +253,7 @@ fn new_full_parts( sc_client_db::new_client( db_config, executor, - &config.chain_spec, + config.expect_chain_spec(), fork_blocks, bad_blocks, extensions, @@ -260,15 +263,14 @@ fn new_full_parts( Ok((client, backend, keystore)) } -impl ServiceBuilder<(), (), TCfg, TGen, TCSExt, (), (), (), (), (), (), (), (), (), ()> +impl ServiceBuilder<(), (), TGen, TCSExt, (), (), (), (), (), (), (), (), (), ()> where TGen: RuntimeGenesis, TCSExt: Extension { /// Start the service builder with a configuration. pub fn new_full( - config: Configuration + config: Configuration ) -> Result, @@ -307,11 +309,10 @@ where TGen: RuntimeGenesis, TCSExt: Extension { /// Start the service builder with a configuration. pub fn new_light( - config: Configuration + config: Configuration ) -> Result, @@ -345,7 +346,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension { state_cache_child_ratio: config.state_cache_child_ratio.map(|v| (v, 100)), pruning: config.pruning.clone(), - source: match &config.database { + source: match config.expect_database() { DatabaseConfig::Path { path, cache_size } => sc_client_db::DatabaseSettingsSrc::Path { path: path.clone(), @@ -369,7 +370,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension { let remote_blockchain = backend.remote_blockchain(); let client = Arc::new(sc_client::light::new_light( backend.clone(), - &config.chain_spec, + config.expect_chain_spec(), executor, )?); @@ -393,8 +394,8 @@ where TGen: RuntimeGenesis, TCSExt: Extension { } } -impl - ServiceBuilder + ServiceBuilder { /// Returns a reference to the client that was stored in this builder. @@ -416,9 +417,9 @@ impl( self, select_chain_builder: impl FnOnce( - &Configuration, &Arc + &Configuration, &Arc ) -> Result, Error> - ) -> Result Result, Error> { let select_chain = select_chain_builder(&self.config, &self.backend)?; @@ -444,8 +445,8 @@ impl( self, - builder: impl FnOnce(&Configuration, &Arc) -> Result - ) -> Result, &Arc) -> Result + ) -> Result, Error> { self.with_opt_select_chain(|cfg, b| builder(cfg, b).map(Option::Some)) } @@ -453,9 +454,9 @@ impl( self, - builder: impl FnOnce(&Configuration, Arc, Option, Arc) + builder: impl FnOnce(&Configuration, Arc, Option, Arc) -> Result - ) -> Result Result, Error> where TSc: Clone { let import_queue = builder( @@ -487,8 +488,8 @@ impl( self, - network_protocol_builder: impl FnOnce(&Configuration) -> Result - ) -> Result) -> Result + ) -> Result, Error> { let network_protocol = network_protocol_builder(&self.config)?; @@ -518,7 +519,6 @@ impl Result Result( self, builder: impl FnOnce( - &Configuration, + &Configuration, Arc, Arc, Option, Option, Arc, ) -> Result<(UImpQu, Option), Error> - ) -> Result Result, Error> where TSc: Clone, TFchr: Clone { let (import_queue, fprb) = builder( @@ -623,14 +622,14 @@ impl( self, builder: impl FnOnce( - &Configuration, + &Configuration, Arc, Arc, Option, Option, Arc, ) -> Result<(UImpQu, UFprb), Error> - ) -> Result Result, Error> where TSc: Clone, TFchr: Clone { self.with_import_queue_and_opt_fprb(|cfg, cl, b, f, sc, tx| @@ -647,7 +646,7 @@ impl, Option, ) -> Result - ) -> Result Result, Error> where TSc: Clone, TFchr: Clone { let transaction_pool = transaction_pool_builder( @@ -685,7 +684,7 @@ impl, Option>>, ) -> Result, - ) -> Result Result, Error> where TSc: Clone, TFchr: Clone { let rpc_extensions = rpc_ext_builder( @@ -742,6 +741,8 @@ impl Pin> + Send>>; } -impl +impl ServiceBuilder< TBl, TRtApi, - TCfg, TGen, TCSExt, Client, @@ -799,7 +799,6 @@ ServiceBuilder< sp_api::ApiExt, TBl: BlockT, TRtApi: 'static + Send + Sync, - TCfg: Default, TGen: RuntimeGenesis, TCSExt: Extension, TBackend: 'static + sc_client_api::backend::Backend + Send, @@ -807,9 +806,7 @@ ServiceBuilder< TSc: Clone, TImpQu: 'static + ImportQueue, TNetP: NetworkSpecialization, - TExPool: 'static - + TransactionPool::Hash> - + TransactionPoolMaintainer::Hash>, + TExPool: MaintainedTransactionPool::Hash> + MallocSizeOfWasm + 'static, TRpc: sc_rpc::RpcExtension + Clone, { @@ -861,13 +858,14 @@ ServiceBuilder< // List of asynchronous tasks to spawn. We collect them, then spawn them all at once. let (to_spawn_tx, to_spawn_rx) = - mpsc::unbounded:: + Send>>>(); + mpsc::unbounded::<(Pin + Send>>, Cow<'static, str>)>(); // A side-channel for essential tasks to communicate shutdown. let (essential_failed_tx, essential_failed_rx) = mpsc::unbounded(); let import_queue = Box::new(import_queue); let chain_info = client.chain_info(); + let chain_spec = config.expect_chain_spec(); let version = config.full_version(); info!("Highest known block at #{}", chain_info.best_number); @@ -886,11 +884,11 @@ ServiceBuilder< imports_external_transactions: !config.roles.is_light(), pool: transaction_pool.clone(), client: client.clone(), - executor: Arc::new(SpawnTaskHandle { sender: to_spawn_tx.clone(), on_exit: exit.clone() }), + executor: SpawnTaskHandle { sender: to_spawn_tx.clone(), on_exit: exit.clone() }, }); let protocol_id = { - let protocol_id_full = match config.chain_spec.protocol_id() { + let protocol_id_full = match chain_spec.protocol_id() { Some(pid) => pid, None => { warn!("Using default protocol ID {:?} because none is configured in the \ @@ -907,6 +905,14 @@ ServiceBuilder< let network_params = sc_network::config::Params { roles: config.roles, + executor: { + let to_spawn_tx = to_spawn_tx.clone(); + Some(Box::new(move |fut| { + if let Err(e) = to_spawn_tx.unbounded_send((fut, From::from("libp2p-node"))) { + error!("Failed to spawn libp2p background task: {:?}", e); + } + })) + }, network_config: config.network.clone(), chain: client.clone(), finality_proof_provider, @@ -925,7 +931,7 @@ ServiceBuilder< let network_status_sinks = Arc::new(Mutex::new(status_sinks::StatusSinks::new())); let offchain_storage = backend.offchain_storage(); - let offchain_workers = match (config.offchain_worker, offchain_storage) { + let offchain_workers = match (config.offchain_worker, offchain_storage.clone()) { (true, Some(db)) => { Some(Arc::new(sc_offchain::OffchainWorkers::new(client.clone(), db))) }, @@ -953,22 +959,39 @@ ServiceBuilder< &BlockId::hash(notification.hash), ¬ification.retracted, ); - let _ = to_spawn_tx_.unbounded_send(Box::pin(future)); + let _ = to_spawn_tx_.unbounded_send(( + Box::pin(future), + From::from("txpool-maintain") + )); } let offchain = offchain.as_ref().and_then(|o| o.upgrade()); - if let Some(offchain) = offchain { - let future = offchain.on_block_imported( - ¬ification.header, - network_state_info.clone(), - is_validator - ); - let _ = to_spawn_tx_.unbounded_send(Box::pin(future)); + match offchain { + Some(offchain) if notification.is_new_best => { + let future = offchain.on_block_imported( + ¬ification.header, + network_state_info.clone(), + is_validator, + ); + let _ = to_spawn_tx_.unbounded_send(( + Box::pin(future), + From::from("offchain-on-block"), + )); + }, + Some(_) => log::debug!( + target: "sc_offchain", + "Skipping offchain workers for non-canon block: {:?}", + notification.header, + ), + _ => {}, } ready(()) }); - let _ = to_spawn_tx.unbounded_send(Box::pin(select(events, exit.clone()).map(drop))); + let _ = to_spawn_tx.unbounded_send(( + Box::pin(select(events, exit.clone()).map(drop)), + From::from("txpool-and-offchain-notif") + )); } { @@ -976,9 +999,9 @@ ServiceBuilder< let network = Arc::downgrade(&network); let transaction_pool_ = transaction_pool.clone(); let events = transaction_pool.import_notification_stream() - .for_each(move |_| { + .for_each(move |hash| { if let Some(network) = network.upgrade() { - network.trigger_repropagate(); + network.propagate_extrinsic(hash); } let status = transaction_pool_.status(); telemetry!(SUBSTRATE_INFO; "txpool.import"; @@ -988,7 +1011,10 @@ ServiceBuilder< ready(()) }); - let _ = to_spawn_tx.unbounded_send(Box::pin(select(events, exit.clone()).map(drop))); + let _ = to_spawn_tx.unbounded_send(( + Box::pin(select(events, exit.clone()).map(drop)), + From::from("telemetry-on-block") + )); } // Prometheus exporter and metrics @@ -1077,7 +1103,11 @@ ServiceBuilder< ready(()) }); - let _ = to_spawn_tx.unbounded_send(Box::pin(select(tel_task, exit.clone()).map(drop))); + + let _ = to_spawn_tx.unbounded_send(( + Box::pin(select(tel_task, exit.clone()).map(drop)), + From::from("telemetry-periodic-send") + )); // Periodically send the network state to the telemetry. let (netstat_tx, netstat_rx) = mpsc::unbounded::<(NetworkStatus<_>, NetworkState)>(); @@ -1090,18 +1120,21 @@ ServiceBuilder< ); ready(()) }); - let _ = to_spawn_tx.unbounded_send(Box::pin(select(tel_task_2, exit.clone()).map(drop))); + let _ = to_spawn_tx.unbounded_send(( + Box::pin(select(tel_task_2, exit.clone()).map(drop)), + From::from("telemetry-periodic-network-state") + )); // RPC let (system_rpc_tx, system_rpc_rx) = mpsc::unbounded(); let gen_handler = || { - use sc_rpc::{chain, state, author, system}; + use sc_rpc::{chain, state, author, system, offchain}; let system_info = sc_rpc::system::SystemInfo { - chain_name: config.chain_spec.name().into(), + chain_name: chain_spec.name().into(), impl_name: config.impl_name.into(), impl_version: config.impl_version.into(), - properties: config.chain_spec.properties().clone(), + properties: chain_spec.properties().clone(), }; let subscriptions = sc_rpc::Subscriptions::new(Arc::new(SpawnTaskHandle { @@ -1141,28 +1174,44 @@ ServiceBuilder< ); let system = system::System::new(system_info, system_rpc_tx.clone()); - sc_rpc_server::rpc_handler(( - state::StateApi::to_delegate(state), - chain::ChainApi::to_delegate(chain), - author::AuthorApi::to_delegate(author), - system::SystemApi::to_delegate(system), - rpc_extensions.clone(), - )) + match offchain_storage.clone() { + Some(storage) => { + let offchain = sc_rpc::offchain::Offchain::new(storage); + sc_rpc_server::rpc_handler(( + state::StateApi::to_delegate(state), + chain::ChainApi::to_delegate(chain), + offchain::OffchainApi::to_delegate(offchain), + author::AuthorApi::to_delegate(author), + system::SystemApi::to_delegate(system), + rpc_extensions.clone(), + )) + }, + None => sc_rpc_server::rpc_handler(( + state::StateApi::to_delegate(state), + chain::ChainApi::to_delegate(chain), + author::AuthorApi::to_delegate(author), + system::SystemApi::to_delegate(system), + rpc_extensions.clone(), + )) + } }; let rpc_handlers = gen_handler(); let rpc = start_rpc_servers(&config, gen_handler)?; - let _ = to_spawn_tx.unbounded_send(Box::pin(select(build_network_future( - config.roles, - network_mut, - client.clone(), - network_status_sinks.clone(), - system_rpc_rx, - has_bootnodes, - ), exit.clone()).map(drop))); + let _ = to_spawn_tx.unbounded_send(( + Box::pin(select(build_network_future( + config.roles, + network_mut, + client.clone(), + network_status_sinks.clone(), + system_rpc_rx, + has_bootnodes, + ), exit.clone()).map(drop)), + From::from("network-worker") + )); - let telemetry_connection_sinks: Arc>>> = Default::default(); + let telemetry_connection_sinks: Arc>>> = Default::default(); // Telemetry let telemetry = config.telemetry_endpoints.clone().map(|endpoints| { @@ -1171,7 +1220,7 @@ ServiceBuilder< let name = config.name.clone(); let impl_name = config.impl_name.to_owned(); let version = version.clone(); - let chain_name = config.chain_spec.name().to_owned(); + let chain_name = config.expect_chain_spec().name().to_owned(); let telemetry_connection_sinks_ = telemetry_connection_sinks.clone(); let telemetry = sc_telemetry::init_telemetry(sc_telemetry::TelemetryConfig { endpoints, @@ -1201,9 +1250,9 @@ ServiceBuilder< }); ready(()) }); - let _ = to_spawn_tx.unbounded_send(Box::pin(select( + let _ = to_spawn_tx.unbounded_send((Box::pin(select( future, exit.clone() - ).map(drop))); + ).map(drop)), From::from("telemetry-worker"))); telemetry }); @@ -1230,10 +1279,10 @@ ServiceBuilder< essential_failed_rx, to_spawn_tx, to_spawn_rx, - tasks_executor: if let Some(exec) = config.tasks_executor { + task_executor: if let Some(exec) = config.task_executor { exec } else { - return Err(Error::TasksExecutorRequired); + return Err(Error::TaskExecutorRequired); }, rpc_handlers, _rpc: rpc, diff --git a/client/service/src/chain_ops.rs b/client/service/src/chain_ops.rs index 0c2fe79718a72e1b815859143009b94df7f87f98..3d77d9c815e4f5c70e6e6c52e3eb2d392f0afcfb 100644 --- a/client/service/src/chain_ops.rs +++ b/client/service/src/chain_ops.rs @@ -22,14 +22,20 @@ use crate::error::Error; use sc_chain_spec::{ChainSpec, RuntimeGenesis, Extension}; use log::{warn, info}; use futures::{future, prelude::*}; -use sp_runtime::traits::{ - Block as BlockT, NumberFor, One, Zero, Header, SaturatedConversion +use sp_runtime::{ + BuildStorage, BenchmarkResults, + traits::{ + Block as BlockT, NumberFor, One, Zero, Header, SaturatedConversion + } }; use sp_runtime::generic::{BlockId, SignedBlock}; use codec::{Decode, Encode, IoReader}; -use sc_client::Client; +use sc_client::{Client, ExecutionStrategy, StateMachine, LocalCallExecutor}; +#[cfg(feature = "rocksdb")] +use sc_client_db::BenchmarkingState; use sp_consensus::import_queue::{IncomingBlock, Link, BlockImportError, BlockImportResult, ImportQueue}; use sp_consensus::BlockOrigin; +use sc_executor::{NativeExecutor, NativeExecutionDispatch, WasmExecutionMethod}; use std::{io::{Read, Write, Seek}, pin::Pin}; @@ -43,21 +49,76 @@ pub fn build_spec(spec: ChainSpec, raw: bool) -> error::Result ( + spec: ChainSpec, + strategy: ExecutionStrategy, + wasm_method: WasmExecutionMethod, + pallet: String, + extrinsic: String, + steps: u32, + repeat: u32, +) -> error::Result<()> where + TBl: BlockT, + TExecDisp: NativeExecutionDispatch + 'static, + G: RuntimeGenesis, + E: Extension, +{ + let genesis_storage = spec.build_storage()?; + let mut changes = Default::default(); + let state = BenchmarkingState::::new(genesis_storage)?; + let executor = NativeExecutor::::new( + wasm_method, + None, // heap pages + ); + let result = StateMachine::<_, _, NumberFor, _>::new( + &state, + None, + &mut changes, + &executor, + "Benchmark_dispatch_benchmark", + &(&pallet, &extrinsic, steps, repeat).encode(), + Default::default(), + ).execute(strategy).map_err(|e| format!("Error executing runtime benchmark: {:?}", e))?; + let results = > as Decode>::decode(&mut &result[..]).unwrap_or(None); + if let Some(results) = results { + // Print benchmark metadata + println!("Pallet: {:?}, Extrinsic: {:?}, Steps: {:?}, Repeat: {:?}", pallet, extrinsic, steps, repeat); + // Print the table header + results[0].0.iter().for_each(|param| print!("{:?},", param.0)); + print!("time\n"); + // Print the values + results.iter().for_each(|result| { + let parameters = &result.0; + parameters.iter().for_each(|param| print!("{:?},", param.1)); + print!("{:?}\n", result.1); + }); + info!("Done."); + } else { + info!("No Results."); + } + Ok(()) +} + + impl< - TBl, TRtApi, TCfg, TGen, TCSExt, TBackend, - TExec, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, + TBl, TRtApi, TGen, TCSExt, TBackend, + TExecDisp, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, Backend > ServiceBuilderCommand for ServiceBuilder< - TBl, TRtApi, TCfg, TGen, TCSExt, Client, + TBl, TRtApi, TGen, TCSExt, + Client>, TBl, TRtApi>, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, Backend > where TBl: BlockT, TBackend: 'static + sc_client_api::backend::Backend + Send, - TExec: 'static + sc_client::CallExecutor + Send + Sync + Clone, + TExecDisp: 'static + NativeExecutionDispatch, TImpQu: 'static + ImportQueue, TRtApi: 'static + Send + Sync, { type Block = TBl; + type NativeDispatch = TExecDisp; fn import_blocks( self, diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 0404018216e904db005ceb1ad79cbe576b335d4d..ef411b5eb999944a505bb18f084ab9ecb8a5e3d9 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -23,13 +23,34 @@ pub use sc_executor::WasmExecutionMethod; use std::{future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc}; pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions; -use sc_chain_spec::{ChainSpec, RuntimeGenesis, Extension, NoExtension}; +use sc_chain_spec::{ChainSpec, NoExtension}; use sp_core::crypto::Protected; use target_info::Target; use sc_telemetry::TelemetryEndpoints; +/// Executable version. Used to pass version information from the root crate. +#[derive(Clone)] +pub struct VersionInfo { + /// Implementation name. + pub name: &'static str, + /// Implementation version. + pub version: &'static str, + /// SCM Commit hash. + pub commit: &'static str, + /// Executable file name. + pub executable_name: &'static str, + /// Executable file description. + pub description: &'static str, + /// Executable file author. + pub author: &'static str, + /// Support URL. + pub support_url: &'static str, + /// Copyright starting year (x-current year) + pub copyright_start_year: i32, +} + /// Service configuration. -pub struct Configuration { +pub struct Configuration { /// Implementation name pub impl_name: &'static str, /// Implementation version @@ -39,7 +60,7 @@ pub struct Configuration { /// Node roles. pub roles: Roles, /// How to spawn background tasks. Mandatory, otherwise creating a `Service` will error. - pub tasks_executor: Option + Send>>) + Send>>, + pub task_executor: Option + Send>>) + Send + Sync>>, /// Extrinsic pool configuration. pub transaction_pool: TransactionPoolOptions, /// Network configuration. @@ -49,7 +70,7 @@ pub struct Configuration { /// Configuration for the keystore. pub keystore: KeystoreConfig, /// Configuration for the database. - pub database: DatabaseConfig, + pub database: Option, /// Size of internal state cache in Bytes pub state_cache_size: usize, /// Size in percent of cache size dedicated to child tries @@ -57,9 +78,7 @@ pub struct Configuration { /// Pruning settings. pub pruning: PruningMode, /// Chain configuration. - pub chain_spec: ChainSpec, - /// Custom configuration. - pub custom: C, + pub chain_spec: Option>, /// Node name. pub name: String, /// Wasm execution method. @@ -146,32 +165,24 @@ pub enum DatabaseConfig { Custom(Arc), } -impl Configuration where - C: Default, - G: RuntimeGenesis, - E: Extension, -{ - /// Create a default config for given chain spec and path to configuration dir - pub fn default_with_spec_and_base_path(chain_spec: ChainSpec, config_dir: Option) -> Self { - let mut configuration = Configuration { +impl Default for Configuration { + /// Create a default config + fn default() -> Self { + Configuration { impl_name: "parity-substrate", impl_version: "0.0.0", impl_commit: "", - chain_spec, - config_dir: config_dir.clone(), + chain_spec: None, + config_dir: None, name: Default::default(), roles: Roles::FULL, - tasks_executor: None, + task_executor: None, transaction_pool: Default::default(), network: Default::default(), keystore: KeystoreConfig::None, - database: DatabaseConfig::Path { - path: Default::default(), - cache_size: Default::default(), - }, + database: None, state_cache_size: Default::default(), state_cache_child_ratio: Default::default(), - custom: Default::default(), pruning: PruningMode::default(), wasm_method: WasmExecutionMethod::Interpreted, execution_strategies: Default::default(), @@ -190,17 +201,21 @@ impl Configuration where dev_key_seed: None, tracing_targets: Default::default(), tracing_receiver: Default::default(), - }; - configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec(); + } + } +} - configuration.telemetry_endpoints = configuration.chain_spec.telemetry_endpoints().clone(); +impl Configuration { + /// Create a default config using `VersionInfo` + pub fn new(version: &VersionInfo) -> Self { + let mut config = Configuration::default(); + config.impl_name = version.name; + config.impl_version = version.version; + config.impl_commit = version.commit; - configuration + config } -} - -impl Configuration { /// Returns full version string of this configuration. pub fn full_version(&self) -> String { full_version_from_strs(self.impl_version, self.impl_commit) @@ -216,11 +231,29 @@ impl Configuration { pub fn in_chain_config_dir(&self, sub: &str) -> Option { self.config_dir.clone().map(|mut path| { path.push("chains"); - path.push(self.chain_spec.id()); + path.push(self.expect_chain_spec().id()); path.push(sub); path }) } + + /// Return a reference to the `ChainSpec` of this `Configuration`. + /// + /// ### Panics + /// + /// This method panic if the `chain_spec` is `None` + pub fn expect_chain_spec(&self) -> &ChainSpec { + self.chain_spec.as_ref().expect("chain_spec must be specified") + } + + /// Return a reference to the `DatabaseConfig` of this `Configuration`. + /// + /// ### Panics + /// + /// This method panic if the `database` is `None` + pub fn expect_database(&self) -> &DatabaseConfig { + self.database.as_ref().expect("database must be specified") + } } /// Returns platform info diff --git a/client/service/src/error.rs b/client/service/src/error.rs index e434cbf6de2567dcbf5724f3cb13f7f70b6c69fa..4d0a2cef942337a816de670882e7a6fe42df5d0e 100644 --- a/client/service/src/error.rs +++ b/client/service/src/error.rs @@ -42,7 +42,7 @@ pub enum Error { SelectChainRequired, /// Tasks executor is missing. #[display(fmt="Tasks executor hasn't been provided.")] - TasksExecutorRequired, + TaskExecutorRequired, /// Other error. Other(String), } diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index c1b87e4491904e8d2698c140e700e7dd7caafe29..8c2f57dd7cb4f543ccc7995e89460f533171a2e3 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -27,11 +27,12 @@ pub mod error; mod builder; mod status_sinks; -use std::{io, pin::Pin}; +use std::{borrow::Cow, io, pin::Pin}; use std::marker::PhantomData; use std::net::SocketAddr; use std::collections::HashMap; -use std::time::{Duration, Instant}; +use std::time::Duration; +use wasm_timer::Instant; use std::task::{Poll, Context}; use parking_lot::Mutex; @@ -42,7 +43,7 @@ use futures::{ future::select, channel::mpsc, compat::*, sink::SinkExt, - task::{Spawn, SpawnExt, FutureObj, SpawnError}, + task::{Spawn, FutureObj, SpawnError}, }; use sc_network::{ NetworkService, NetworkState, specialization::NetworkSpecialization, @@ -52,6 +53,7 @@ use log::{log, warn, debug, error, Level}; use codec::{Encode, Decode}; use sp_runtime::generic::BlockId; use sp_runtime::traits::{NumberFor, Block as BlockT}; +use parity_util_mem::MallocSizeOf; pub use self::error::Error; pub use self::builder::{ @@ -61,10 +63,11 @@ pub use self::builder::{ }; pub use config::{Configuration, Roles, PruningMode}; pub use sc_chain_spec::{ChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension}; -pub use sp_transaction_pool::{TransactionPool, TransactionPoolMaintainer, InPoolTransaction, error::IntoPoolError}; +pub use sp_transaction_pool::{TransactionPool, InPoolTransaction, error::IntoPoolError}; pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions; pub use sc_client::FinalityNotifications; pub use sc_rpc::Metadata as RpcMetadata; +pub use sc_executor::NativeExecutionDispatch; #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; #[doc(hidden)] @@ -72,6 +75,16 @@ pub use sc_network::{FinalityProofProvider, OnDemand, config::BoxFinalityProofRe const DEFAULT_PROTOCOL_ID: &str = "sup"; +/// A type that implements `MallocSizeOf` on native but not wasm. +#[cfg(not(target_os = "unknown"))] +pub trait MallocSizeOfWasm: MallocSizeOf {} +#[cfg(target_os = "unknown")] +pub trait MallocSizeOfWasm {} +#[cfg(not(target_os = "unknown"))] +impl MallocSizeOfWasm for T {} +#[cfg(target_os = "unknown")] +impl MallocSizeOfWasm for T {} + /// Substrate service. pub struct Service { client: Arc, @@ -92,15 +105,15 @@ pub struct Service { /// A receiver for spawned essential-tasks concluding. essential_failed_rx: mpsc::UnboundedReceiver<()>, /// Sender for futures that must be spawned as background tasks. - to_spawn_tx: mpsc::UnboundedSender + Send>>>, + to_spawn_tx: mpsc::UnboundedSender<(Pin + Send>>, Cow<'static, str>)>, /// Receiver for futures that must be spawned as background tasks. - to_spawn_rx: mpsc::UnboundedReceiver + Send>>>, + to_spawn_rx: mpsc::UnboundedReceiver<(Pin + Send>>, Cow<'static, str>)>, /// How to spawn background tasks. - tasks_executor: Box + Send>>) + Send>, + task_executor: Arc + Send>>) + Send + Sync>, rpc_handlers: sc_rpc_server::RpcHandler, _rpc: Box, _telemetry: Option, - _telemetry_on_connect_sinks: Arc>>>, + _telemetry_on_connect_sinks: Arc>>>, _offchain_workers: Option>, keystore: sc_keystore::KeyStorePtr, marker: PhantomData, @@ -112,15 +125,29 @@ pub type TaskExecutor = Arc; /// An handle for spawning tasks in the service. #[derive(Clone)] pub struct SpawnTaskHandle { - sender: mpsc::UnboundedSender + Send>>>, + sender: mpsc::UnboundedSender<(Pin + Send>>, Cow<'static, str>)>, on_exit: exit_future::Exit, } +impl SpawnTaskHandle { + /// Spawns the given task with the given name. + pub fn spawn(&self, name: impl Into>, task: impl Future + Send + 'static) { + let on_exit = self.on_exit.clone(); + let future = async move { + futures::pin_mut!(task); + let _ = select(on_exit, task).await; + }; + if self.sender.unbounded_send((Box::pin(future), name.into())).is_err() { + error!("Failed to send task to spawn over channel"); + } + } +} + impl Spawn for SpawnTaskHandle { fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { let future = select(self.on_exit.clone(), future).map(drop); - self.sender.unbounded_send(Box::pin(future)) + self.sender.unbounded_send((Box::pin(future), From::from("unnamed"))) .map_err(|_| SpawnError::shutdown()) } } @@ -129,7 +156,7 @@ type Boxed01Future01 = Box + Send + impl futures01::future::Executor for SpawnTaskHandle { fn execute(&self, future: Boxed01Future01) -> Result<(), futures01::future::ExecuteError>{ - self.spawn(future.compat().map(drop)); + self.spawn("unnamed", future.compat().map(drop)); Ok(()) } } @@ -148,24 +175,23 @@ pub trait AbstractService: 'static + Future> + /// Chain selection algorithm. type SelectChain: sp_consensus::SelectChain; /// Transaction pool. - type TransactionPool: TransactionPool - + TransactionPoolMaintainer; + type TransactionPool: TransactionPool + MallocSizeOfWasm; /// Network specialization. type NetworkSpecialization: NetworkSpecialization; /// Get event stream for telemetry connection established events. - fn telemetry_on_connect_stream(&self) -> mpsc::UnboundedReceiver<()>; + fn telemetry_on_connect_stream(&self) -> futures::channel::mpsc::UnboundedReceiver<()>; /// return a shared instance of Telemetry (if enabled) fn telemetry(&self) -> Option; /// Spawns a task in the background that runs the future passed as parameter. - fn spawn_task(&self, task: impl Future + Send + Unpin + 'static); + fn spawn_task(&self, name: impl Into>, task: impl Future + Send + 'static); /// Spawns a task in the background that runs the future passed as /// parameter. The given task is considered essential, i.e. if it errors we /// trigger a service exit. - fn spawn_essential_task(&self, task: impl Future + Send + Unpin + 'static); + fn spawn_essential_task(&self, name: impl Into>, task: impl Future + Send + 'static); /// Returns a handle for spawning tasks. fn spawn_task_handle(&self) -> SpawnTaskHandle; @@ -213,8 +239,7 @@ where TExec: 'static + sc_client::CallExecutor + Send + Sync + Clone, TRtApi: 'static + Send + Sync, TSc: sp_consensus::SelectChain + 'static + Clone + Send + Unpin, - TExPool: 'static + TransactionPool - + TransactionPoolMaintainer, + TExPool: 'static + TransactionPool + MallocSizeOfWasm, TOc: 'static + Send + Sync, TNetSpec: NetworkSpecialization, { @@ -226,8 +251,8 @@ where type TransactionPool = TExPool; type NetworkSpecialization = TNetSpec; - fn telemetry_on_connect_stream(&self) -> mpsc::UnboundedReceiver<()> { - let (sink, stream) = mpsc::unbounded(); + fn telemetry_on_connect_stream(&self) -> futures::channel::mpsc::UnboundedReceiver<()> { + let (sink, stream) = futures::channel::mpsc::unbounded(); self._telemetry_on_connect_sinks.lock().push(sink); stream } @@ -240,12 +265,16 @@ where self.keystore.clone() } - fn spawn_task(&self, task: impl Future + Send + Unpin + 'static) { - let task = select(self.on_exit(), task).map(drop); - let _ = self.to_spawn_tx.unbounded_send(Box::pin(task)); + fn spawn_task(&self, name: impl Into>, task: impl Future + Send + 'static) { + let on_exit = self.on_exit(); + let task = async move { + futures::pin_mut!(task); + let _ = select(on_exit, task).await; + }; + let _ = self.to_spawn_tx.unbounded_send((Box::pin(task), name.into())); } - fn spawn_essential_task(&self, task: impl Future + Send + Unpin + 'static) { + fn spawn_essential_task(&self, name: impl Into>, task: impl Future + Send + 'static) { let mut essential_failed = self.essential_failed_tx.clone(); let essential_task = std::panic::AssertUnwindSafe(task) .catch_unwind() @@ -253,9 +282,13 @@ where error!("Essential task failed. Shutting down service."); let _ = essential_failed.send(()); }); - let task = select(self.on_exit(), essential_task).map(drop); + let on_exit = self.on_exit(); + let task = async move { + futures::pin_mut!(essential_task); + let _ = select(on_exit, essential_task).await; + }; - let _ = self.to_spawn_tx.unbounded_send(Box::pin(task)); + let _ = self.to_spawn_tx.unbounded_send((Box::pin(task), name.into())); } fn spawn_task_handle(&self) -> SpawnTaskHandle { @@ -319,8 +352,8 @@ impl Future for } } - while let Poll::Ready(Some(task_to_spawn)) = Pin::new(&mut this.to_spawn_rx).poll_next(cx) { - (this.tasks_executor)(task_to_spawn); + while let Poll::Ready(Some((task_to_spawn, name))) = Pin::new(&mut this.to_spawn_rx).poll_next(cx) { + (this.task_executor)(Box::pin(futures_diagnose::diagnose(name, task_to_spawn))); } // The service future never ends. @@ -335,7 +368,7 @@ impl Spawn for &self, future: FutureObj<'static, ()> ) -> Result<(), SpawnError> { - self.to_spawn_tx.unbounded_send(Box::pin(future)) + self.to_spawn_tx.unbounded_send((Box::pin(future), From::from("unnamed"))) .map_err(|_| SpawnError::shutdown()) } } @@ -503,8 +536,8 @@ impl Drop for /// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them alive. #[cfg(not(target_os = "unknown"))] -fn start_rpc_servers sc_rpc_server::RpcHandler>( - config: &Configuration, +fn start_rpc_servers sc_rpc_server::RpcHandler>( + config: &Configuration, mut gen_handler: H ) -> Result, error::Error> { fn maybe_start_server(address: Option, mut start: F) -> Result, io::Error> @@ -544,8 +577,8 @@ fn start_rpc_servers sc_rpc_server::RpcHandler sc_rpc_server::RpcHandler>( - _: &Configuration, +fn start_rpc_servers sc_rpc_server::RpcHandler>( + _: &Configuration, _: H ) -> Result, error::Error> { Ok(Box::new(())) @@ -577,7 +610,7 @@ pub struct TransactionPoolAdapter { imports_external_transactions: bool, pool: Arc

, client: Arc, - executor: TaskExecutor, + executor: SpawnTaskHandle, } /// Get transactions for propagation. @@ -610,7 +643,7 @@ where H: std::hash::Hash + Eq + sp_runtime::traits::Member + sp_runtime::traits::MaybeSerialize, E: 'static + IntoPoolError + From, { - fn transactions(&self) -> Vec<(H, ::Extrinsic)> { + fn transactions(&self) -> Vec<(H, B::Extrinsic)> { transactions_to_propagate(&*self.pool) } @@ -651,9 +684,7 @@ where } }); - if let Err(e) = self.executor.spawn(Box::new(import_future)) { - warn!("Error scheduling extrinsic import: {:?}", e); - } + self.executor.spawn("extrinsic-import", import_future); } Err(e) => debug!("Error decoding transaction {}", e), } @@ -662,6 +693,10 @@ where fn on_broadcasted(&self, propagations: HashMap>) { self.pool.on_broadcasted(propagations) } + + fn transaction(&self, hash: &H) -> Option { + self.pool.ready_transaction(hash).map(|tx| tx.data().clone()) + } } #[cfg(test)] @@ -678,7 +713,10 @@ mod tests { // given let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain(); let client = Arc::new(client); - let pool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))); + let pool = Arc::new(BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + )); let best = longest_chain.best_chain().unwrap(); let transaction = Transfer { amount: 5, diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index 01ccf71a4bd7bab81543cb7df7c34d89f6b0fb41..c9dbe97464aa135a12dc2928eb2262478152315d 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -3,6 +3,7 @@ name = "sc-service-test" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] tempfile = "3.1.0" diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 7e3838fb2c8aac7b404c6899dcf875c1634a0f41..d2f186927f6398963d6b3259b31d8cede3b9c491 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -133,11 +133,11 @@ fn node_config ( index: usize, spec: &ChainSpec, role: Roles, - tasks_executor: Box + Send>>) + Send>, + task_executor: Arc + Send>>) + Send + Sync>, key_seed: Option, base_port: u16, root: &TempDir, -) -> Configuration<(), G, E> +) -> Configuration { let root = root.path().join(format!("node-{}", index)); @@ -166,6 +166,7 @@ fn node_config ( enable_mdns: false, allow_private_ipv4: true, wasm_external_transport: None, + use_yamux_flow_control: true, }, max_parallel_downloads: NetworkConfiguration::default().max_parallel_downloads, }; @@ -175,7 +176,7 @@ fn node_config ( impl_version: "0.1", impl_commit: "", roles: role, - tasks_executor: Some(tasks_executor), + task_executor: Some(task_executor), transaction_pool: Default::default(), network: network_config, keystore: KeystoreConfig::Path { @@ -183,15 +184,14 @@ fn node_config ( password: None }, config_dir: Some(root.clone()), - database: DatabaseConfig::Path { + database: Some(DatabaseConfig::Path { path: root.join("db"), cache_size: None - }, + }), state_cache_size: 16777216, state_cache_child_ratio: None, pruning: Default::default(), - chain_spec: (*spec).clone(), - custom: Default::default(), + chain_spec: Some((*spec).clone()), name: format!("Node {}", index), wasm_method: sc_service::config::WasmExecutionMethod::Interpreted, execution_strategies: Default::default(), @@ -221,11 +221,11 @@ impl TestNet where fn new( temp: &TempDir, spec: ChainSpec, - full: impl Iterator) -> Result<(F, U), Error>>, - light: impl Iterator) -> Result>, + full: impl Iterator) -> Result<(F, U), Error>>, + light: impl Iterator) -> Result>, authorities: impl Iterator) -> Result<(F, U), Error> + impl FnOnce(Configuration) -> Result<(F, U), Error> )>, base_port: u16 ) -> TestNet { @@ -248,22 +248,22 @@ impl TestNet where fn insert_nodes( &mut self, temp: &TempDir, - full: impl Iterator) -> Result<(F, U), Error>>, - light: impl Iterator) -> Result>, - authorities: impl Iterator) -> Result<(F, U), Error>)> + full: impl Iterator) -> Result<(F, U), Error>>, + light: impl Iterator) -> Result>, + authorities: impl Iterator) -> Result<(F, U), Error>)> ) { let executor = self.runtime.executor(); for (key, authority) in authorities { - let tasks_executor = { + let task_executor = { let executor = executor.clone(); - Box::new(move |fut: Pin + Send>>| executor.spawn(fut.unit_error().compat())) + Arc::new(move |fut: Pin + Send>>| executor.spawn(fut.unit_error().compat())) }; let node_config = node_config( self.nodes, &self.chain_spec, Roles::AUTHORITY, - tasks_executor, + task_executor, Some(key), self.base_port, &temp, @@ -279,11 +279,11 @@ impl TestNet where } for full in full { - let tasks_executor = { + let task_executor = { let executor = executor.clone(); - Box::new(move |fut: Pin + Send>>| executor.spawn(fut.unit_error().compat())) + Arc::new(move |fut: Pin + Send>>| executor.spawn(fut.unit_error().compat())) }; - let node_config = node_config(self.nodes, &self.chain_spec, Roles::FULL, tasks_executor, None, self.base_port, &temp); + let node_config = node_config(self.nodes, &self.chain_spec, Roles::FULL, task_executor, None, self.base_port, &temp); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); let (service, user_data) = full(node_config).expect("Error creating test node service"); let service = SyncService::from(service); @@ -295,11 +295,11 @@ impl TestNet where } for light in light { - let tasks_executor = { + let task_executor = { let executor = executor.clone(); - Box::new(move |fut: Pin + Send>>| executor.spawn(fut.unit_error().compat())) + Arc::new(move |fut: Pin + Send>>| executor.spawn(fut.unit_error().compat())) }; - let node_config = node_config(self.nodes, &self.chain_spec, Roles::LIGHT, tasks_executor, None, self.base_port, &temp); + let node_config = node_config(self.nodes, &self.chain_spec, Roles::LIGHT, task_executor, None, self.base_port, &temp); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); let service = SyncService::from(light(node_config).expect("Error creating test node service")); @@ -321,9 +321,9 @@ pub fn connectivity( light_builder: Lb, ) where E: Clone, - Fb: Fn(Configuration<(), G, E>) -> Result, + Fb: Fn(Configuration) -> Result, F: AbstractService, - Lb: Fn(Configuration<(), G, E>) -> Result, + Lb: Fn(Configuration) -> Result, L: AbstractService, { const NUM_FULL_NODES: usize = 5; @@ -420,9 +420,9 @@ pub fn sync( mut make_block_and_import: B, mut extrinsic_factory: ExF ) where - Fb: Fn(Configuration<(), G, E>) -> Result<(F, U), Error>, + Fb: Fn(Configuration) -> Result<(F, U), Error>, F: AbstractService, - Lb: Fn(Configuration<(), G, E>) -> Result, + Lb: Fn(Configuration) -> Result, L: AbstractService, B: FnMut(&F, &mut U), ExF: FnMut(&F, &U) -> ::Extrinsic, @@ -489,9 +489,9 @@ pub fn consensus( light_builder: Lb, authorities: impl IntoIterator ) where - Fb: Fn(Configuration<(), G, E>) -> Result, + Fb: Fn(Configuration) -> Result, F: AbstractService, - Lb: Fn(Configuration<(), G, E>) -> Result, + Lb: Fn(Configuration) -> Result, L: AbstractService, E: Clone, { diff --git a/client/src/block_rules.rs b/client/src/block_rules.rs new file mode 100644 index 0000000000000000000000000000000000000000..e5614511817d35b5f2dbe4ebfb9501c5410553ec --- /dev/null +++ b/client/src/block_rules.rs @@ -0,0 +1,72 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Client fixed chain specification rules + +use std::collections::{HashMap, HashSet}; + +use sp_runtime::{ + traits::{Block as BlockT, NumberFor}, +}; + +use sc_client_api::{ForkBlocks, BadBlocks}; + +/// Chain specification rules lookup result. +pub enum LookupResult { + /// Specification rules do not contain any special rules about this block + NotSpecial, + /// The bock is known to be bad and should not be imported + KnownBad, + /// There is a specified canonical block hash for the given height + Expected(B::Hash) +} + +/// Chain-specific block filtering rules. +/// +/// This holds known bad blocks and known good forks, and +/// is usually part of the chain spec. +pub struct BlockRules { + bad: HashSet, + forks: HashMap, B::Hash>, +} + +impl BlockRules { + /// New block rules with provided black and white lists. + pub fn new( + fork_blocks: ForkBlocks, + bad_blocks: BadBlocks, + ) -> Self { + Self { + bad: bad_blocks.unwrap_or(HashSet::new()), + forks: fork_blocks.unwrap_or(vec![]).into_iter().collect(), + } + } + + /// Check if there's any rule affecting the given block. + pub fn lookup(&self, number: NumberFor, hash: &B::Hash) -> LookupResult { + if let Some(hash_for_height) = self.forks.get(&number) { + if hash_for_height != hash { + return LookupResult::Expected(hash_for_height.clone()); + } + } + + if self.bad.contains(hash) { + return LookupResult::KnownBad; + } + + LookupResult::NotSpecial + } +} diff --git a/client/src/call_executor.rs b/client/src/call_executor.rs index 6c685fc1b82c7c5ad55c27370e42ad624aaedf53..18ad5b113e983f98b94e384b5432357fe2e1d4e6 100644 --- a/client/src/call_executor.rs +++ b/client/src/call_executor.rs @@ -77,10 +77,14 @@ where extensions: Option, ) -> sp_blockchain::Result> { let mut changes = OverlayedChanges::default(); + let changes_trie = backend::changes_tries_state_at_block( + id, self.backend.changes_trie_storage() + )?; + // make sure to destroy state before exiting this function let state = self.backend.state_at(*id)?; let return_data = StateMachine::new( &state, - backend::changes_tries_state_at_block(id, self.backend.changes_trie_storage())?, + changes_trie, &mut changes, &self.executor, method, @@ -89,12 +93,12 @@ where ).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( strategy.get_manager(), None, - )?; + ); { let _lock = self.backend.get_import_lock().read(); self.backend.destroy_state(state)?; } - Ok(return_data.into_encoded()) + Ok(return_data?.into_encoded()) } fn contextual_call< @@ -131,37 +135,36 @@ where _ => {}, } - let mut state = self.backend.state_at(*at)?; let changes_trie_state = backend::changes_tries_state_at_block(at, self.backend.changes_trie_storage())?; - let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut()); + // make sure to destroy state before exiting this function + let mut state = self.backend.state_at(*at)?; let result = match recorder { - Some(recorder) => { - let trie_state = state.as_trie_backend() - .ok_or_else(|| - Box::new(sp_state_machine::ExecutionError::UnableToGenerateProof) - as Box - )?; - - let backend = sp_state_machine::ProvingBackend::new_with_recorder( - trie_state, - recorder.clone(), - ); - - StateMachine::new( - &backend, - changes_trie_state, - &mut *changes.borrow_mut(), - &self.executor, - method, - call_data, - extensions.unwrap_or_default(), + Some(recorder) => state.as_trie_backend() + .ok_or_else(|| + Box::new(sp_state_machine::ExecutionError::UnableToGenerateProof) + as Box ) - // TODO: https://github.com/paritytech/substrate/issues/4455 - // .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c)) - .execute_using_consensus_failure_handler(execution_manager, native_call) - } + .and_then(|trie_state| { + let backend = sp_state_machine::ProvingBackend::new_with_recorder( + trie_state, + recorder.clone(), + ); + + StateMachine::new( + &backend, + changes_trie_state, + &mut *changes.borrow_mut(), + &self.executor, + method, + call_data, + extensions.unwrap_or_default(), + ) + // TODO: https://github.com/paritytech/substrate/issues/4455 + // .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c)) + .execute_using_consensus_failure_handler(execution_manager, native_call) + }), None => StateMachine::new( &state, changes_trie_state, @@ -173,18 +176,19 @@ where ) .with_storage_transaction_cache(storage_transaction_cache.as_mut().map(|c| &mut **c)) .execute_using_consensus_failure_handler(execution_manager, native_call) - }?; + }; { let _lock = self.backend.get_import_lock().read(); self.backend.destroy_state(state)?; } - Ok(result) + result.map_err(Into::into) } fn runtime_version(&self, id: &BlockId) -> sp_blockchain::Result { let mut overlay = OverlayedChanges::default(); - let state = self.backend.state_at(*id)?; let changes_trie_state = backend::changes_tries_state_at_block(id, self.backend.changes_trie_storage())?; + // make sure to destroy state before exiting this function + let state = self.backend.state_at(*id)?; let mut cache = StorageTransactionCache::::default(); let mut ext = Ext::new( &mut overlay, diff --git a/client/src/cht.rs b/client/src/cht.rs index 29f19a77504b90fc5344cf12ec605e8dee267a0c..1435b77ec592e21d1a05be901662653dc6fcfbc1 100644 --- a/client/src/cht.rs +++ b/client/src/cht.rs @@ -28,7 +28,7 @@ use codec::Encode; use sp_trie; use sp_core::{H256, convert_hash}; -use sp_runtime::traits::{Header as HeaderT, SimpleArithmetic, Zero, One}; +use sp_runtime::traits::{Header as HeaderT, AtLeast32Bit, Zero, One}; use sp_state_machine::{ MemoryDB, TrieBackend, Backend as StateBackend, StorageProof, InMemoryBackend, prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend @@ -48,7 +48,7 @@ pub fn size>() -> N { /// Returns Some(cht_number) if CHT is need to be built when the block with given number is canonized. pub fn is_build_required(cht_size: N, block_num: N) -> Option where - N: Clone + SimpleArithmetic, + N: Clone + AtLeast32Bit, { let block_cht_num = block_to_cht_number(cht_size.clone(), block_num.clone())?; let two = N::one() + N::one(); @@ -66,7 +66,7 @@ pub fn is_build_required(cht_size: N, block_num: N) -> Option /// Returns Some(max_cht_number) if CHT has ever been built given maximal canonical block number. pub fn max_cht_number(cht_size: N, max_canonical_block: N) -> Option where - N: Clone + SimpleArithmetic, + N: Clone + AtLeast32Bit, { let max_cht_number = block_to_cht_number(cht_size, max_canonical_block)?; let two = N::one() + N::one(); @@ -291,18 +291,18 @@ fn build_pairs( /// More generally: CHT N includes block (1 + N*SIZE)...((N+1)*SIZE). /// This is because the genesis hash is assumed to be known /// and including it would be redundant. -pub fn start_number(cht_size: N, cht_num: N) -> N { +pub fn start_number(cht_size: N, cht_num: N) -> N { (cht_num * cht_size) + N::one() } /// Get the ending block of a given CHT. -pub fn end_number(cht_size: N, cht_num: N) -> N { +pub fn end_number(cht_size: N, cht_num: N) -> N { (cht_num + N::one()) * cht_size } /// Convert a block number to a CHT number. /// Returns `None` for `block_num` == 0, `Some` otherwise. -pub fn block_to_cht_number(cht_size: N, block_num: N) -> Option { +pub fn block_to_cht_number(cht_size: N, block_num: N) -> Option { if block_num == N::zero() { None } else { diff --git a/client/src/client.rs b/client/src/client.rs index c74a005c6fea617e00370f1eb0b8ac7020ee6edb..d085b92025fdfbe3e4292cdf2a20aa7c6ea2b9aa 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -82,7 +82,7 @@ use sp_blockchain::Error; use crate::{ call_executor::LocalCallExecutor, light::{call_executor::prove_execution, fetcher::ChangesProof}, - in_mem, genesis, cht, + in_mem, genesis, cht, block_rules::{BlockRules, LookupResult as BlockLookupResult}, }; /// Substrate Client @@ -94,12 +94,51 @@ pub struct Client where Block: BlockT { finality_notification_sinks: Mutex>>>, // holds the block hash currently being imported. TODO: replace this with block queue importing_block: RwLock>, - fork_blocks: ForkBlocks, - bad_blocks: BadBlocks, + block_rules: BlockRules, execution_extensions: ExecutionExtensions, _phantom: PhantomData, } +/// An `Iterator` that iterates keys in a given block under a prefix. +pub struct KeyIterator<'a, State, Block> { + state: State, + prefix: Option<&'a StorageKey>, + current_key: Vec, + _phantom: PhantomData, +} + +impl <'a, State, Block> KeyIterator<'a, State, Block> { + fn new(state: State, prefix: Option<&'a StorageKey>, current_key: Vec) -> Self { + Self { + state, + prefix, + current_key, + _phantom: PhantomData, + } + } +} + +impl<'a, State, Block> Iterator for KeyIterator<'a, State, Block> where + Block: BlockT, + State: StateBackend>, +{ + type Item = StorageKey; + + fn next(&mut self) -> Option { + let next_key = self.state + .next_storage_key(&self.current_key) + .ok() + .flatten()?; + if let Some(prefix) = self.prefix { + if !next_key.starts_with(&prefix.0[..]) { + return None; + } + } + self.current_key = next_key.clone(); + Some(StorageKey(next_key)) + } +} + // used in importing a block, where additional changes are made after the runtime // executed. enum PrePostHeader { @@ -219,8 +258,7 @@ impl Client where import_notification_sinks: Default::default(), finality_notification_sinks: Default::default(), importing_block: Default::default(), - fork_blocks, - bad_blocks, + block_rules: BlockRules::new(fork_blocks, bad_blocks), execution_extensions, _phantom: Default::default(), }) @@ -236,14 +274,47 @@ impl Client where self.backend.state_at(*block) } - /// Given a `BlockId` and a key prefix, return the matching child storage keys in that block. + /// Given a `BlockId` and a key prefix, return the matching storage keys in that block. pub fn storage_keys(&self, id: &BlockId, key_prefix: &StorageKey) -> sp_blockchain::Result> { let keys = self.state_at(id)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect(); Ok(keys) } + /// Given a `BlockId` and a key prefix, return the matching child storage keys and values in that block. + pub fn storage_pairs(&self, id: &BlockId, key_prefix: &StorageKey) + -> sp_blockchain::Result> + { + let state = self.state_at(id)?; + let keys = state + .keys(&key_prefix.0) + .into_iter() + .map(|k| { + let d = state.storage(&k).ok().flatten().unwrap_or_default(); + (StorageKey(k), StorageData(d)) + }) + .collect(); + Ok(keys) + } + + /// Given a `BlockId` and a key prefix, return a `KeyIterator` iterates matching storage keys in that block. + pub fn storage_keys_iter<'a>( + &self, + id: &BlockId, + prefix: Option<&'a StorageKey>, + start_key: Option<&StorageKey> + ) -> sp_blockchain::Result> { + let state = self.state_at(id)?; + let start_key = start_key + .or(prefix) + .map(|key| key.0.clone()) + .unwrap_or_else(Vec::new); + Ok(KeyIterator::new(state, prefix, start_key)) + } + /// Given a `BlockId` and a key, return the value under the key in that block. - pub fn storage(&self, id: &BlockId, key: &StorageKey) -> sp_blockchain::Result> { + pub fn storage(&self, id: &BlockId, key: &StorageKey) + -> sp_blockchain::Result> + { Ok(self.state_at(id)? .storage(&key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? .map(StorageData) @@ -252,7 +323,8 @@ impl Client where /// Given a `BlockId` and a key, return the value under the hash in that block. pub fn storage_hash(&self, id: &BlockId, key: &StorageKey) - -> sp_blockchain::Result> { + -> sp_blockchain::Result> + { Ok(self.state_at(id)? .storage_hash(&key.0).map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))? ) @@ -818,12 +890,19 @@ impl Client where finalized, auxiliary, fork_choice, + intermediates, import_existing, .. } = import_block; assert!(justification.is_some() && finalized || justification.is_none()); + if !intermediates.is_empty() { + return Err(Error::IncompletePipeline) + } + + let fork_choice = fork_choice.ok_or(Error::IncompletePipeline)?; + let import_headers = if post_digests.is_empty() { PrePostHeader::Same(header) } else { @@ -1520,6 +1599,9 @@ impl sp_consensus::BlockImport for &Client>, new_cache: HashMap>, ) -> Result { + let span = tracing::span!(tracing::Level::DEBUG, "import_block"); + let _enter = span.enter(); + if let Some(res) = self.prepare_block_storage_changes(&mut import_block).map_err(|e| { warn!("Block prepare storage changes error:\n{:?}", e); ConsensusError::ClientImport(e.to_string()) @@ -1544,32 +1626,25 @@ impl sp_consensus::BlockImport for &Client { + trace!( + "Rejecting known bad block: #{} {:?}", + number, + hash, + ); + return Ok(ImportResult::KnownBad); + }, + BlockLookupResult::Expected(expected_hash) => { trace!( "Rejecting block from known invalid fork. Got {:?}, expected: {:?} at height {}", hash, - h, + expected_hash, number ); return Ok(ImportResult::KnownBad); - } - } - - let bad_block = self.bad_blocks.as_ref() - .filter(|bs| bs.contains(&hash)) - .is_some(); - - if bad_block { - trace!( - "Rejecting known bad block: #{} {:?}", - number, - hash, - ); - return Ok(ImportResult::KnownBad); + }, + BlockLookupResult::NotSpecial => {} } // Own status must be checked first. If the block and ancestry is pruned @@ -1902,6 +1977,7 @@ pub(crate) mod tests { sc_client_db::{Backend, DatabaseSettings, DatabaseSettingsSrc, PruningMode}, runtime::{self, Block, Transfer, RuntimeApi, TestAPI}, }; + use hex_literal::hex; /// Returns tuple, consisting of: /// 1) test client pre-filled with blocks changing balances; @@ -3002,6 +3078,107 @@ pub(crate) mod tests { ); } + + #[test] + fn respects_block_rules() { + + fn run_test( + record_only: bool, + known_bad: &mut HashSet, + fork_rules: &mut Vec<(u64, H256)>, + ) { + let mut client = if record_only { + TestClientBuilder::new().build() + } else { + TestClientBuilder::new() + .set_block_rules( + Some(fork_rules.clone()), + Some(known_bad.clone()), + ) + .build() + }; + + let block_ok = client.new_block_at(&BlockId::Number(0), Default::default(), false) + .unwrap().build().unwrap().block; + + let params = BlockCheckParams { + hash: block_ok.hash().clone(), + number: 0, + parent_hash: block_ok.header().parent_hash().clone(), + allow_missing_state: false, + import_existing: false, + }; + assert_eq!(client.check_block(params).unwrap(), ImportResult::imported(false)); + + // this is 0x0d6d6612a10485370d9e085aeea7ec427fb3f34d961c6a816cdbe5cde2278864 + let mut block_not_ok = client.new_block_at(&BlockId::Number(0), Default::default(), false) + .unwrap(); + block_not_ok.push_storage_change(vec![0], Some(vec![1])).unwrap(); + let block_not_ok = block_not_ok.build().unwrap().block; + + let params = BlockCheckParams { + hash: block_not_ok.hash().clone(), + number: 0, + parent_hash: block_not_ok.header().parent_hash().clone(), + allow_missing_state: false, + import_existing: false, + }; + if record_only { + known_bad.insert(block_not_ok.hash()); + } else { + assert_eq!(client.check_block(params).unwrap(), ImportResult::KnownBad); + } + + // Now going to the fork + client.import_as_final(BlockOrigin::Own, block_ok).unwrap(); + + // And check good fork + let mut block_ok = client.new_block_at(&BlockId::Number(1), Default::default(), false) + .unwrap(); + block_ok.push_storage_change(vec![0], Some(vec![2])).unwrap(); + let block_ok = block_ok.build().unwrap().block; + + let params = BlockCheckParams { + hash: block_ok.hash().clone(), + number: 1, + parent_hash: block_ok.header().parent_hash().clone(), + allow_missing_state: false, + import_existing: false, + }; + if record_only { + fork_rules.push((1, block_ok.hash().clone())); + } + assert_eq!(client.check_block(params).unwrap(), ImportResult::imported(false)); + + // And now try bad fork + let mut block_not_ok = client.new_block_at(&BlockId::Number(1), Default::default(), false) + .unwrap(); + block_not_ok.push_storage_change(vec![0], Some(vec![3])).unwrap(); + let block_not_ok = block_not_ok.build().unwrap().block; + + let params = BlockCheckParams { + hash: block_not_ok.hash().clone(), + number: 1, + parent_hash: block_not_ok.header().parent_hash().clone(), + allow_missing_state: false, + import_existing: false, + }; + + if !record_only { + assert_eq!(client.check_block(params).unwrap(), ImportResult::KnownBad); + } + } + + let mut known_bad = HashSet::new(); + let mut fork_rules = Vec::new(); + + // records what bad_blocks and fork_blocks hashes should be + run_test(true, &mut known_bad, &mut fork_rules); + + // enforces rules and actually makes assertions + run_test(false, &mut known_bad, &mut fork_rules); + } + #[test] fn returns_status_for_pruned_blocks() { let _ = env_logger::try_init(); @@ -3204,4 +3381,61 @@ pub(crate) mod tests { vec![(30, 0), (27, 0), (25, 0), (24, 0), (11, 0)] ); } + + #[test] + fn storage_keys_iter_prefix_and_start_key_works() { + let client = substrate_test_runtime_client::new(); + + let prefix = StorageKey(hex!("3a").to_vec()); + + let res: Vec<_> = client.storage_keys_iter(&BlockId::Number(0), Some(&prefix), None) + .unwrap() + .map(|x| x.0) + .collect(); + assert_eq!(res, [hex!("3a636f6465").to_vec(), hex!("3a686561707061676573").to_vec()]); + + let res: Vec<_> = client.storage_keys_iter(&BlockId::Number(0), Some(&prefix), Some(&StorageKey(hex!("3a636f6465").to_vec()))) + .unwrap() + .map(|x| x.0) + .collect(); + assert_eq!(res, [hex!("3a686561707061676573").to_vec()]); + + let res: Vec<_> = client.storage_keys_iter(&BlockId::Number(0), Some(&prefix), Some(&StorageKey(hex!("3a686561707061676573").to_vec()))) + .unwrap() + .map(|x| x.0) + .collect(); + assert_eq!(res, Vec::>::new()); + } + + #[test] + fn storage_keys_iter_works() { + let client = substrate_test_runtime_client::new(); + + let prefix = StorageKey(hex!("").to_vec()); + + let res: Vec<_> = client.storage_keys_iter(&BlockId::Number(0), Some(&prefix), None) + .unwrap() + .take(2) + .map(|x| x.0) + .collect(); + assert_eq!(res, [hex!("0befda6e1ca4ef40219d588a727f1271").to_vec(), hex!("3a636f6465").to_vec()]); + + let res: Vec<_> = client.storage_keys_iter(&BlockId::Number(0), Some(&prefix), Some(&StorageKey(hex!("3a636f6465").to_vec()))) + .unwrap() + .take(3) + .map(|x| x.0) + .collect(); + assert_eq!(res, [ + hex!("3a686561707061676573").to_vec(), + hex!("6644b9b8bc315888ac8e41a7968dc2b4141a5403c58acdf70b7e8f7e07bf5081").to_vec(), + hex!("79c07e2b1d2e2abfd4855b936617eeff5e0621c4869aa60c02be9adcc98a0d1d").to_vec(), + ]); + + let res: Vec<_> = client.storage_keys_iter(&BlockId::Number(0), Some(&prefix), Some(&StorageKey(hex!("79c07e2b1d2e2abfd4855b936617eeff5e0621c4869aa60c02be9adcc98a0d1d").to_vec()))) + .unwrap() + .take(1) + .map(|x| x.0) + .collect(); + assert_eq!(res, [hex!("cf722c0832b5231d35e29f319ff27389f5032bfc7bfc3ba5ed7839f2042fb99f").to_vec()]); + } } diff --git a/client/src/leaves.rs b/client/src/leaves.rs index bb556da83a84a2f1bfb6a3f7b0ce2491c5926bc8..1082e6ca071eac8463f0c95163e4e8a86b1a6cb3 100644 --- a/client/src/leaves.rs +++ b/client/src/leaves.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use std::cmp::Reverse; use kvdb::{KeyValueDB, DBTransaction}; -use sp_runtime::traits::SimpleArithmetic; +use sp_runtime::traits::AtLeast32Bit; use codec::{Encode, Decode}; use sp_blockchain::{Error, Result}; @@ -65,7 +65,7 @@ pub struct LeafSet { impl LeafSet where H: Clone + PartialEq + Decode + Encode, - N: std::fmt::Debug + Clone + SimpleArithmetic + Decode + Encode, + N: std::fmt::Debug + Clone + AtLeast32Bit + Decode + Encode, { /// Construct a new, blank leaf set. pub fn new() -> Self { @@ -251,7 +251,7 @@ pub struct Undo<'a, H: 'a, N: 'a> { impl<'a, H: 'a, N: 'a> Undo<'a, H, N> where H: Clone + PartialEq + Decode + Encode, - N: std::fmt::Debug + Clone + SimpleArithmetic + Decode + Encode, + N: std::fmt::Debug + Clone + AtLeast32Bit + Decode + Encode, { /// Undo an imported block by providing the displaced leaf. pub fn undo_import(&mut self, displaced: ImportDisplaced) { diff --git a/client/src/lib.rs b/client/src/lib.rs index 8aa71c5ec5c8876f6e5d1cc6df21e9bc45ba9827..d97246d478c2b6df0eb13128c4ceba23d9ccd6b0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -83,6 +83,7 @@ pub mod light; pub mod leaves; mod call_executor; mod client; +mod block_rules; pub use sc_client_api::{ blockchain, @@ -103,4 +104,4 @@ pub use crate::{ }, leaves::LeafSet, }; -pub use sp_state_machine::{ExecutionStrategy, StorageProof}; +pub use sp_state_machine::{ExecutionStrategy, StorageProof, StateMachine}; diff --git a/client/src/light/fetcher.rs b/client/src/light/fetcher.rs index d66108b7f0adba6a9d414d3fc7d7dc9ff5f84bbb..9df6a38630684f8f545f2a4f1175fa3b21d386f6 100644 --- a/client/src/light/fetcher.rs +++ b/client/src/light/fetcher.rs @@ -25,7 +25,7 @@ use codec::{Decode, Encode}; use sp_core::{convert_hash, traits::CodeExecutor}; use sp_runtime::traits::{ Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor, - SimpleArithmetic, CheckedConversion, + AtLeast32Bit, CheckedConversion, }; use sp_state_machine::{ ChangesTrieRootsStorage, ChangesTrieAnchorBlockId, ChangesTrieConfigurationRange, @@ -286,7 +286,7 @@ impl FetchChecker for LightDataChecker } /// A view of BTreeMap as a changes trie roots storage. -struct RootsStorage<'a, Number: SimpleArithmetic, Hash: 'a> { +struct RootsStorage<'a, Number: AtLeast32Bit, Hash: 'a> { roots: (Number, &'a [Hash]), prev_roots: &'a BTreeMap, } @@ -294,7 +294,7 @@ struct RootsStorage<'a, Number: SimpleArithmetic, Hash: 'a> { impl<'a, H, Number, Hash> ChangesTrieRootsStorage for RootsStorage<'a, Number, Hash> where H: Hasher, - Number: ::std::fmt::Display + ::std::hash::Hash + Clone + SimpleArithmetic + Encode + Decode + Send + Sync + 'static, + Number: ::std::fmt::Display + ::std::hash::Hash + Clone + AtLeast32Bit + Encode + Decode + Send + Sync + 'static, Hash: 'a + Send + Sync + Clone + AsRef<[u8]>, { fn build_anchor( diff --git a/client/state-db/Cargo.toml b/client/state-db/Cargo.toml index 9e162c5cb989872329fc60ce9c36a97ad93113c7..1bfa13693780a5e549d8d1fa82c5436ab54e4c98 100644 --- a/client/state-db/Cargo.toml +++ b/client/state-db/Cargo.toml @@ -3,9 +3,10 @@ name = "sc-state-db" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] -parking_lot = "0.9.0" +parking_lot = "0.10.0" log = "0.4.8" sp-core = { version = "2.0.0", path = "../../primitives/core" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } diff --git a/client/telemetry/Cargo.toml b/client/telemetry/Cargo.toml index c536b7599b59b5325844f16b10dc2122bb415c5c..f75c6dd44eeb1a67206394b211937ae18a9e53b9 100644 --- a/client/telemetry/Cargo.toml +++ b/client/telemetry/Cargo.toml @@ -4,13 +4,15 @@ version = "2.0.0" authors = ["Parity Technologies "] description = "Telemetry utils" edition = "2018" +license = "GPL-3.0" [dependencies] bytes = "0.5" -parking_lot = "0.9.0" +parking_lot = "0.10.0" futures = "0.3.1" -futures-timer = "2.0.0" -libp2p = { version = "0.14.0-alpha.1", default-features = false, features = ["libp2p-websocket"] } +futures-timer = "3.0.1" +wasm-timer = "0.2.0" +libp2p = { version = "0.15.0", default-features = false, features = ["libp2p-websocket"] } log = "0.4.8" pin-project = "0.4.6" rand = "0.7.2" diff --git a/client/telemetry/src/lib.rs b/client/telemetry/src/lib.rs index 906df545e2377484d8d116cfbf3abacc3b0c2806..1c6f1425d6647a101f96d06745d06a5ecab2425e 100644 --- a/client/telemetry/src/lib.rs +++ b/client/telemetry/src/lib.rs @@ -63,7 +63,8 @@ use libp2p::{Multiaddr, wasm_ext}; use log::{error, warn}; use parking_lot::Mutex; use serde::{Serialize, Deserialize}; -use std::{pin::Pin, sync::Arc, task::{Context, Poll}, time::{Duration, Instant}}; +use std::{pin::Pin, sync::Arc, task::{Context, Poll}, time::Duration}; +use wasm_timer::Instant; pub use libp2p::wasm_ext::ExtTransport; pub use slog_scope::with_logger; diff --git a/client/telemetry/src/worker.rs b/client/telemetry/src/worker.rs index 8f43bb612a10503d0d8968821bc90f1469140f85..ef1f9fa067156e82f9ad85dd4f00d65613fa4ddb 100644 --- a/client/telemetry/src/worker.rs +++ b/client/telemetry/src/worker.rs @@ -98,10 +98,10 @@ impl TelemetryWorker { .map_ok(|data| BytesMut::from(data.as_ref())); future::ready(Ok::<_, io::Error>(connec)) }) - }); + }) + .timeout(CONNECT_TIMEOUT); let transport = transport - .timeout(CONNECT_TIMEOUT) .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) .map(|out, _| { let out = out diff --git a/client/tracing/Cargo.toml b/client/tracing/Cargo.toml index ed9b52fc26e20adad3a689c227179557544c8c62..0b6b02acd524a6b1db105f0b4710dddd4759ac8b 100644 --- a/client/tracing/Cargo.toml +++ b/client/tracing/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] erased-serde = "0.3.9" log = { version = "0.4.8" } -parking_lot = "0.9.0" +parking_lot = "0.10.0" serde = "1.0.101" serde_json = "1.0.41" slog = { version = "2.5.2", features = ["nested-values"] } diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index 701826307129d44c1c40e2643744892ef2712558..27aec1b92e104e77b7d4f3bba281080767fa4b04 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -3,13 +3,16 @@ name = "sc-transaction-pool" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } derive_more = "0.99.2" futures = { version = "0.3.1", features = ["compat"] } +futures-diagnose = "1.0" log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" +wasm-timer = "0.2" sp-core = { version = "2.0.0", path = "../../primitives/core" } sp-api = { version = "2.0.0", path = "../../primitives/api" } sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } @@ -17,7 +20,9 @@ sc-transaction-graph = { version = "2.0.0", path = "./graph" } sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } sc-client-api = { version = "2.0.0", path = "../api" } sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [dev-dependencies] sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +substrate-test-runtime-transaction-pool = { version = "2.0.0", path = "../../test-utils/runtime/transaction-pool" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/client/transaction-pool/graph/Cargo.toml b/client/transaction-pool/graph/Cargo.toml index c5897e84fe50deb44d36f0d9328a4048374116ef..3bbe46bc50427a31696b498c81d3ff3b0dd6bd5f 100644 --- a/client/transaction-pool/graph/Cargo.toml +++ b/client/transaction-pool/graph/Cargo.toml @@ -3,16 +3,19 @@ name = "sc-transaction-graph" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] derive_more = "0.99.2" futures = "0.3.1" log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" serde = { version = "1.0.101", features = ["derive"] } +wasm-timer = "0.2" sp-core = { version = "2.0.0", path = "../../../primitives/core" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [dev-dependencies] assert_matches = "1.3.0" diff --git a/client/transaction-pool/graph/benches/basics.rs b/client/transaction-pool/graph/benches/basics.rs index 557a2ca3d1f8558d8bc6f84d8b1f9f14b8f9f5ba..f3f67f1446f5cea37904f82382544a83062c6630 100644 --- a/client/transaction-pool/graph/benches/basics.rs +++ b/client/transaction-pool/graph/benches/basics.rs @@ -16,7 +16,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use futures::executor::block_on; +use futures::{future::{ready, Ready}, executor::block_on}; use sc_transaction_graph::*; use sp_runtime::transaction_validity::{ValidTransaction, InvalidTransaction}; use codec::Encode; @@ -49,7 +49,8 @@ impl ChainApi for TestApi { type Block = Block; type Hash = H256; type Error = sp_transaction_pool::error::Error; - type ValidationFuture = futures::future::Ready>; + type ValidationFuture = Ready>; + type BodyFuture = Ready>>>; fn validate_transaction( &self, @@ -61,14 +62,14 @@ impl ChainApi for TestApi { match self.block_id_to_number(at) { Ok(Some(num)) if num > 5 => { - return futures::future::ready( + return ready( Ok(Err(InvalidTransaction::Stale.into())) ) }, _ => {}, } - futures::future::ready( + ready( Ok(Ok(ValidTransaction { priority: 4, requires: if nonce > 1 && self.nonce_dependant { @@ -105,6 +106,10 @@ impl ChainApi for TestApi { let encoded = uxt.encode(); (blake2_256(&encoded).into(), encoded.len()) } + + fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { + ready(Ok(None)) + } } fn uxt(transfer: Transfer) -> Extrinsic { @@ -148,15 +153,15 @@ fn bench_configured(pool: Pool, number: u64) { fn benchmark_main(c: &mut Criterion) { - c.bench_function("sequential 50 tx", |b| { + c.bench_function("sequential 50 tx", |b| { b.iter(|| { - bench_configured(Pool::new(Default::default(), TestApi::new_dependant()), 50); + bench_configured(Pool::new(Default::default(), TestApi::new_dependant().into()), 50); }); }); c.bench_function("random 100 tx", |b| { b.iter(|| { - bench_configured(Pool::new(Default::default(), TestApi::default()), 100); + bench_configured(Pool::new(Default::default(), TestApi::default().into()), 100); }); }); } diff --git a/client/transaction-pool/graph/src/base_pool.rs b/client/transaction-pool/graph/src/base_pool.rs index 1da731b71e6d51cdc067fc0ca281536302d4bb13..8a33d8244eb00eae54e7347871d12ca2c1c7a165 100644 --- a/client/transaction-pool/graph/src/base_pool.rs +++ b/client/transaction-pool/graph/src/base_pool.rs @@ -84,7 +84,7 @@ pub struct PruneStatus { /// Immutable transaction #[cfg_attr(test, derive(Clone))] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, parity_util_mem::MallocSizeOf)] pub struct Transaction { /// Raw extrinsic representing that transaction. pub data: Extrinsic, @@ -210,6 +210,7 @@ const RECENTLY_PRUNED_TAGS: usize = 2; /// Most likely it is required to revalidate them and recompute set of /// required tags. #[derive(Debug)] +#[cfg_attr(not(target_os = "unknown"), derive(parity_util_mem::MallocSizeOf))] pub struct BasePool { reject_future_transactions: bool, future: FutureTransactions, @@ -374,9 +375,9 @@ impl BasePool Vec>>> { - let ready = self.ready.by_hash(hashes); - let future = self.future.by_hash(hashes); + pub fn by_hashes(&self, hashes: &[Hash]) -> Vec>>> { + let ready = self.ready.by_hashes(hashes); + let future = self.future.by_hashes(hashes); ready .into_iter() @@ -385,6 +386,11 @@ impl BasePool Option>> { + self.ready.by_hash(hash) + } + /// Makes sure that the transactions in the queues stay within provided limits. /// /// Removes and returns worst transactions from the queues and all transactions that depend on them. @@ -841,6 +847,33 @@ mod tests { } } + #[test] + fn can_track_heap_size() { + let mut pool = pool(); + pool.import(Transaction { + data: vec![5u8; 1024], + bytes: 1, + hash: 5, + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0], vec![4]], + propagate: true, + }).expect("import 1 should be ok"); + pool.import(Transaction { + data: vec![3u8; 1024], + bytes: 1, + hash: 7, + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![2], vec![7]], + propagate: true, + }).expect("import 2 should be ok"); + + assert!(parity_util_mem::malloc_size(&pool) > 5000); + } + #[test] fn should_remove_invalid_transactions() { // given diff --git a/client/transaction-pool/graph/src/future.rs b/client/transaction-pool/graph/src/future.rs index d106c65d45ab413d853be13eed32efedf9d00e53..a84a5fbe689c37aacbba24ed1e6a8c6885961589 100644 --- a/client/transaction-pool/graph/src/future.rs +++ b/client/transaction-pool/graph/src/future.rs @@ -19,16 +19,17 @@ use std::{ fmt, hash, sync::Arc, - time, }; use sp_core::hexdisplay::HexDisplay; use sp_runtime::transaction_validity::{ TransactionTag as Tag, }; +use wasm_timer::Instant; use crate::base_pool::Transaction; +#[cfg_attr(not(target_os = "unknown"), derive(parity_util_mem::MallocSizeOf))] /// Transaction with partially satisfied dependencies. pub struct WaitingTransaction { /// Transaction details. @@ -36,7 +37,7 @@ pub struct WaitingTransaction { /// Tags that are required and have not been satisfied yet by other transactions in the pool. pub missing_tags: HashSet, /// Time of import to the Future Queue. - pub imported_at: time::Instant, + pub imported_at: Instant, } impl fmt::Debug for WaitingTransaction { @@ -90,7 +91,7 @@ impl WaitingTransaction { WaitingTransaction { transaction: Arc::new(transaction), missing_tags, - imported_at: time::Instant::now(), + imported_at: Instant::now(), } } @@ -110,6 +111,7 @@ impl WaitingTransaction { /// Contains transactions that are still awaiting for some other transactions that /// could provide a tag that they require. #[derive(Debug)] +#[cfg_attr(not(target_os = "unknown"), derive(parity_util_mem::MallocSizeOf))] pub struct FutureTransactions { /// tags that are not yet provided by any transaction and we await for them wanted_tags: HashMap>, @@ -160,7 +162,7 @@ impl FutureTransactions { } /// Returns a list of known transactions - pub fn by_hash(&self, hashes: &[Hash]) -> Vec>>> { + pub fn by_hashes(&self, hashes: &[Hash]) -> Vec>>> { hashes.iter().map(|h| self.waiting.get(h).map(|x| x.transaction.clone())).collect() } @@ -243,3 +245,30 @@ impl FutureTransactions { self.waiting.values().fold(0, |acc, tx| acc + tx.transaction.bytes) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_track_heap_size() { + let mut future = FutureTransactions::default(); + future.import(WaitingTransaction { + transaction: Transaction { + data: vec![0u8; 1024], + bytes: 1, + hash: 1, + priority: 1, + valid_till: 2, + requires: vec![vec![1], vec![2]], + provides: vec![vec![3], vec![4]], + propagate: true, + }.into(), + missing_tags: vec![vec![1u8], vec![2u8]].into_iter().collect(), + imported_at: std::time::Instant::now(), + }); + + // data is at least 1024! + assert!(parity_util_mem::malloc_size(&future) > 1024); + } +} diff --git a/client/transaction-pool/graph/src/pool.rs b/client/transaction-pool/graph/src/pool.rs index 629bd0a9a93a8c055a166bec8c2bb11ea9bf2ebe..edcd211df9f36ea88a39741683e19b4e17fdf51d 100644 --- a/client/transaction-pool/graph/src/pool.rs +++ b/client/transaction-pool/graph/src/pool.rs @@ -27,7 +27,6 @@ use serde::Serialize; use futures::{ Future, FutureExt, channel::mpsc, - future::{Either, ready, join_all}, }; use sp_runtime::{ generic::BlockId, @@ -35,11 +34,12 @@ use sp_runtime::{ transaction_validity::{TransactionValidity, TransactionTag as Tag, TransactionValidityError}, }; use sp_transaction_pool::{error, PoolStatus}; +use wasm_timer::Instant; use crate::validated_pool::{ValidatedPool, ValidatedTransaction}; /// Modification notification event stream type; -pub type EventStream = mpsc::UnboundedReceiver<()>; +pub type EventStream = mpsc::UnboundedReceiver; /// Extrinsic hash type for a pool. pub type ExHash = ::Hash; @@ -68,6 +68,8 @@ pub trait ChainApi: Send + Sync { type Error: From + error::IntoPoolError; /// Validate transaction future. type ValidationFuture: Future> + Send + Unpin; + /// Body future (since block body might be remote) + type BodyFuture: Future::Extrinsic>>, Self::Error>> + Unpin + Send + 'static; /// Verify extrinsic at given block. fn validate_transaction( @@ -84,6 +86,9 @@ pub trait ChainApi: Send + Sync { /// Returns hash and encoding length of the extrinsic. fn hash_and_length(&self, uxt: &ExtrinsicFor) -> (Self::Hash, usize); + + /// Returns a block body given the block id. + fn block_body(&self, at: &BlockId) -> Self::BodyFuture; } /// Pool configuration options. @@ -101,11 +106,11 @@ impl Default for Options { fn default() -> Self { Options { ready: base::Limit { - count: 512, - total_bytes: 10 * 1024 * 1024, + count: 8192, + total_bytes: 20 * 1024 * 1024, }, future: base::Limit { - count: 128, + count: 512, total_bytes: 1 * 1024 * 1024, }, reject_future_transactions: false, @@ -118,17 +123,28 @@ pub struct Pool { validated_pool: Arc>, } +#[cfg(not(target_os = "unknown"))] +impl parity_util_mem::MallocSizeOf for Pool +where + B::Hash: parity_util_mem::MallocSizeOf, + ExtrinsicFor: parity_util_mem::MallocSizeOf, +{ + fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + self.validated_pool.size_of(ops) + } +} + impl Pool { /// Create a new transaction pool. - pub fn new(options: Options, api: B) -> Self { + pub fn new(options: Options, api: Arc) -> Self { Pool { validated_pool: Arc::new(ValidatedPool::new(options, api)), } } /// Imports a bunch of unverified extrinsics to the pool - pub fn submit_at(&self, at: &BlockId, xts: T, force: bool) - -> impl Future, B::Error>>, B::Error>> + pub async fn submit_at(&self, at: &BlockId, xts: T, force: bool) + -> Result, B::Error>>, B::Error> where T: IntoIterator> { @@ -138,49 +154,43 @@ impl Pool { .map(|validated_transactions| validated_pool.submit(validated_transactions .into_iter() .map(|(_, tx)| tx)))) + .await } /// Imports one unverified extrinsic to the pool - pub fn submit_one( + pub async fn submit_one( &self, at: &BlockId, xt: ExtrinsicFor, - ) -> impl Future, B::Error>> { + ) -> Result, B::Error> { self.submit_at(at, std::iter::once(xt), false) .map(|import_result| import_result.and_then(|mut import_result| import_result .pop() .expect("One extrinsic passed; one result returned; qed") )) + .await } /// Import a single extrinsic and starts to watch their progress in the pool. - pub fn submit_and_watch( + pub async fn submit_and_watch( &self, at: &BlockId, xt: ExtrinsicFor, - ) -> impl Future, BlockHash>, B::Error>> { - let block_number = match self.resolve_block_number(at) { - Ok(block_number) => block_number, - Err(err) => return Either::Left(ready(Err(err))) - }; - - let validated_pool = self.validated_pool.clone(); - Either::Right( - self.verify_one(at, block_number, xt, false) - .map(move |validated_transactions| validated_pool.submit_and_watch(validated_transactions.1)) - ) + ) -> Result, BlockHash>, B::Error> { + let block_number = self.resolve_block_number(at)?; + let (_, tx) = self.verify_one(at, block_number, xt, false).await; + self.validated_pool.submit_and_watch(tx) } /// Revalidate all ready transactions. /// /// Returns future that performs validation of all ready transactions and /// then resubmits all transactions back to the pool. - pub fn revalidate_ready( + pub async fn revalidate_ready( &self, at: &BlockId, max: Option, - ) -> impl Future> { - use std::time::Instant; + ) -> Result<(), B::Error> { log::debug!(target: "txpool", "Fetching ready transactions (up to: {})", max.map(|x| format!("{}", x)).unwrap_or_else(|| "all".into()) @@ -191,23 +201,20 @@ impl Pool { .take(max.unwrap_or_else(usize::max_value)); let now = Instant::now(); - self.verify(at, ready, false) - .map(move |revalidated_transactions| { - log::debug!(target: "txpool", - "Re-verified transactions, took {} ms. Resubmitting.", - now.elapsed().as_millis() - ); - let now = Instant::now(); - let res = revalidated_transactions.map( - |revalidated_transactions| validated_pool.resubmit(revalidated_transactions) - ); - log::debug!(target: "txpool", - "Resubmitted. Took {} ms. Status: {:?}", - now.elapsed().as_millis(), - validated_pool.status() - ); - res - }) + let revalidated_transactions = self.verify(at, ready, false).await?; + log::debug!(target: "txpool", + "Re-verified transactions, took {} ms. Resubmitting.", + now.elapsed().as_millis() + ); + + let now = Instant::now(); + self.validated_pool.resubmit(revalidated_transactions); + log::debug!(target: "txpool", + "Resubmitted. Took {} ms. Status: {:?}", + now.elapsed().as_millis(), + validated_pool.status() + ); + Ok(()) } /// Prunes known ready transactions. @@ -233,12 +240,12 @@ impl Pool { /// To perform pruning we need the tags that each extrinsic provides and to avoid calling /// into runtime too often we first lookup all extrinsics that are in the pool and get /// their provided tags from there. Otherwise we query the runtime at the `parent` block. - pub fn prune( + pub async fn prune( &self, at: &BlockId, parent: &BlockId, extrinsics: &[ExtrinsicFor], - ) -> impl Future> { + ) -> Result<(), B::Error> { log::debug!( target: "txpool", "Starting pruning of block {:?} (extrinsics: {})", @@ -252,34 +259,26 @@ impl Pool { // Zip the ones from the pool with the full list (we get pairs `(Extrinsic, Option>)`) let all = extrinsics.iter().zip(in_pool_tags.into_iter()); - // Prepare future that collect tags for all extrinsics - let future_tags = join_all(all - .map(|(extrinsic, in_pool_tags)| - match in_pool_tags { - // reuse the tags for extrinsics that were found in the pool - Some(tags) => Either::Left( - ready(tags) - ), - // if it's not found in the pool query the runtime at parent block - // to get validity info and tags that the extrinsic provides. - None => Either::Right(self.validated_pool.api().validate_transaction(parent, extrinsic.clone()) - .then(|validity| ready(match validity { - Ok(Ok(validity)) => validity.provides, - // silently ignore invalid extrinsics, - // cause they might just be inherent - _ => Vec::new(), - }))), - } - )); + let mut future_tags = Vec::new(); + for (extrinsic, in_pool_tags) in all { + match in_pool_tags { + // reuse the tags for extrinsics that were found in the pool + Some(tags) => future_tags.extend(tags), + // if it's not found in the pool query the runtime at parent block + // to get validity info and tags that the extrinsic provides. + None => { + let validity = self.validated_pool.api() + .validate_transaction(parent, extrinsic.clone()) + .await; + + if let Ok(Ok(validity)) = validity { + future_tags.extend(validity.provides); + } + }, + } + } - // Prune transactions by tags - let at = at.clone(); - let self_clone = self.clone(); - future_tags.then(move |tags| self_clone.prune_tags( - &at, - tags.into_iter().flat_map(|tags| tags), - in_pool_hashes, - )) + self.prune_tags(at, future_tags, in_pool_hashes).await } /// Prunes ready transactions that provide given list of tags. @@ -303,50 +302,47 @@ impl Pool { /// the second parameter of `known_imported_hashes`. These transactions /// (if pruned) are not revalidated and become temporarily banned to /// prevent importing them in the (near) future. - pub fn prune_tags( + pub async fn prune_tags( &self, at: &BlockId, tags: impl IntoIterator, known_imported_hashes: impl IntoIterator> + Clone, - ) -> impl Future> { + ) -> Result<(), B::Error> { log::debug!(target: "txpool", "Pruning at {:?}", at); // Prune all transactions that provide given tags let prune_status = match self.validated_pool.prune_tags(tags) { Ok(prune_status) => prune_status, - Err(e) => return Either::Left(ready(Err(e))), + Err(e) => return Err(e), }; // Make sure that we don't revalidate extrinsics that were part of the recently // imported block. This is especially important for UTXO-like chains cause the // inputs are pruned so such transaction would go to future again. - self.validated_pool.ban(&std::time::Instant::now(), known_imported_hashes.clone().into_iter()); + self.validated_pool.ban(&Instant::now(), known_imported_hashes.clone().into_iter()); // Try to re-validate pruned transactions since some of them might be still valid. // note that `known_imported_hashes` will be rejected here due to temporary ban. let pruned_hashes = prune_status.pruned.iter().map(|tx| tx.hash.clone()).collect::>(); let pruned_transactions = prune_status.pruned.into_iter().map(|tx| tx.data.clone()); - let reverify_future = self.verify(at, pruned_transactions, false); + + let reverified_transactions = self.verify(at, pruned_transactions, false).await?; log::trace!(target: "txpool", "Prunning at {:?}. Resubmitting transactions.", at); // And finally - submit reverified transactions back to the pool - let at = at.clone(); - let validated_pool = self.validated_pool.clone(); - Either::Right(reverify_future.then(move |reverified_transactions| - ready(reverified_transactions.and_then(|reverified_transactions| - validated_pool.resubmit_pruned( - &at, - known_imported_hashes, - pruned_hashes, - reverified_transactions.into_iter().map(|(_, xt)| xt).collect(), - )) - ))) + + self.validated_pool.resubmit_pruned( + &at, + known_imported_hashes, + pruned_hashes, + reverified_transactions.into_iter().map(|(_, xt)| xt).collect(), + ) } /// Return an event stream of notifications for when transactions are imported to the pool. /// /// Consumers of this stream should use the `ready` method to actually get the /// pending transactions in the right order. - pub fn import_notification_stream(&self) -> EventStream { + pub fn import_notification_stream(&self) -> EventStream> { self.validated_pool.import_notification_stream() } @@ -383,69 +379,84 @@ impl Pool { } /// Returns future that validates a bunch of transactions at given block. - fn verify( + async fn verify( &self, at: &BlockId, xts: impl IntoIterator>, force: bool, - ) -> impl Future, ValidatedTransactionFor>, B::Error>> { + ) -> Result, ValidatedTransactionFor>, B::Error> { // we need a block number to compute tx validity - let block_number = match self.resolve_block_number(at) { - Ok(block_number) => block_number, - Err(err) => return Either::Left(ready(Err(err))), - }; + let block_number = self.resolve_block_number(at)?; + let mut result = HashMap::new(); - // for each xt, prepare a validation future - let validation_futures = xts.into_iter().map(move |xt| - self.verify_one(at, block_number, xt, force) - ); + for (hash, validated_tx) in + futures::future::join_all( + xts.into_iter() + .map(|xt| self.verify_one(at, block_number, xt, force)) + ) + .await + { + result.insert(hash, validated_tx); + } - // make single validation future that waits all until all extrinsics are validated - Either::Right(join_all(validation_futures).then(|x| ready(Ok(x.into_iter().collect())))) + Ok(result) } /// Returns future that validates single transaction at given block. - fn verify_one( + async fn verify_one( &self, block_id: &BlockId, block_number: NumberFor, xt: ExtrinsicFor, force: bool, - ) -> impl Future, ValidatedTransactionFor)> { + ) -> (ExHash, ValidatedTransactionFor) { let (hash, bytes) = self.validated_pool.api().hash_and_length(&xt); if !force && self.validated_pool.is_banned(&hash) { - return Either::Left(ready(( + return ( hash.clone(), ValidatedTransaction::Invalid(hash, error::Error::TemporarilyBanned.into()), - ))) + ) } - Either::Right(self.validated_pool.api().validate_transaction(block_id, xt.clone()) - .then(move |validation_result| ready((hash.clone(), match validation_result { - Ok(validity) => match validity { - Ok(validity) => if validity.provides.is_empty() { - ValidatedTransaction::Invalid(hash, error::Error::NoTagsProvided.into()) - } else { - ValidatedTransaction::Valid(base::Transaction { - data: xt, - bytes, - hash, - priority: validity.priority, - requires: validity.requires, - provides: validity.provides, - propagate: validity.propagate, - valid_till: block_number - .saturated_into::() - .saturating_add(validity.longevity), - }) - }, - Err(TransactionValidityError::Invalid(e)) => - ValidatedTransaction::Invalid(hash, error::Error::InvalidTransaction(e).into()), - Err(TransactionValidityError::Unknown(e)) => - ValidatedTransaction::Unknown(hash, error::Error::UnknownTransaction(e).into()), - }, - Err(e) => ValidatedTransaction::Invalid(hash, e), - })))) + let validation_result = self.validated_pool.api().validate_transaction(block_id, xt.clone()).await; + + let status = match validation_result { + Ok(status) => status, + Err(e) => return (hash.clone(), ValidatedTransaction::Invalid(hash, e)), + }; + + let validity = match status { + Ok(validity) => { + if validity.provides.is_empty() { + ValidatedTransaction::Invalid(hash.clone(), error::Error::NoTagsProvided.into()) + } else { + ValidatedTransaction::Valid(base::Transaction { + data: xt, + bytes, + hash: hash.clone(), + priority: validity.priority, + requires: validity.requires, + provides: validity.provides, + propagate: validity.propagate, + valid_till: block_number + .saturated_into::() + .saturating_add(validity.longevity), + }) + } + }, + + Err(TransactionValidityError::Invalid(e)) => + ValidatedTransaction::Invalid(hash.clone(), error::Error::InvalidTransaction(e).into()), + Err(TransactionValidityError::Unknown(e)) => + ValidatedTransaction::Unknown(hash.clone(), error::Error::UnknownTransaction(e).into()), + }; + + (hash, validity) + } + + /// Get ready transaction by hash, if it present in the pool. + pub fn ready_transaction(&self, hash: &ExHash) -> Option> { + self.validated_pool.ready_by_hash(hash) } } @@ -459,10 +470,7 @@ impl Clone for Pool { #[cfg(test)] mod tests { - use std::{ - collections::{HashMap, HashSet}, - time::Instant, - }; + use std::collections::{HashMap, HashSet}; use parking_lot::Mutex; use futures::executor::block_on; use super::*; @@ -471,6 +479,7 @@ mod tests { use codec::Encode; use substrate_test_runtime::{Block, Extrinsic, Transfer, H256, AccountId}; use assert_matches::assert_matches; + use wasm_timer::Instant; use crate::base_pool::Limit; const INVALID_NONCE: u64 = 254; @@ -488,6 +497,7 @@ mod tests { type Hash = u64; type Error = error::Error; type ValidationFuture = futures::future::Ready>; + type BodyFuture = futures::future::Ready>>>; /// Verify extrinsic at given block. fn validate_transaction( @@ -560,6 +570,10 @@ mod tests { len ) } + + fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { + futures::future::ready(Ok(None)) + } } fn uxt(transfer: Transfer) -> Extrinsic { @@ -567,7 +581,7 @@ mod tests { } fn pool() -> Pool { - Pool::new(Default::default(), TestApi::default()) + Pool::new(Default::default(), TestApi::default().into()) } #[test] @@ -643,8 +657,8 @@ mod tests { // then let mut it = futures::executor::block_on_stream(stream); - assert_eq!(it.next(), Some(())); - assert_eq!(it.next(), Some(())); + assert_eq!(it.next(), Some(32)); + assert_eq!(it.next(), Some(33)); assert_eq!(it.next(), None); } @@ -713,7 +727,7 @@ mod tests { ready: limit.clone(), future: limit.clone(), ..Default::default() - }, TestApi::default()); + }, TestApi::default().into()); let hash1 = block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer { from: AccountId::from_h256(H256::from_low_u64_be(1)), @@ -748,7 +762,7 @@ mod tests { ready: limit.clone(), future: limit.clone(), ..Default::default() - }, TestApi::default()); + }, TestApi::default().into()); // when block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer { @@ -924,7 +938,7 @@ mod tests { ready: limit.clone(), future: limit.clone(), ..Default::default() - }, TestApi::default()); + }, TestApi::default().into()); let xt = uxt(Transfer { from: AccountId::from_h256(H256::from_low_u64_be(1)), @@ -958,7 +972,7 @@ mod tests { let (tx, rx) = std::sync::mpsc::sync_channel(1); let mut api = TestApi::default(); api.delay = Arc::new(Mutex::new(rx.into())); - let pool = Arc::new(Pool::new(Default::default(), api)); + let pool = Arc::new(Pool::new(Default::default(), api.into())); // when let xt = uxt(Transfer { diff --git a/client/transaction-pool/graph/src/ready.rs b/client/transaction-pool/graph/src/ready.rs index cdb0076e5a948dc2dcb4cec104ac749544193ae2..23f0d49a93071ec180822bef294888a4a6373127 100644 --- a/client/transaction-pool/graph/src/ready.rs +++ b/client/transaction-pool/graph/src/ready.rs @@ -36,7 +36,7 @@ use crate::base_pool::Transaction; /// An in-pool transaction reference. /// /// Should be cheap to clone. -#[derive(Debug)] +#[derive(Debug, parity_util_mem::MallocSizeOf)] pub struct TransactionRef { /// The actual transaction data. pub transaction: Arc>, @@ -74,7 +74,7 @@ impl PartialEq for TransactionRef { } impl Eq for TransactionRef {} -#[derive(Debug)] +#[derive(Debug, parity_util_mem::MallocSizeOf)] pub struct ReadyTx { /// A reference to a transaction pub transaction: TransactionRef, @@ -104,7 +104,7 @@ Hence every hash retrieved from `provided_tags` is always present in `ready`; qed "#; -#[derive(Debug)] +#[derive(Debug, parity_util_mem::MallocSizeOf)] pub struct ReadyTransactions { /// Insertion id insertion_id: u64, @@ -230,8 +230,13 @@ impl ReadyTransactions { self.ready.read().contains_key(hash) } - /// Retrieve transaction by hash - pub fn by_hash(&self, hashes: &[Hash]) -> Vec>>> { + /// Retrive transaction by hash + pub fn by_hash(&self, hash: &Hash) -> Option>> { + self.by_hashes(&[hash.clone()]).into_iter().next().unwrap_or(None) + } + + /// Retrieve transactions by hash + pub fn by_hashes(&self, hashes: &[Hash]) -> Vec>>> { let ready = self.ready.read(); hashes.iter().map(|hash| { ready.get(hash).map(|x| x.transaction.transaction.clone()) @@ -671,6 +676,24 @@ mod tests { assert_eq!(it.next(), None); } + #[test] + fn can_report_heap_size() { + let mut ready = ReadyTransactions::default(); + let tx = Transaction { + data: vec![5], + bytes: 1, + hash: 5, + priority: 1, + valid_till: u64::max_value(), // use the max_value() here for testing. + requires: vec![], + provides: vec![], + propagate: true, + }; + import(&mut ready, tx).unwrap(); + + assert!(parity_util_mem::malloc_size(&ready) > 200); + } + #[test] fn should_order_refs() { let mut id = 1; diff --git a/client/transaction-pool/graph/src/rotator.rs b/client/transaction-pool/graph/src/rotator.rs index e1c852f95a418146c7b0eefc5b2d0804f5bb507d..55a9230522e2f7ee19e31cd004184d691ee8fd9e 100644 --- a/client/transaction-pool/graph/src/rotator.rs +++ b/client/transaction-pool/graph/src/rotator.rs @@ -23,9 +23,10 @@ use std::{ collections::HashMap, hash, iter, - time::{Duration, Instant}, + time::Duration, }; use parking_lot::RwLock; +use wasm_timer::Instant; use crate::base_pool::Transaction; diff --git a/client/transaction-pool/graph/src/validated_pool.rs b/client/transaction-pool/graph/src/validated_pool.rs index 29f82fb894ac0df67ec7461ad43462bbb95520a2..d02bc0ec7236d4eede749965ecd9b615775463a1 100644 --- a/client/transaction-pool/graph/src/validated_pool.rs +++ b/client/transaction-pool/graph/src/validated_pool.rs @@ -19,7 +19,6 @@ use std::{ fmt, hash, sync::Arc, - time, }; use crate::base_pool as base; @@ -37,6 +36,7 @@ use sp_runtime::{ transaction_validity::TransactionTag as Tag, }; use sp_transaction_pool::{error, PoolStatus}; +use wasm_timer::Instant; use crate::base_pool::PruneStatus; use crate::pool::{EventStream, Options, ChainApi, BlockHash, ExHash, ExtrinsicFor, TransactionFor}; @@ -63,20 +63,32 @@ pub type ValidatedTransactionFor = ValidatedTransaction< /// Pool that deals with validated transactions. pub(crate) struct ValidatedPool { - api: B, + api: Arc, options: Options, listener: RwLock, BlockHash>>, pool: RwLock, ExtrinsicFor, >>, - import_notification_sinks: Mutex>>, + import_notification_sinks: Mutex>>>, rotator: PoolRotator>, } +#[cfg(not(target_os = "unknown"))] +impl parity_util_mem::MallocSizeOf for ValidatedPool +where + B::Hash: parity_util_mem::MallocSizeOf, + ExtrinsicFor: parity_util_mem::MallocSizeOf, +{ + fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + // other entries insignificant or non-primary references + self.pool.size_of(ops) + } +} + impl ValidatedPool { /// Create a new transaction pool. - pub fn new(options: Options, api: B) -> Self { + pub fn new(options: Options, api: Arc) -> Self { let base_pool = base::BasePool::new(options.reject_future_transactions); ValidatedPool { api, @@ -89,7 +101,7 @@ impl ValidatedPool { } /// Bans given set of hashes. - pub fn ban(&self, now: &std::time::Instant, hashes: impl IntoIterator>) { + pub fn ban(&self, now: &Instant, hashes: impl IntoIterator>) { self.rotator.ban(now, hashes) } @@ -125,8 +137,8 @@ impl ValidatedPool { ValidatedTransaction::Valid(tx) => { let imported = self.pool.write().import(tx)?; - if let base::Imported::Ready { .. } = imported { - self.import_notification_sinks.lock().retain(|sink| sink.unbounded_send(()).is_ok()); + if let base::Imported::Ready { ref hash, .. } = imported { + self.import_notification_sinks.lock().retain(|sink| sink.unbounded_send(hash.clone()).is_ok()); } let mut listener = self.listener.write(); @@ -134,7 +146,7 @@ impl ValidatedPool { Ok(imported.hash().clone()) } ValidatedTransaction::Invalid(hash, err) => { - self.rotator.ban(&std::time::Instant::now(), std::iter::once(hash)); + self.rotator.ban(&Instant::now(), std::iter::once(hash)); Err(err.into()) }, ValidatedTransaction::Unknown(hash, err) => { @@ -150,19 +162,27 @@ impl ValidatedPool { let future_limit = &self.options.future; debug!(target: "txpool", "Pool Status: {:?}", status); - if ready_limit.is_exceeded(status.ready, status.ready_bytes) - || future_limit.is_exceeded(status.future, status.future_bytes) { + || future_limit.is_exceeded(status.future, status.future_bytes) + { + debug!( + target: "txpool", + "Enforcing limits ({}/{}kB ready, {}/{}kB future", + ready_limit.count, ready_limit.total_bytes / 1024, + future_limit.count, future_limit.total_bytes / 1024, + ); + // clean up the pool let removed = { let mut pool = self.pool.write(); let removed = pool.enforce_limits(ready_limit, future_limit) .into_iter().map(|x| x.hash.clone()).collect::>(); // ban all removed transactions - self.rotator.ban(&std::time::Instant::now(), removed.iter().map(|x| x.clone())); + self.rotator.ban(&Instant::now(), removed.iter().map(|x| x.clone())); removed }; // run notifications + debug!(target: "txpool", "Enforcing limits: {} dropped", removed.len()); let mut listener = self.listener.write(); for h in &removed { listener.dropped(h, None); @@ -189,7 +209,7 @@ impl ValidatedPool { .map(|_| watcher) }, ValidatedTransaction::Invalid(hash, err) => { - self.rotator.ban(&std::time::Instant::now(), std::iter::once(hash)); + self.rotator.ban(&Instant::now(), std::iter::once(hash)); Err(err.into()) }, ValidatedTransaction::Unknown(_, err) => Err(err.into()), @@ -320,7 +340,7 @@ impl ValidatedPool { /// For each extrinsic, returns tags that it provides (if known), or None (if it is unknown). pub fn extrinsics_tags(&self, hashes: &[ExHash]) -> Vec>> { - self.pool.read().by_hash(&hashes) + self.pool.read().by_hashes(&hashes) .into_iter() .map(|existing_in_pool| existing_in_pool .map(|transaction| transaction.provides.iter().cloned() @@ -328,6 +348,11 @@ impl ValidatedPool { .collect() } + /// Get ready transaction by hash + pub fn ready_by_hash(&self, hash: &ExHash) -> Option> { + self.pool.read().ready_by_hash(hash) + } + /// Prunes ready transactions that provide given list of tags. pub fn prune_tags( &self, @@ -406,7 +431,7 @@ impl ValidatedPool { let block_number = self.api.block_id_to_number(at)? .ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())? .saturated_into::(); - let now = time::Instant::now(); + let now = Instant::now(); let to_remove = { self.ready() .filter(|tx| self.rotator.ban_if_stale(&now, block_number, &tx)) @@ -444,7 +469,7 @@ impl ValidatedPool { } /// Return an event stream of transactions imported to the pool. - pub fn import_notification_stream(&self) -> EventStream { + pub fn import_notification_stream(&self) -> EventStream> { let (sink, stream) = mpsc::unbounded(); self.import_notification_sinks.lock().push(sink); stream @@ -473,7 +498,7 @@ impl ValidatedPool { debug!(target: "txpool", "Removing invalid transactions: {:?}", hashes); // temporarily ban invalid transactions - self.rotator.ban(&time::Instant::now(), hashes.iter().cloned()); + self.rotator.ban(&Instant::now(), hashes.iter().cloned()); let invalid = self.pool.write().remove_subtree(hashes); diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index 8495b8b65f17bbaeaadf60b3cbb34fa2ac4af685..bfc13c01fdf53b0992f711fa6c58215aab12ba59 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -19,12 +19,13 @@ use std::{marker::PhantomData, pin::Pin, sync::Arc}; use codec::{Decode, Encode}; use futures::{ - channel::oneshot, executor::{ThreadPool, ThreadPoolBuilder}, future::{Future, FutureExt, ready}, + channel::oneshot, executor::{ThreadPool, ThreadPoolBuilder}, future::{Future, FutureExt, ready, Ready}, }; use sc_client_api::{ blockchain::HeaderBackend, - light::{Fetcher, RemoteCallRequest} + light::{Fetcher, RemoteCallRequest, RemoteBodyRequest}, + BlockBody, }; use sp_core::Hasher; use sp_runtime::{ @@ -63,7 +64,7 @@ impl FullChainApi where impl sc_transaction_graph::ChainApi for FullChainApi where Block: BlockT, - Client: ProvideRuntimeApi + BlockIdTo + 'static + Send + Sync, + Client: ProvideRuntimeApi + BlockBody + BlockIdTo + 'static + Send + Sync, Client::Api: TaggedTransactionQueue, sp_api::ApiErrorFor: Send, { @@ -71,6 +72,11 @@ impl sc_transaction_graph::ChainApi for FullChainApi> + Send>>; + type BodyFuture = Ready::Extrinsic>>>>; + + fn block_body(&self, id: &BlockId) -> Self::BodyFuture { + ready(self.client.block_body(&id).map_err(|e| error::Error::from(e))) + } fn validate_transaction( &self, @@ -81,13 +87,13 @@ impl sc_transaction_graph::ChainApi for FullChainApi sc_transaction_graph::ChainApi for LightChainApi> + Send + Unpin>; + type BodyFuture = Pin::Extrinsic>>>> + Send>>; fn validate_transaction( &self, @@ -197,4 +204,33 @@ impl sc_transaction_graph::ChainApi for LightChainApi::Hashing as HashT>::hash(x), x.len()) }) } + + fn block_body(&self, id: &BlockId) -> Self::BodyFuture { + let header = self.client.header(*id) + .and_then(|h| h.ok_or(sp_blockchain::Error::UnknownBlock(format!("{}", id)))); + let header = match header { + Ok(header) => header, + Err(err) => { + log::warn!(target: "txpool", "Failed to query header: {:?}", err); + return Box::pin(ready(Ok(None))); + } + }; + + let fetcher = self.fetcher.clone(); + async move { + let transactions = fetcher.remote_body({ + RemoteBodyRequest { + header, + retry_count: None, + } + }) + .await + .unwrap_or_else(|e| { + log::warn!(target: "txpool", "Failed to fetch block body: {:?}", e); + Vec::new() + }); + + Ok(Some(transactions)) + }.boxed() + } } diff --git a/client/transaction-pool/src/lib.rs b/client/transaction-pool/src/lib.rs index 4d71307c0abd05118ad2530e54d8ec45552e2c11..f4895d9a11e4011f0a2b28baa089b6962a16362a 100644 --- a/client/transaction-pool/src/lib.rs +++ b/client/transaction-pool/src/lib.rs @@ -20,27 +20,28 @@ #![warn(unused_extern_crates)] mod api; -mod maintainer; - pub mod error; -#[cfg(test)] -mod tests; + +#[cfg(any(feature = "test-helpers", test))] +pub mod testing; pub use sc_transaction_graph as txpool; pub use crate::api::{FullChainApi, LightChainApi}; -pub use crate::maintainer::{FullBasicPoolMaintainer, LightBasicPoolMaintainer}; -use std::{collections::HashMap, sync::Arc}; -use futures::{Future, FutureExt}; +use std::{collections::HashMap, sync::Arc, pin::Pin}; +use futures::{Future, FutureExt, future::ready}; +use parking_lot::Mutex; use sp_runtime::{ generic::BlockId, - traits::Block as BlockT, + traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic}, }; use sp_transaction_pool::{ TransactionPool, PoolStatus, ImportNotificationStream, - TxHash, TransactionFor, TransactionStatusStreamFor, + TxHash, TransactionFor, TransactionStatusStreamFor, BlockHash, + MaintainedTransactionPool, PoolFuture, }; +use wasm_timer::Instant; /// Basic implementation of transaction pool that can be customized by providing PoolApi. pub struct BasicPool @@ -49,6 +50,38 @@ pub struct BasicPool PoolApi: sc_transaction_graph::ChainApi, { pool: Arc>, + api: Arc, + revalidation_strategy: Arc>>>, +} + +#[cfg(not(target_os = "unknown"))] +impl parity_util_mem::MallocSizeOf for BasicPool +where + PoolApi: sc_transaction_graph::ChainApi, + PoolApi::Hash: parity_util_mem::MallocSizeOf, + Block: BlockT, +{ + fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + // other entries insignificant or non-primary references + self.pool.size_of(ops) + } +} + +/// Type of revalidation. +pub enum RevalidationType { + /// Light revalidation type. + /// + /// During maintenance, transaction pool makes periodic revalidation + /// of all transactions depending on number of blocks or time passed. + /// Also this kind of revalidation does not resubmit transactions from + /// retracted blocks, since it is too expensive. + Light, + + /// Full revalidation type. + /// + /// During maintenance, transaction pool revalidates some fixed amount of + /// transactions from the pool of valid transactions. + Full, } impl BasicPool @@ -57,10 +90,32 @@ impl BasicPool PoolApi: sc_transaction_graph::ChainApi, { /// Create new basic transaction pool with provided api. - pub fn new(options: sc_transaction_graph::Options, pool_api: PoolApi) -> Self { + pub fn new( + options: sc_transaction_graph::Options, + pool_api: Arc, + ) -> Self { + Self::with_revalidation_type(options, pool_api, RevalidationType::Full) + } + + /// Create new basic transaction pool with provided api and custom + /// revalidation type. + pub fn with_revalidation_type( + options: sc_transaction_graph::Options, + pool_api: Arc, + revalidation_type: RevalidationType, + ) -> Self { + let cloned_api = pool_api.clone(); BasicPool { + api: cloned_api, pool: Arc::new(sc_transaction_graph::Pool::new(options, pool_api)), + revalidation_strategy: Arc::new(Mutex::new( + match revalidation_type { + RevalidationType::Light => RevalidationStrategy::Light(RevalidationStatus::NotScheduled), + RevalidationType::Full => RevalidationStrategy::Always, + } + )), } + } /// Gets shared reference to the underlying pool. @@ -72,38 +127,50 @@ impl BasicPool impl TransactionPool for BasicPool where Block: BlockT, - PoolApi: 'static + sc_transaction_graph::ChainApi, + PoolApi: 'static + sc_transaction_graph::ChainApi, { type Block = PoolApi::Block; type Hash = sc_transaction_graph::ExHash; type InPoolTransaction = sc_transaction_graph::base_pool::Transaction, TransactionFor>; - type Error = error::Error; + type Error = PoolApi::Error; fn submit_at( &self, at: &BlockId, - xts: impl IntoIterator> + 'static, - ) -> Box, Self::Error>>, Self::Error>> + Send + Unpin> { - Box::new(self.pool.submit_at(at, xts, false)) + xts: Vec>, + ) -> PoolFuture, Self::Error>>, Self::Error> { + let pool = self.pool.clone(); + let at = *at; + async move { + pool.submit_at(&at, xts, false).await + }.boxed() } fn submit_one( &self, at: &BlockId, xt: TransactionFor, - ) -> Box, Self::Error>> + Send + Unpin> { - Box::new(self.pool.submit_one(at, xt)) + ) -> PoolFuture, Self::Error> { + let pool = self.pool.clone(); + let at = *at; + async move { + pool.submit_one(&at, xt).await + }.boxed() } fn submit_and_watch( &self, at: &BlockId, xt: TransactionFor, - ) -> Box>, Self::Error>> + Send + Unpin> { - Box::new( - self.pool.submit_and_watch(at, xt) + ) -> PoolFuture>, Self::Error> { + let at = *at; + let pool = self.pool.clone(); + + async move { + pool.submit_and_watch(&at, xt) .map(|result| result.map(|watcher| Box::new(watcher.into_stream()) as _)) - ) + .await + }.boxed() } fn remove_invalid(&self, hashes: &[TxHash]) -> Vec> { @@ -118,7 +185,7 @@ impl TransactionPool for BasicPool Box::new(self.pool.ready()) } - fn import_notification_stream(&self) -> ImportNotificationStream { + fn import_notification_stream(&self) -> ImportNotificationStream> { self.pool.import_notification_stream() } @@ -129,4 +196,174 @@ impl TransactionPool for BasicPool fn on_broadcasted(&self, propagations: HashMap, Vec>) { self.pool.on_broadcasted(propagations) } + + fn ready_transaction(&self, hash: &TxHash) -> Option> { + self.pool.ready_transaction(hash) + } +} + +#[cfg_attr(test, derive(Debug))] +enum RevalidationStatus { + /// The revalidation has never been completed. + NotScheduled, + /// The revalidation is scheduled. + Scheduled(Option, Option), + /// The revalidation is in progress. + InProgress, +} + +enum RevalidationStrategy { + Always, + Light(RevalidationStatus) +} + +struct RevalidationAction { + revalidate: bool, + resubmit: bool, + revalidate_amount: Option, +} + +impl RevalidationStrategy { + pub fn clear(&mut self) { + if let Self::Light(status) = self { + status.clear() + } + } + + pub fn next( + &mut self, + block: N, + revalidate_time_period: Option, + revalidate_block_period: Option, + ) -> RevalidationAction { + match self { + Self::Light(status) => RevalidationAction { + revalidate: status.next_required( + block, + revalidate_time_period, + revalidate_block_period + ), + resubmit: false, + revalidate_amount: None, + }, + Self::Always => RevalidationAction { + revalidate: true, + resubmit: true, + revalidate_amount: Some(16), + } + } + } +} + +impl RevalidationStatus { + /// Called when revalidation is completed. + pub fn clear(&mut self) { + *self = Self::NotScheduled; + } + + /// Returns true if revalidation is required. + pub fn next_required( + &mut self, + block: N, + revalidate_time_period: Option, + revalidate_block_period: Option, + ) -> bool { + match *self { + Self::NotScheduled => { + *self = Self::Scheduled( + revalidate_time_period.map(|period| Instant::now() + period), + revalidate_block_period.map(|period| block + period), + ); + false + }, + Self::Scheduled(revalidate_at_time, revalidate_at_block) => { + let is_required = revalidate_at_time.map(|at| Instant::now() >= at).unwrap_or(false) + || revalidate_at_block.map(|at| block >= at).unwrap_or(false); + if is_required { + *self = Self::InProgress; + } + is_required + }, + Self::InProgress => false, + } + } +} + +impl MaintainedTransactionPool for BasicPool +where + Block: BlockT, + PoolApi: 'static + sc_transaction_graph::ChainApi, +{ + fn maintain(&self, id: &BlockId, retracted: &[BlockHash]) + -> Pin + Send>> + { + let id = id.clone(); + let pool = self.pool.clone(); + let api = self.api.clone(); + + let block_number = match api.block_id_to_number(&id) { + Ok(Some(number)) => number, + _ => { + log::trace!(target: "txqueue", "Skipping chain event - no number for that block {:?}", id); + return Box::pin(ready(())); + } + }; + + let next_action = self.revalidation_strategy.lock().next( + block_number, + Some(std::time::Duration::from_secs(60)), + Some(20.into()), + ); + let revalidation_strategy = self.revalidation_strategy.clone(); + let retracted = retracted.to_vec(); + + async move { + // We don't query block if we won't prune anything + if !pool.status().is_empty() { + let hashes = api.block_body(&id).await + .unwrap_or_else(|e| { + log::warn!("Prune known transactions: error request {:?}!", e); + None + }) + .unwrap_or_default() + .into_iter() + .map(|tx| pool.hash_of(&tx)) + .collect::>(); + + if let Err(e) = pool.prune_known(&id, &hashes) { + log::error!("Cannot prune known in the pool {:?}!", e); + } + } + + if next_action.resubmit { + let mut resubmit_transactions = Vec::new(); + + for retracted_hash in retracted { + let block_transactions = api.block_body(&BlockId::hash(retracted_hash.clone())).await + .unwrap_or_else(|e| { + log::warn!("Failed to fetch block body {:?}!", e); + None + }) + .unwrap_or_default() + .into_iter() + .filter(|tx| tx.is_signed().unwrap_or(true)); + + resubmit_transactions.extend(block_transactions); + } + if let Err(e) = pool.submit_at(&id, resubmit_transactions, true).await { + log::debug!(target: "txpool", + "[{:?}] Error re-submitting transactions: {:?}", id, e + ) + } + } + + if next_action.revalidate { + if let Err(e) = pool.revalidate_ready(&id, next_action.revalidate_amount).await { + log::warn!("Revalidate ready failed {:?}", e); + } + } + + revalidation_strategy.lock().clear(); + }.boxed() + } } diff --git a/client/transaction-pool/src/maintainer.rs b/client/transaction-pool/src/maintainer.rs deleted file mode 100644 index 97dc7e10a6f11011c8560c7bf292a956ce9db095..0000000000000000000000000000000000000000 --- a/client/transaction-pool/src/maintainer.rs +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -use std::{ - marker::{PhantomData, Unpin}, - sync::Arc, - time::Instant, -}; -use futures::{ - Future, FutureExt, - future::{Either, join, ready}, -}; -use log::{warn, debug, trace}; -use parking_lot::Mutex; - -use sc_client_api::{ - client::BlockBody, - light::{Fetcher, RemoteBodyRequest}, -}; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, Extrinsic, Header, NumberFor, SimpleArithmetic}, -}; -use sp_blockchain::HeaderBackend; -use sp_transaction_pool::{TransactionPoolMaintainer, runtime_api::TaggedTransactionQueue}; -use sp_api::ProvideRuntimeApi; - -use sc_transaction_graph::{self, ChainApi}; - -/// Basic transaction pool maintainer for full clients. -pub struct FullBasicPoolMaintainer { - pool: Arc>, - client: Arc, -} - -impl FullBasicPoolMaintainer { - /// Create new basic full pool maintainer. - pub fn new( - pool: Arc>, - client: Arc, - ) -> Self { - FullBasicPoolMaintainer { pool, client } - } -} - -impl TransactionPoolMaintainer -for - FullBasicPoolMaintainer -where - Block: BlockT, - Client: ProvideRuntimeApi + HeaderBackend + BlockBody + 'static, - Client::Api: TaggedTransactionQueue, - PoolApi: ChainApi + 'static, -{ - type Block = Block; - type Hash = Block::Hash; - - fn maintain( - &self, - id: &BlockId, - retracted: &[Block::Hash], - ) -> Box + Send + Unpin> { - let now = std::time::Instant::now(); - let took = move || format!("Took {} ms", now.elapsed().as_millis()); - - let id = *id; - trace!(target: "txpool", "[{:?}] Starting pool maintainance", id); - // Put transactions from retracted blocks back into the pool. - let client_copy = self.client.clone(); - let retracted_transactions = retracted.to_vec().into_iter() - .filter_map(move |hash| client_copy.block_body(&BlockId::hash(hash)).ok().unwrap_or(None)) - .flat_map(|block| block.into_iter()) - // if signed information is not present, attempt to resubmit anyway. - .filter(|tx| tx.is_signed().unwrap_or(true)); - let resubmit_future = self.pool - .submit_at(&id, retracted_transactions, true) - .then(move |resubmit_result| ready(match resubmit_result { - Ok(_) => trace!(target: "txpool", - "[{:?}] Re-submitting retracted done. {}", id, took() - ), - Err(e) => debug!(target: "txpool", - "[{:?}] Error re-submitting transactions: {:?}", id, e - ), - })); - - // Avoid calling into runtime if there is nothing to prune from the pool anyway. - if self.pool.status().is_empty() { - return Box::new(resubmit_future) - } - - let block = (self.client.header(id), self.client.block_body(&id)); - let prune_future = match block { - (Ok(Some(header)), Ok(Some(extrinsics))) => { - let parent_id = BlockId::hash(*header.parent_hash()); - let prune_future = self.pool - .prune(&id, &parent_id, &extrinsics) - .then(move |prune_result| ready(match prune_result { - Ok(_) => trace!(target: "txpool", - "[{:?}] Pruning done. {}", id, took() - ), - Err(e) => warn!(target: "txpool", - "[{:?}] Error pruning transactions: {:?}", id, e - ), - })); - - Either::Left(resubmit_future.then(|_| prune_future)) - }, - (Ok(_), Ok(_)) => Either::Right(resubmit_future), - err => { - warn!(target: "txpool", "[{:?}] Error reading block: {:?}", id, err); - Either::Right(resubmit_future) - }, - }; - - let revalidate_future = self.pool - .revalidate_ready(&id, Some(16)) - .then(move |result| ready(match result { - Ok(_) => debug!(target: "txpool", - "[{:?}] Revalidation done: {}", id, took() - ), - Err(e) => warn!(target: "txpool", - "[{:?}] Encountered errors while revalidating transactions: {:?}", id, e - ), - })); - - Box::new(prune_future.then(|_| revalidate_future)) - } -} - -/// Basic transaction pool maintainer for light clients. -pub struct LightBasicPoolMaintainer { - pool: Arc>, - client: Arc, - fetcher: Arc, - revalidate_time_period: Option, - revalidate_block_period: Option>, - revalidation_status: Arc>>>, - _phantom: PhantomData, -} - -impl LightBasicPoolMaintainer - where - Block: BlockT, - Client: ProvideRuntimeApi + HeaderBackend + BlockBody + 'static, - Client::Api: TaggedTransactionQueue, - PoolApi: ChainApi + 'static, - F: Fetcher + 'static, -{ - /// Create light pool maintainer with default constants. - /// - /// Default constants are: revalidate every 60 seconds or every 20 blocks - /// (whatever happens first). - pub fn with_defaults( - pool: Arc>, - client: Arc, - fetcher: Arc, - ) -> Self { - Self::new( - pool, - client, - fetcher, - Some(std::time::Duration::from_secs(60)), - Some(20.into()), - ) - } - - /// Create light pool maintainer with passed constants. - pub fn new( - pool: Arc>, - client: Arc, - fetcher: Arc, - revalidate_time_period: Option, - revalidate_block_period: Option>, - ) -> Self { - Self { - pool, - client, - fetcher, - revalidate_time_period, - revalidate_block_period, - revalidation_status: Arc::new(Mutex::new(TxPoolRevalidationStatus::NotScheduled)), - _phantom: Default::default(), - } - } - - /// Returns future that prunes block transactions from the pool. - fn prune( - &self, - id: &BlockId, - header: &Block::Header, - ) -> impl std::future::Future { - // fetch transactions (possible future optimization: proofs of inclusion) that - // have been included into new block and prune these from the pool - let id = id.clone(); - let pool = self.pool.clone(); - self.fetcher.remote_body(RemoteBodyRequest { - header: header.clone(), - retry_count: None, - }) - .then(move |transactions| ready( - transactions - .map_err(|e| format!("{}", e)) - .and_then(|transactions| { - let hashes = transactions - .into_iter() - .map(|tx| pool.hash_of(&tx)) - .collect::>(); - pool.prune_known(&id, &hashes) - .map_err(|e| format!("{}", e)) - }) - )) - .then(|r| { - if let Err(e) = r { - warn!("Error pruning known transactions: {}", e) - } - ready(()) - }) - } - - /// Returns future that performs in-pool transations revalidation, if required. - fn revalidate( - &self, - id: &BlockId, - header: &Block::Header, - ) -> impl std::future::Future { - // to determine whether ready transaction is still valid, we perform periodic revalidaton - // of ready transactions - let is_revalidation_required = self.revalidation_status.lock().is_required( - *header.number(), - self.revalidate_time_period, - self.revalidate_block_period, - ); - match is_revalidation_required { - true => { - let revalidation_status = self.revalidation_status.clone(); - Either::Left(self.pool - .revalidate_ready(id, None) - .map(|r| r.map_err(|e| warn!("Error revalidating known transactions: {}", e))) - .map(move |_| revalidation_status.lock().clear())) - }, - false => Either::Right(ready(())), - } - } -} - -impl TransactionPoolMaintainer -for - LightBasicPoolMaintainer -where - Block: BlockT, - Client: ProvideRuntimeApi + HeaderBackend + BlockBody + 'static, - Client::Api: TaggedTransactionQueue, - PoolApi: ChainApi + 'static, - F: Fetcher + 'static, -{ - type Block = Block; - type Hash = Block::Hash; - - fn maintain( - &self, - id: &BlockId, - _retracted: &[Block::Hash], - ) -> Box + Send + Unpin> { - // Do nothing if transaction pool is empty. - if self.pool.status().is_empty() { - self.revalidation_status.lock().clear(); - return Box::new(ready(())); - } - let header = self.client.header(*id) - .and_then(|h| h.ok_or(sp_blockchain::Error::UnknownBlock(format!("{}", id)))); - let header = match header { - Ok(header) => header, - Err(err) => { - println!("Failed to maintain light tx pool: {:?}", err); - return Box::new(ready(())); - } - }; - - // else prune block transactions from the pool - let prune_future = self.prune(id, &header); - - // and then (optionally) revalidate in-pool transactions - let revalidate_future = self.revalidate(id, &header); - - let maintain_future = join( - prune_future, - revalidate_future, - ).map(|_| ()); - - Box::new(maintain_future) - } -} - -/// The status of transactions revalidation at light tx pool. -#[cfg_attr(test, derive(Debug))] -enum TxPoolRevalidationStatus { - /// The revalidation has never been completed. - NotScheduled, - /// The revalidation is scheduled. - Scheduled(Option, Option), - /// The revalidation is in progress. - InProgress, -} - -impl TxPoolRevalidationStatus { - /// Called when revalidation is completed. - pub fn clear(&mut self) { - *self = TxPoolRevalidationStatus::NotScheduled; - } - - /// Returns true if revalidation is required. - pub fn is_required( - &mut self, - block: N, - revalidate_time_period: Option, - revalidate_block_period: Option, - ) -> bool { - match *self { - TxPoolRevalidationStatus::NotScheduled => { - *self = TxPoolRevalidationStatus::Scheduled( - revalidate_time_period.map(|period| Instant::now() + period), - revalidate_block_period.map(|period| block + period), - ); - false - }, - TxPoolRevalidationStatus::Scheduled(revalidate_at_time, revalidate_at_block) => { - let is_required = revalidate_at_time.map(|at| Instant::now() >= at).unwrap_or(false) - || revalidate_at_block.map(|at| block >= at).unwrap_or(false); - if is_required { - *self = TxPoolRevalidationStatus::InProgress; - } - is_required - }, - TxPoolRevalidationStatus::InProgress => false, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use futures::executor::block_on; - use codec::Encode; - use substrate_test_runtime_client::{ - prelude::*, Client, runtime::{Block, Transfer}, sp_consensus::{BlockOrigin, SelectChain}, - LongestChain, - }; - use sp_transaction_pool::PoolStatus; - use crate::api::{FullChainApi, LightChainApi}; - - struct TestSetup { - client: Arc>, - longest_chain: LongestChain, - pool: Arc>, - } - - impl TestSetup { - fn new() -> TestSetup, Block>> { - let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain(); - let client = Arc::new(client); - let pool = Arc::new( - sc_transaction_graph::Pool::new(Default::default(), FullChainApi::new(client.clone())), - ); - TestSetup { - client, - longest_chain, - pool, - } - } - - fn new_light(fetcher: Arc) -> TestSetup, F, Block>> - where F: Fetcher + 'static, - { - let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain(); - let client = Arc::new(client); - let pool = Arc::new( - sc_transaction_graph::Pool::new( - Default::default(), - LightChainApi::new(client.clone(), fetcher) - ), - ); - TestSetup { - client, - longest_chain, - pool, - } - } - } - - fn setup() -> TestSetup, Block>> { - TestSetup::, Block>>::new() - } - - fn setup_light(fetcher: Arc) -> TestSetup, F, Block>> - where F: Fetcher + 'static, - { - TestSetup::, F, Block>>::new_light(fetcher) - } - - #[test] - fn should_remove_transactions_from_the_full_pool() { - let mut setup = setup(); - - let transaction = Transfer { - amount: 5, - nonce: 0, - from: AccountKeyring::Alice.into(), - to: Default::default(), - }.into_signed_tx(); - let best = setup.longest_chain.best_chain().unwrap(); - - // store the transaction in the pool - block_on(setup.pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap(); - - // import the block - let mut builder = setup.client.new_block(Default::default()).unwrap(); - builder.push(transaction.clone()).unwrap(); - let block = builder.build().unwrap().block; - let id = BlockId::hash(block.header().hash()); - setup.client.import(BlockOrigin::Own, block).unwrap(); - - // fire notification - this should clean up the queue - assert_eq!(setup.pool.status().ready, 1); - block_on(FullBasicPoolMaintainer::new(setup.pool.clone(), setup.client.clone()).maintain(&id, &[])); - - // then - assert_eq!(setup.pool.status().ready, 0); - assert_eq!(setup.pool.status().future, 0); - } - - #[test] - fn should_remove_transactions_from_the_light_pool() { - let transaction = Transfer { - amount: 5, - nonce: 0, - from: AccountKeyring::Alice.into(), - to: Default::default(), - }.into_signed_tx(); - let fetcher_transaction = transaction.clone(); - let fetcher = Arc::new(substrate_test_runtime_client::new_light_fetcher() - .with_remote_body(Some(Box::new(move |_| Ok(vec![fetcher_transaction.clone()])))) - .with_remote_call(Some(Box::new(move |_| { - let validity: sp_runtime::transaction_validity::TransactionValidity = - Ok(sp_runtime::transaction_validity::ValidTransaction { - priority: 0, - requires: Vec::new(), - provides: vec![vec![42]], - longevity: 0, - propagate: true, - }); - Ok(validity.encode()) - })))); - - let setup = setup_light(fetcher.clone()); - let best = setup.longest_chain.best_chain().unwrap(); - - // store the transaction in the pool - block_on(setup.pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap(); - - // fire notification - this should clean up the queue - assert_eq!(setup.pool.status().ready, 1); - block_on(LightBasicPoolMaintainer::with_defaults(setup.pool.clone(), setup.client.clone(), fetcher).maintain( - &BlockId::Number(0), - &[], - )); - - // then - assert_eq!(setup.pool.status().ready, 0); - assert_eq!(setup.pool.status().future, 0); - } - - #[test] - fn should_schedule_transactions_revalidation_at_light_pool() { - // when revalidation is not scheduled, it became scheduled - let mut status = TxPoolRevalidationStatus::NotScheduled; - assert!(!status.is_required(10u32, None, None)); - match status { - TxPoolRevalidationStatus::Scheduled(_, _) => (), - _ => panic!("Unexpected status: {:?}", status), - } - - // revalidation required at time - let mut status = TxPoolRevalidationStatus::Scheduled(Some(Instant::now()), None); - assert!(status.is_required(10u32, None, None)); - match status { - TxPoolRevalidationStatus::InProgress => (), - _ => panic!("Unexpected status: {:?}", status), - } - - // revalidation required at block - let mut status = TxPoolRevalidationStatus::Scheduled(None, Some(10)); - assert!(status.is_required(10u32, None, None)); - match status { - TxPoolRevalidationStatus::InProgress => (), - _ => panic!("Unexpected status: {:?}", status), - } - } - - #[test] - fn should_revalidate_transactions_at_light_pool() { - use std::sync::atomic; - use sp_runtime::transaction_validity::*; - - let build_fetcher = || { - let validated = Arc::new(atomic::AtomicBool::new(false)); - Arc::new(substrate_test_runtime_client::new_light_fetcher() - .with_remote_body(Some(Box::new(move |_| Ok(vec![])))) - .with_remote_call(Some(Box::new(move |_| { - let is_inserted = validated.swap(true, atomic::Ordering::SeqCst); - let validity: TransactionValidity = if is_inserted { - Err(TransactionValidityError::Invalid( - InvalidTransaction::Custom(0) - )) - } else { - Ok(ValidTransaction { - priority: 0, - requires: Vec::new(), - provides: vec![vec![42]], - longevity: 0, - propagate: true, - }) - }; - Ok(validity.encode()) - })))) - }; - - fn with_fetcher_maintain + 'static>( - fetcher: Arc, - revalidate_time_period: Option, - revalidate_block_period: Option, - prepare_maintainer: impl Fn(&Mutex>), - ) -> PoolStatus { - let setup = setup_light(fetcher.clone()); - let best = setup.longest_chain.best_chain().unwrap(); - - // let's prepare maintainer - let maintainer = LightBasicPoolMaintainer::new( - setup.pool.clone(), - setup.client.clone(), - fetcher, - revalidate_time_period, - revalidate_block_period, - ); - prepare_maintainer(&*maintainer.revalidation_status); - - // store the transaction in the pool - block_on(setup.pool.submit_one( - &BlockId::hash(best.hash()), - Transfer { - amount: 5, - nonce: 0, - from: AccountKeyring::Alice.into(), - to: Default::default(), - }.into_signed_tx(), - )).unwrap(); - - // and run maintain procedures - block_on(maintainer.maintain(&BlockId::Number(0), &[])); - - setup.pool.status() - } - - // when revalidation is never required - nothing happens - let fetcher = build_fetcher(); - //let maintainer = DefaultLightTransactionPoolMaintainer::new(client.clone(), fetcher.clone(), None, None); - let status = with_fetcher_maintain(fetcher, None, None, |_revalidation_status| {}); - assert_eq!(status.ready, 1); - - // when revalidation is scheduled by time - it is performed - let fetcher = build_fetcher(); - let status = with_fetcher_maintain(fetcher, None, None, |revalidation_status| - *revalidation_status.lock() = TxPoolRevalidationStatus::Scheduled(Some(Instant::now()), None) - ); - assert_eq!(status.ready, 0); - - // when revalidation is scheduled by block number - it is performed - let fetcher = build_fetcher(); - let status = with_fetcher_maintain(fetcher, None, None, |revalidation_status| - *revalidation_status.lock() = TxPoolRevalidationStatus::Scheduled(None, Some(0)) - ); - assert_eq!(status.ready, 0); - } - - #[test] - fn should_add_reverted_transactions_to_the_pool() { - let mut setup = setup(); - - let transaction = Transfer { - amount: 5, - nonce: 0, - from: AccountKeyring::Alice.into(), - to: Default::default(), - }.into_signed_tx(); - let best = setup.longest_chain.best_chain().unwrap(); - - // store the transaction in the pool - block_on(setup.pool.submit_one(&BlockId::hash(best.hash()), transaction.clone())).unwrap(); - - // import the block - let mut builder = setup.client.new_block(Default::default()).unwrap(); - builder.push(transaction.clone()).unwrap(); - let block = builder.build().unwrap().block; - let block1_hash = block.header().hash(); - let id = BlockId::hash(block1_hash.clone()); - setup.client.import(BlockOrigin::Own, block).unwrap(); - - // fire notification - this should clean up the queue - assert_eq!(setup.pool.status().ready, 1); - block_on(FullBasicPoolMaintainer::new(setup.pool.clone(), setup.client.clone()).maintain(&id, &[])); - - // then - assert_eq!(setup.pool.status().ready, 0); - assert_eq!(setup.pool.status().future, 0); - - // import second block - let builder = setup.client.new_block_at( - &BlockId::hash(best.hash()), - Default::default(), - false, - ).unwrap(); - let block = builder.build().unwrap().block; - let id = BlockId::hash(block.header().hash()); - setup.client.import(BlockOrigin::Own, block).unwrap(); - - // fire notification - this should add the transaction back to the pool. - block_on(FullBasicPoolMaintainer::new(setup.pool.clone(), setup.client.clone()).maintain(&id, &[block1_hash])); - - // then - assert_eq!(setup.pool.status().ready, 1); - assert_eq!(setup.pool.status().future, 0); - } -} diff --git a/primitives/core/src/tests.rs b/client/transaction-pool/src/testing/mod.rs similarity index 86% rename from primitives/core/src/tests.rs rename to client/transaction-pool/src/testing/mod.rs index 1eda2157c410686d37f14fae6e2af4d6b008db84..a8f40c6b6475b57656bb0f373851f2162f283be5 100644 --- a/primitives/core/src/tests.rs +++ b/client/transaction-pool/src/testing/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,4 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Tests. +//! Tests for top-level transaction pool api + +mod pool; diff --git a/client/transaction-pool/src/tests.rs b/client/transaction-pool/src/testing/pool.rs similarity index 54% rename from client/transaction-pool/src/tests.rs rename to client/transaction-pool/src/testing/pool.rs index 1199e41cf8740f1ffd05be970c3ae70028582b0a..fed02067b184aa0321fe84f7c3c14ee4b772e4bd 100644 --- a/client/transaction-pool/src/tests.rs +++ b/client/transaction-pool/src/testing/pool.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,113 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . - -use super::*; - -use codec::Encode; +use crate::*; +use sc_transaction_graph::Pool; use futures::executor::block_on; -use sc_transaction_graph::{self, Pool}; -use substrate_test_runtime_client::{runtime::{AccountId, Block, Hash, Index, Extrinsic, Transfer}, AccountKeyring::{self, *}}; use sp_runtime::{ - generic::{self, BlockId}, - traits::{Hash as HashT, BlakeTwo256}, - transaction_validity::{TransactionValidity, ValidTransaction}, + generic::BlockId, + transaction_validity::ValidTransaction, }; +use substrate_test_runtime_client::{ + runtime::{Block, Hash, Index}, + AccountKeyring::*, +}; +use substrate_test_runtime_transaction_pool::{TestApi, uxt}; -struct TestApi { - pub modifier: Box, -} - -impl TestApi { - fn default() -> Self { - TestApi { - modifier: Box::new(|_| {}), - } - } -} - -impl sc_transaction_graph::ChainApi for TestApi { - type Block = Block; - type Hash = Hash; - type Error = error::Error; - type ValidationFuture = futures::future::Ready>; - - fn validate_transaction( - &self, - at: &BlockId, - uxt: sc_transaction_graph::ExtrinsicFor, - ) -> Self::ValidationFuture { - let expected = index(at); - let requires = if expected == uxt.transfer().nonce { - vec![] - } else { - vec![vec![uxt.transfer().nonce as u8 - 1]] - }; - let provides = vec![vec![uxt.transfer().nonce as u8]]; - - let mut validity = ValidTransaction { - priority: 1, - requires, - provides, - longevity: 64, - propagate: true, - }; - - (self.modifier)(&mut validity); - - futures::future::ready(Ok( - Ok(validity) - )) - } - - fn block_id_to_number(&self, at: &BlockId) -> error::Result>> { - Ok(Some(number_of(at))) - } - - fn block_id_to_hash(&self, at: &BlockId) -> error::Result>> { - Ok(match at { - generic::BlockId::Hash(x) => Some(x.clone()), - _ => Some(Default::default()), - }) - } - - fn hash_and_length(&self, ex: &sc_transaction_graph::ExtrinsicFor) -> (Self::Hash, usize) { - let encoded = ex.encode(); - (BlakeTwo256::hash(&encoded), encoded.len()) - } - -} - -fn index(at: &BlockId) -> u64 { - 209 + number_of(at) -} - -fn number_of(at: &BlockId) -> u64 { - match at { - generic::BlockId::Number(n) => *n as u64, - _ => 0, - } -} - -fn uxt(who: AccountKeyring, nonce: Index) -> Extrinsic { - let transfer = Transfer { - from: who.into(), - to: AccountId::default(), - nonce, - amount: 1, - }; - let signature = transfer.using_encoded(|e| who.sign(e)); - Extrinsic::Transfer(transfer, signature.into()) +fn pool() -> Pool { + Pool::new(Default::default(), TestApi::with_alice_nonce(209).into()) } -fn pool() -> Pool { - Pool::new(Default::default(), TestApi::default()) +fn maintained_pool() -> BasicPool { + BasicPool::new(Default::default(), std::sync::Arc::new(TestApi::with_alice_nonce(209))) } #[test] fn submission_should_work() { let pool = pool(); - assert_eq!(209, index(&BlockId::number(0))); block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).unwrap(); let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); @@ -162,13 +79,19 @@ fn late_nonce_should_be_queued() { #[test] fn prune_tags_should_work() { let pool = pool(); - block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).unwrap(); + let hash209 = block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).unwrap(); block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 210))).unwrap(); let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); assert_eq!(pending, vec![209, 210]); - block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![])).unwrap(); + block_on( + pool.prune_tags( + &BlockId::number(1), + vec![vec![209]], + vec![hash209], + ) + ).expect("Prune tags"); let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); assert_eq!(pending, vec![210]); @@ -192,22 +115,24 @@ fn should_ban_invalid_transactions() { #[test] fn should_correctly_prune_transactions_providing_more_than_one_tag() { - let mut api = TestApi::default(); - api.modifier = Box::new(|v: &mut ValidTransaction| { + let api = Arc::new(TestApi::with_alice_nonce(209)); + api.set_valid_modifier(Box::new(|v: &mut ValidTransaction| { v.provides.push(vec![155]); - }); - let pool = Pool::new(Default::default(), api); + })); + let pool = Pool::new(Default::default(), api.clone()); let xt = uxt(Alice, 209); block_on(pool.submit_one(&BlockId::number(0), xt.clone())).expect("1. Imported"); assert_eq!(pool.status().ready, 1); // remove the transaction that just got imported. + api.increment_nonce(Alice.into()); block_on(pool.prune_tags(&BlockId::number(1), vec![vec![209]], vec![])).expect("1. Pruned"); assert_eq!(pool.status().ready, 0); // it's re-imported to future assert_eq!(pool.status().future, 1); // so now let's insert another transaction that also provides the 155 + api.increment_nonce(Alice.into()); let xt = uxt(Alice, 211); block_on(pool.submit_one(&BlockId::number(2), xt.clone())).expect("2. Imported"); assert_eq!(pool.status().ready, 1); @@ -216,7 +141,88 @@ fn should_correctly_prune_transactions_providing_more_than_one_tag() { assert_eq!(pending, vec![211]); // prune it and make sure the pool is empty + api.increment_nonce(Alice.into()); block_on(pool.prune_tags(&BlockId::number(3), vec![vec![155]], vec![])).expect("2. Pruned"); assert_eq!(pool.status().ready, 0); assert_eq!(pool.status().future, 2); } + +#[test] +fn should_prune_old_during_maintenance() { + let xt = uxt(Alice, 209); + + let pool = maintained_pool(); + + block_on(pool.submit_one(&BlockId::number(0), xt.clone())).expect("1. Imported"); + assert_eq!(pool.status().ready, 1); + + pool.api.push_block(1, vec![xt.clone()]); + + block_on(pool.maintain(&BlockId::number(1), &[])); + assert_eq!(pool.status().ready, 0); +} + +#[test] +fn should_revalidate_during_maintenance() { + let xt1 = uxt(Alice, 209); + let xt2 = uxt(Alice, 210); + + let pool = maintained_pool(); + block_on(pool.submit_one(&BlockId::number(0), xt1.clone())).expect("1. Imported"); + block_on(pool.submit_one(&BlockId::number(0), xt2.clone())).expect("2. Imported"); + assert_eq!(pool.status().ready, 2); + assert_eq!(pool.api.validation_requests().len(), 2); + + pool.api.push_block(1, vec![xt1.clone()]); + + block_on(pool.maintain(&BlockId::number(1), &[])); + assert_eq!(pool.status().ready, 1); + // test that pool revalidated transaction that left ready and not included in the block + assert_eq!(pool.api.validation_requests().len(), 3); +} + +#[test] +fn should_resubmit_from_retracted_during_maintaince() { + let xt = uxt(Alice, 209); + let retracted_hash = Hash::random(); + + let pool = maintained_pool(); + + block_on(pool.submit_one(&BlockId::number(0), xt.clone())).expect("1. Imported"); + assert_eq!(pool.status().ready, 1); + + pool.api.push_block(1, vec![]); + pool.api.push_fork_block(retracted_hash, vec![xt.clone()]); + + block_on(pool.maintain(&BlockId::number(1), &[retracted_hash])); + assert_eq!(pool.status().ready, 1); +} + +#[test] +fn should_not_retain_invalid_hashes_from_retracted() { + let xt = uxt(Alice, 209); + let retracted_hash = Hash::random(); + + let pool = maintained_pool(); + + block_on(pool.submit_one(&BlockId::number(0), xt.clone())).expect("1. Imported"); + assert_eq!(pool.status().ready, 1); + + pool.api.push_block(1, vec![]); + pool.api.push_fork_block(retracted_hash, vec![xt.clone()]); + pool.api.add_invalid(&xt); + + block_on(pool.maintain(&BlockId::number(1), &[retracted_hash])); + assert_eq!(pool.status().ready, 0); +} + +#[test] +fn can_track_heap_size() { + let pool = maintained_pool(); + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).expect("1. Imported"); + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 210))).expect("1. Imported"); + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 211))).expect("1. Imported"); + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 212))).expect("1. Imported"); + + assert!(parity_util_mem::malloc_size(&pool) > 3000); +} \ No newline at end of file diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index f371f21e1ab7e90df582ece618093d9aca5153f3..cd6b41c4c7a3c5523b0622c968b7f1fd4b9c055b 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-assets" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index f77eb663db871a006ea8d3c86617b15560c24665..8cdc9b9cc0fa8338670f4d7ab68db3689fa56af9 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -133,7 +133,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{Parameter, decl_module, decl_event, decl_storage, decl_error, ensure}; -use sp_runtime::traits::{Member, SimpleArithmetic, Zero, StaticLookup}; +use sp_runtime::traits::{Member, AtLeast32Bit, Zero, StaticLookup}; use frame_system::{self as system, ensure_signed}; use sp_runtime::traits::One; @@ -143,10 +143,10 @@ pub trait Trait: frame_system::Trait { type Event: From> + Into<::Event>; /// The units in which we record balances. - type Balance: Member + Parameter + SimpleArithmetic + Default + Copy; + type Balance: Member + Parameter + AtLeast32Bit + Default + Copy; /// The arithmetic type of asset identifier. - type AssetId: Parameter + SimpleArithmetic + Default + Copy; + type AssetId: Parameter + AtLeast32Bit + Default + Copy; } decl_module! { @@ -228,11 +228,11 @@ decl_error! { decl_storage! { trait Store for Module as Assets { /// The number of units of assets held by any given account. - Balances: map (T::AssetId, T::AccountId) => T::Balance; + Balances: map hasher(blake2_256) (T::AssetId, T::AccountId) => T::Balance; /// The next asset identifier up for grabs. NextAssetId get(fn next_asset_id): T::AssetId; /// The total unit supply of an asset. - TotalSupply: map T::AssetId => T::Balance; + TotalSupply: map hasher(blake2_256) T::AssetId => T::Balance; } } @@ -293,6 +293,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { type Event = (); diff --git a/frame/aura/Cargo.toml b/frame/aura/Cargo.toml index 26754458321fd19ca2284ed7e6d2d2579799c7a3..e982a02b47c8db39d755af9739123c4f909562a0 100644 --- a/frame/aura/Cargo.toml +++ b/frame/aura/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-aura" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } @@ -23,7 +24,7 @@ pallet-timestamp = { version = "2.0.0", default-features = false, path = "../tim [dev-dependencies] lazy_static = "1.4.0" -parking_lot = "0.9.0" +parking_lot = "0.10.0" [features] default = ["std"] diff --git a/frame/aura/src/lib.rs b/frame/aura/src/lib.rs index 1a711f314abcdee5d7fa4c15f1c652741f71dccd..e2aafde9efe6ef699bcd61ef861fe7104dd7e331 100644 --- a/frame/aura/src/lib.rs +++ b/frame/aura/src/lib.rs @@ -33,8 +33,6 @@ //! //! - [Timestamp](../pallet_timestamp/index.html): The Timestamp module is used in Aura to track //! consensus rounds (via `slots`). -//! - [Consensus](../frame_consensus/index.html): The Consensus module does not relate directly to Aura, -//! but serves to manage offline reporting by implementing `ProvideInherent` in a similar way. //! //! ## References //! diff --git a/frame/aura/src/mock.rs b/frame/aura/src/mock.rs index 81366cf0842d62a587b6b35b75fb5c2a1254f17a..5b3ccd7745f8cc9cd261c45ebe7a8e21ca691176 100644 --- a/frame/aura/src/mock.rs +++ b/frame/aura/src/mock.rs @@ -61,6 +61,9 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl pallet_timestamp::Trait for Test { diff --git a/frame/authority-discovery/Cargo.toml b/frame/authority-discovery/Cargo.toml index 3760d863d70ad1f2d0e02af4dcd23c84ddd42dd3..d8c72683cac786067793c4883d167f567dc15c18 100644 --- a/frame/authority-discovery/Cargo.toml +++ b/frame/authority-discovery/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-authority-discovery" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-authority-discovery = { version = "2.0.0", default-features = false, path = "../../primitives/authority-discovery" } diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index c4270433970755edb2afce2275a372438dc30bc8..77906e1bfe34f7e12bf9c7cb03978f71128906bb 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -103,7 +103,6 @@ mod tests { use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; type AuthorityDiscovery = Module; - type SessionIndex = u32; #[derive(Clone, Eq, PartialEq)] pub struct Test; @@ -158,6 +157,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl_outer_origin! { diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index 298da65a2b02bd012e9533f2cdc30e29102afffc..124d66c1bc33f25cfab28fb3a28dc3f262c182df 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" description = "Block and Uncle Author tracking for the SRML" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index 216f5c729a9c4be709442245b28128bcbf69e700..f2dbd4674f97c4ed69d9387aa3458b1692e20ea8 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -431,6 +431,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index 30dbda4e53c2933ab3549af45cb6dce2de48c706..5e7764862a66b4c58f5b9fcb57e58a6325d97659 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-babe" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] hex-literal = "0.2.1" @@ -22,7 +23,7 @@ sp-io ={ path = "../../primitives/io", default-features = false } [dev-dependencies] lazy_static = "1.4.0" -parking_lot = "0.9.0" +parking_lot = "0.10.0" sp-version = { version = "2.0.0", default-features = false, path = "../../primitives/version" } sp-core = { version = "2.0.0", path = "../../primitives/core" } substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index f249ca9e299bf6e34412e90c62195c0b82d9b6be..5921b1ba20325b5ea1dcb913dbd2194e5e11c703 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -23,10 +23,10 @@ pub use pallet_timestamp; use sp_std::{result, prelude::*}; -use frame_support::{decl_storage, decl_module, traits::FindAuthor, traits::Get}; +use frame_support::{decl_storage, decl_module, traits::{FindAuthor, Get, Randomness as RandomnessT}}; use sp_timestamp::OnTimestampSet; -use sp_runtime::{generic::DigestItem, ConsensusEngineId, Perbill}; -use sp_runtime::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon}; +use sp_runtime::{generic::DigestItem, ConsensusEngineId, Perbill, PerThing}; +use sp_runtime::traits::{IsMember, SaturatedConversion, Saturating, Hash}; use sp_staking::{ SessionIndex, offence::{Offence, Kind}, @@ -35,8 +35,9 @@ use sp_staking::{ use codec::{Encode, Decode}; use sp_inherents::{InherentIdentifier, InherentData, ProvideInherent, MakeFatalError}; use sp_consensus_babe::{ - BABE_ENGINE_ID, ConsensusLog, BabeAuthorityWeight, NextEpochDescriptor, RawBabePreDigest, - SlotNumber, inherents::{INHERENT_IDENTIFIER, BabeInherentData} + BABE_ENGINE_ID, ConsensusLog, BabeAuthorityWeight, SlotNumber, + inherents::{INHERENT_IDENTIFIER, BabeInherentData}, + digests::{NextEpochDescriptor, RawPreDigest}, }; pub use sp_consensus_babe::{AuthorityId, VRF_OUTPUT_LENGTH, PUBLIC_KEY_LENGTH}; @@ -145,7 +146,7 @@ decl_storage! { /// We reset all segments and return to `0` at the beginning of every /// epoch. SegmentIndex build(|_| 0): u32; - UnderConstruction: map u32 => Vec<[u8; 32 /* VRF_OUTPUT_LENGTH */]>; + UnderConstruction: map hasher(blake2_256) u32 => Vec<[u8; 32 /* VRF_OUTPUT_LENGTH */]>; /// Temporary value (cleared at block finalization) which is `Some` /// if per-block initialization has already been called for current block. @@ -190,9 +191,13 @@ decl_module! { } } -impl RandomnessBeacon for Module { - fn random() -> [u8; VRF_OUTPUT_LENGTH] { - Self::randomness() +impl RandomnessT<::Hash> for Module { + fn random(subject: &[u8]) -> T::Hash { + let mut subject = subject.to_vec(); + subject.reserve(VRF_OUTPUT_LENGTH); + subject.extend_from_slice(&Self::randomness()[..]); + + ::Hashing::hash(&subject[..]) } } @@ -205,11 +210,11 @@ impl FindAuthor for Module { { for (id, mut data) in digests.into_iter() { if id == BABE_ENGINE_ID { - let pre_digest = RawBabePreDigest::decode(&mut data).ok()?; + let pre_digest = RawPreDigest::decode(&mut data).ok()?; return Some(match pre_digest { - RawBabePreDigest::Primary { authority_index, .. } => + RawPreDigest::Primary { authority_index, .. } => authority_index, - RawBabePreDigest::Secondary { authority_index, .. } => + RawPreDigest::Secondary { authority_index, .. } => authority_index, }); } @@ -397,7 +402,7 @@ impl Module { .iter() .filter_map(|s| s.as_pre_runtime()) .filter_map(|(id, mut data)| if id == BABE_ENGINE_ID { - RawBabePreDigest::decode(&mut data).ok() + RawPreDigest::decode(&mut data).ok() } else { None }) @@ -424,7 +429,7 @@ impl Module { CurrentSlot::put(digest.slot_number()); - if let RawBabePreDigest::Primary { vrf_output, .. } = digest { + if let RawPreDigest::Primary { vrf_output, .. } = digest { // place the VRF output into the `Initialized` storage item // and it'll be put onto the under-construction randomness // later, once we've decided which epoch this block is in. diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index e65f305dc4de2b17a4b7be94c07d1ae8e292e4ab..efb5570c1db7b3d9da7fe95db6ad8799e153d4f2 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -20,7 +20,7 @@ use super::{Trait, Module, GenesisConfig}; use sp_consensus_babe::AuthorityId; use sp_runtime::{ - traits::IdentityLookup, Perbill, testing::{Header, UintAuthorityId}, impl_opaque_keys, + traits::IdentityLookup, Perbill, PerThing, testing::{Header, UintAuthorityId}, impl_opaque_keys, }; use sp_version::RuntimeVersion; use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; @@ -66,6 +66,9 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl_opaque_keys! { diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index dbd61238166b0eb3544d0e849d1de43ca8fb8c85..976a264d7ba7bb24f29c556ec92822a728598075 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -34,7 +34,7 @@ fn make_pre_digest( vrf_output: [u8; sp_consensus_babe::VRF_OUTPUT_LENGTH], vrf_proof: [u8; sp_consensus_babe::VRF_PROOF_LENGTH], ) -> Digest { - let digest_data = sp_consensus_babe::RawBabePreDigest::Primary { + let digest_data = sp_consensus_babe::digests::RawPreDigest::Primary { authority_index, slot_number, vrf_output, @@ -110,7 +110,7 @@ fn first_block_epoch_zero_start() { let authorities = Babe::authorities(); let consensus_log = sp_consensus_babe::ConsensusLog::NextEpochData( - sp_consensus_babe::NextEpochDescriptor { + sp_consensus_babe::digests::NextEpochDescriptor { authorities, randomness: Babe::randomness(), } diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index f9522b5379ff5fcbbe627b21ffe506be278aed2e..871290b182fdd0ec6e177ac0f1b1cf9557929289 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -3,11 +3,13 @@ name = "pallet-balances" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } @@ -23,7 +25,8 @@ std = [ "serde", "codec/std", "sp-std/std", - "frame-support/std", + "sp-io/std", "sp-runtime/std", + "frame-support/std", "frame-system/std", ] diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..605561d5975995d6099a6c24e7ac4cac46cb932d --- /dev/null +++ b/frame/balances/src/benchmarking.rs @@ -0,0 +1,321 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Balances pallet benchmarking. + +use super::*; + +use frame_system::RawOrigin; +use sp_io::hashing::blake2_256; +use sp_runtime::{BenchmarkResults, BenchmarkParameter}; +use sp_runtime::traits::{Bounded, Benchmarking, BenchmarkingSetup, Dispatchable}; + +use crate::Module as Balances; + +// Support Functions +fn account(name: &'static str, index: u32) -> T::AccountId { + let entropy = (name, index).using_encoded(blake2_256); + T::AccountId::decode(&mut &entropy[..]).unwrap_or_default() +} + +// Benchmark `transfer` extrinsic with the worst possible conditions: +// * Transfer will kill the sender account. +// * Transfer will create the recipient account. +struct Transfer; +impl BenchmarkingSetup, RawOrigin> for Transfer { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Existential Deposit Multiplier + (BenchmarkParameter::E, 2, 1000), + // User Seed + (BenchmarkParameter::U, 1, 1000), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Constants + let ed = T::ExistentialDeposit::get(); + + // Select an account + let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; + let user = account::("user", u); + let user_origin = RawOrigin::Signed(user.clone()); + + // Give some multiple of the existential deposit + creation fee + transfer fee + let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1; + let balance = ed.saturating_mul(e.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance); + + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. + let recipient = account::("recipient", u); + let recipient_lookup: ::Source = T::Lookup::unlookup(recipient); + let transfer_amt = ed.saturating_mul((e - 1).into()) + 1.into(); + + // Return the `transfer` call + Ok((crate::Call::::transfer(recipient_lookup, transfer_amt), user_origin)) + } +} + +// Benchmark `transfer` with the best possible condition: +// * Both accounts exist and will continue to exist. +struct TransferBestCase; +impl BenchmarkingSetup, RawOrigin> for TransferBestCase { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Existential Deposit Multiplier + (BenchmarkParameter::E, 2, 1000), + // User Seed + (BenchmarkParameter::U, 1, 1000), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Constants + let ed = T::ExistentialDeposit::get(); + + // Select a sender + let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; + let user = account::("user", u); + let user_origin = RawOrigin::Signed(user.clone()); + + // Select a recipient + let recipient = account::("recipient", u); + let recipient_lookup: ::Source = T::Lookup::unlookup(recipient.clone()); + + // Get the existential deposit multiplier + let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1; + + // Give the sender account max funds for transfer (their account will never reasonably be killed). + let _ = as Currency<_>>::make_free_balance_be(&user, T::Balance::max_value()); + + // Give the recipient account existential deposit (thus their account already exists). + let _ = as Currency<_>>::make_free_balance_be(&recipient, ed); + + // Transfer e * existential deposit. + let transfer_amt = ed.saturating_mul(e.into()); + + // Return the `transfer` call + Ok((crate::Call::::transfer(recipient_lookup, transfer_amt), user_origin)) + } +} + +// Benchmark `transfer_keep_alive` with the worst possible condition: +// * The recipient account is created. +struct TransferKeepAlive; +impl BenchmarkingSetup, RawOrigin> for TransferKeepAlive { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Existential Deposit Multiplier + (BenchmarkParameter::E, 2, 1000), + // User Seed + (BenchmarkParameter::U, 1, 1000), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Constants + let ed = T::ExistentialDeposit::get(); + + // Select a sender + let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; + let user = account::("user", u); + let user_origin = RawOrigin::Signed(user.clone()); + + // Select a recipient + let recipient = account::("recipient", u); + let recipient_lookup: ::Source = T::Lookup::unlookup(recipient.clone()); + + // Get the existential deposit multiplier + let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1; + + // Give the sender account max funds, thus a transfer will not kill account. + let _ = as Currency<_>>::make_free_balance_be(&user, T::Balance::max_value()); + + // Transfer e * existential deposit. + let transfer_amt = ed.saturating_mul(e.into()); + + // Return the `transfer_keep_alive` call + Ok((crate::Call::::transfer_keep_alive(recipient_lookup, transfer_amt), user_origin)) + } +} + +// Benchmark `set_balance` coming from ROOT account. This always creates an account. +struct SetBalance; +impl BenchmarkingSetup, RawOrigin> for SetBalance { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Existential Deposit Multiplier + (BenchmarkParameter::E, 2, 1000), + // User Seed + (BenchmarkParameter::U, 1, 1000), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Constants + let ed = T::ExistentialDeposit::get(); + + // Select a sender + let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; + let user = account::("user", u); + let user_lookup: ::Source = T::Lookup::unlookup(user.clone()); + + // Get the existential deposit multiplier for free and reserved + let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1; + let balance_amt = ed.saturating_mul(e.into()); + + // Return the `set_balance` call + Ok((crate::Call::::set_balance(user_lookup, balance_amt, balance_amt), RawOrigin::Root)) + } +} + +// Benchmark `set_balance` coming from ROOT account. This always kills an account. +struct SetBalanceKilling; +impl BenchmarkingSetup, RawOrigin> for SetBalanceKilling { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Existential Deposit Multiplier + (BenchmarkParameter::E, 2, 1000), + // User Seed + (BenchmarkParameter::U, 1, 1000), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Constants + let ed = T::ExistentialDeposit::get(); + + // Select a sender + let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; + let user = account::("user", u); + let user_lookup: ::Source = T::Lookup::unlookup(user.clone()); + + // Get the existential deposit multiplier for free and reserved + let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1; + // Give the user some initial balance + let balance_amt = ed.saturating_mul(e.into()); + let _ = as Currency<_>>::make_free_balance_be(&user, balance_amt); + + // Return the `set_balance` call that will kill the account + Ok((crate::Call::::set_balance(user_lookup, 0.into(), 0.into()), RawOrigin::Root)) + } +} + +// The list of available benchmarks for this pallet. +enum SelectedBenchmark { + Transfer, + TransferBestCase, + TransferKeepAlive, + SetBalance, + SetBalanceKilling, +} + +// Allow us to select a benchmark from the list of available benchmarks. +impl BenchmarkingSetup, RawOrigin> for SelectedBenchmark { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + match self { + Self::Transfer => , RawOrigin>>::components(&Transfer), + Self::TransferBestCase => , RawOrigin>>::components(&TransferBestCase), + Self::TransferKeepAlive => , RawOrigin>>::components(&TransferKeepAlive), + Self::SetBalance => , RawOrigin>>::components(&SetBalance), + Self::SetBalanceKilling => , RawOrigin>>::components(&SetBalanceKilling), + } + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + match self { + Self::Transfer => , RawOrigin>>::instance(&Transfer, components), + Self::TransferBestCase => , RawOrigin>>::instance(&TransferBestCase, components), + Self::TransferKeepAlive => , RawOrigin>>::instance(&TransferKeepAlive, components), + Self::SetBalance => , RawOrigin>>::instance(&SetBalance, components), + Self::SetBalanceKilling => , RawOrigin>>::instance(&SetBalanceKilling, components), + } + } +} + +impl Benchmarking for Module { + fn run_benchmark(extrinsic: Vec, steps: u32, repeat: u32) -> Result, &'static str> { + // Map the input to the selected benchmark. + let selected_benchmark = match extrinsic.as_slice() { + b"transfer" => SelectedBenchmark::Transfer, + b"transfer_best_case" => SelectedBenchmark::TransferBestCase, + b"transfer_keep_alive" => SelectedBenchmark::TransferKeepAlive, + b"set_balance" => SelectedBenchmark::SetBalance, + b"set_balance_killing" => SelectedBenchmark::SetBalanceKilling, + _ => return Err("Could not find extrinsic."), + }; + + // Warm up the DB + sp_io::benchmarking::commit_db(); + sp_io::benchmarking::wipe_db(); + + let components = , RawOrigin>>::components(&selected_benchmark); + // results go here + let mut results: Vec = Vec::new(); + // Select the component we will be benchmarking. Each component will be benchmarked. + for (name, low, high) in components.iter() { + // Create up to `STEPS` steps for that component between high and low. + let step_size = ((high - low) / steps).max(1); + let num_of_steps = (high - low) / step_size; + for s in 0..num_of_steps { + // This is the value we will be testing for component `name` + let component_value = low + step_size * s; + + // Select the mid value for all the other components. + let c: Vec<(BenchmarkParameter, u32)> = components.iter() + .map(|(n, l, h)| + (*n, if n == name { component_value } else { (h - l) / 2 + l }) + ).collect(); + + // Run the benchmark `repeat` times. + for _r in 0..repeat { + // Set up the externalities environment for the setup we want to benchmark. + let (call, caller) = , RawOrigin>>::instance(&selected_benchmark, &c)?; + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + sp_io::benchmarking::commit_db(); + // Run the benchmark. + let start = sp_io::benchmarking::current_time(); + call.dispatch(caller.clone().into())?; + let finish = sp_io::benchmarking::current_time(); + let elapsed = finish - start; + sp_std::if_std!{ + if let RawOrigin::Signed(who) = caller.clone() { + let balance = Account::::get(&who).free; + println!("Free Balance {:?}", balance); + } + } + results.push((c.clone(), elapsed)); + // Wipe the DB back to the genesis state. + sp_io::benchmarking::wipe_db(); + } + } + } + return Ok(results); + } +} diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index b488b96701cf1a1af049895532b4daf8f340a140..fc2294dd5113f16e8a2e32e68e284c61b714c552 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -38,37 +38,31 @@ //! ### Terminology //! //! - **Existential Deposit:** The minimum balance required to create or keep an account open. This prevents -//! "dust accounts" from filling storage. -//! - **Total Issuance:** The total number of units in existence in a system. -//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its balance is set -//! to zero. -//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only -//! balance that matters for most operations. When this balance falls below the existential -//! deposit, most functionality of the account is removed. When both it and the reserved balance -//! are deleted, then the account is said to be dead. -//! -//! No account should ever have a free balance that is strictly between 0 and the existential +//! "dust accounts" from filling storage. When the free plus the reserved balance (i.e. the total balance) +//! fall below this, then the account is said to be dead; and it loses its functionality as well as any +//! prior history and all information on it is removed from the chain's state. +//! No account should ever have a total balance that is strictly between 0 and the existential //! deposit (exclusive). If this ever happens, it indicates either a bug in this module or an //! erroneous raw mutation of storage. //! +//! - **Total Issuance:** The total number of units in existence in a system. +//! +//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its +//! total balance has become zero (or, strictly speaking, less than the Existential Deposit). +//! +//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only +//! balance that matters for most operations. +//! //! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. //! Reserved balance can still be slashed, but only after all the free balance has been slashed. -//! If the reserved balance falls below the existential deposit, it and any related functionality -//! will be deleted. When both it and the free balance are deleted, then the account is said to -//! be dead. -//! -//! No account should ever have a reserved balance that is strictly between 0 and the existential -//! deposit (exclusive). If this ever happens, it indicates either a bug in this module or an -//! erroneous raw mutation of storage. //! //! - **Imbalance:** A condition when some funds were credited or debited without equal and opposite accounting //! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will //! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is //! simply dropped, it should automatically maintain any book-keeping such as total issuance.) +//! //! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple //! locks always operate over the same funds, so they "overlay" rather than "stack". -//! - **Vesting:** Similar to a lock, this is another, but independent, liquidity restriction that reduces linearly -//! over time. //! //! ### Implementations //! @@ -94,10 +88,6 @@ //! - `transfer` - Transfer some liquid free balance to another account. //! - `set_balance` - Set the balances of a given account. The origin of this call must be root. //! -//! ### Public Functions -//! -//! - `vesting_balance` - Get the amount that is currently being vested and cannot be transferred out of this account. -//! //! ## Usage //! //! The following examples show how to use the Balances module in your custom module. @@ -141,7 +131,6 @@ //! STAKING_ID, //! &ledger.stash, //! ledger.total, -//! T::BlockNumber::max_value(), //! WithdrawReasons::all() //! ); //! // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. @@ -159,87 +148,57 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod tests_local; +#[cfg(test)] +mod tests_composite; +#[cfg(test)] +#[macro_use] +mod tests; +mod benchmarking; + use sp_std::prelude::*; -use sp_std::{cmp, result, mem, fmt::Debug}; +use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr, convert::Infallible}; use codec::{Codec, Encode, Decode}; use frame_support::{ - StorageValue, Parameter, decl_event, decl_storage, decl_module, decl_error, - traits::{ - UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnReapAccount, OnUnbalanced, TryDrop, + StorageValue, Parameter, decl_event, decl_storage, decl_module, decl_error, ensure, + weights::SimpleDispatchInfo, traits::{ + Currency, OnReapAccount, OnUnbalanced, TryDrop, StoredMap, WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, - Imbalance, SignedImbalance, ReservableCurrency, Get, VestingCurrency, - }, - weights::SimpleDispatchInfo, + Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive, + ExistenceRequirement::AllowDeath, IsDeadAccount, BalanceStatus as Status + } }; use sp_runtime::{ RuntimeDebug, DispatchResult, DispatchError, traits::{ - Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, - Saturating, Bounded, + Zero, AtLeast32Bit, StaticLookup, Member, CheckedAdd, CheckedSub, + MaybeSerializeDeserialize, Saturating, Bounded, }, }; -use frame_system::{self as system, IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; - -#[cfg(test)] -mod mock; -#[cfg(test)] -mod tests; +use frame_system::{self as system, ensure_signed, ensure_root}; +use frame_support::storage::migration::{ + get_storage_value, take_storage_value, put_storage_value, StorageIterator +}; pub use self::imbalances::{PositiveImbalance, NegativeImbalance}; pub trait Subtrait: frame_system::Trait { /// The balance of an account. - type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + - MaybeSerializeDeserialize + Debug + From; - - /// A function that is invoked when the free-balance has fallen below the existential deposit and - /// has been reduced to zero. - /// - /// Gives a chance to clean up resources associated with the given account. - type OnFreeBalanceZero: OnFreeBalanceZero; - - /// A function that is invoked when the free-balance and the reserved-balance has fallen below - /// the existential deposit and both have been reduced to zero. - /// - /// All resources should be cleaned up all resources associated with the given account. - type OnReapAccount: OnReapAccount; - - /// Handler for when a new account is created. - type OnNewAccount: OnNewAccount; + type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy + + MaybeSerializeDeserialize + Debug; /// The minimum amount required to keep an account open. type ExistentialDeposit: Get; - /// The fee required to make a transfer. - type TransferFee: Get; - - /// The fee required to create an account. - type CreationFee: Get; + /// The means of storing the balances of an account. + type AccountStore: StoredMap>; } pub trait Trait: frame_system::Trait { /// The balance of an account. - type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + - MaybeSerializeDeserialize + Debug + From; - - /// A function that is invoked when the free-balance has fallen below the existential deposit and - /// has been reduced to zero. - /// - /// Gives a chance to clean up resources associated with the given account. - type OnFreeBalanceZero: OnFreeBalanceZero; - - /// A function that is invoked when the free-balance and the reserved-balance has fallen below - /// the existential deposit and both have been reduced to zero. - /// - /// All resources should be cleaned up all resources associated with the given account. - type OnReapAccount: OnReapAccount; - - /// Handler for when a new account is created. - type OnNewAccount: OnNewAccount; - - /// Handler for the unbalanced reduction when taking fees associated with balance - /// transfer (which may also include account creation). - type TransferPayment: OnUnbalanced>; + type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy + + MaybeSerializeDeserialize + Debug; /// Handler for the unbalanced reduction when removing a dust account. type DustRemoval: OnUnbalanced>; @@ -250,21 +209,14 @@ pub trait Trait: frame_system::Trait { /// The minimum amount required to keep an account open. type ExistentialDeposit: Get; - /// The fee required to make a transfer. - type TransferFee: Get; - - /// The fee required to create an account. - type CreationFee: Get; + /// The means of storing the balances of an account. + type AccountStore: StoredMap>; } impl, I: Instance> Subtrait for T { type Balance = T::Balance; - type OnFreeBalanceZero = T::OnFreeBalanceZero; - type OnReapAccount = T::OnReapAccount; - type OnNewAccount = T::OnNewAccount; type ExistentialDeposit = T::ExistentialDeposit; - type TransferFee = T::TransferFee; - type CreationFee = T::CreationFee; + type AccountStore = T::AccountStore; } decl_event!( @@ -272,12 +224,13 @@ decl_event!( ::AccountId, >::Balance { - /// A new account was created. - NewAccount(AccountId, Balance), - /// An account was reaped. - ReapedAccount(AccountId, Balance), - /// Transfer succeeded (from, to, value, fees). - Transfer(AccountId, AccountId, Balance, Balance), + /// An account was created with some free balance. + Endowed(AccountId, Balance), + /// An account was removed whose balance was non-zero but below ExistentialDeposit, + /// resulting in an outright loss. + DustLost(AccountId, Balance), + /// Transfer succeeded (from, to, value). + Transfer(AccountId, AccountId, Balance), /// A balance was set by root (who, free, reserved). BalanceSet(AccountId, Balance, Balance), /// Some amount was deposited (e.g. for transaction fees). @@ -306,40 +259,91 @@ decl_error! { } } -/// Struct to encode the vesting schedule of an individual account. -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct VestingSchedule { - /// Locked amount at genesis. - pub locked: Balance, - /// Amount that gets unlocked every block after `starting_block`. - pub per_block: Balance, - /// Starting block for unlocking(vesting). - pub starting_block: BlockNumber, +/// Simplified reasons for withdrawing balance. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] +pub enum Reasons { + /// Paying system transaction fees. + Fee = 0, + /// Any reason other than paying system transaction fees. + Misc = 1, + /// Any reason at all. + All = 2, } -impl VestingSchedule { - /// Amount locked at block `n`. - pub fn locked_at(&self, n: BlockNumber) -> Balance - where Balance: From - { - // Number of blocks that count toward vesting - // Saturating to 0 when n < starting_block - let vested_block_count = n.saturating_sub(self.starting_block); - // Return amount that is still locked in vesting - if let Some(x) = Balance::from(vested_block_count).checked_mul(&self.per_block) { - self.locked.max(x) - x +impl From for Reasons { + fn from(r: WithdrawReasons) -> Reasons { + if r == WithdrawReasons::from(WithdrawReason::TransactionPayment) { + Reasons::Fee + } else if r.contains(WithdrawReason::TransactionPayment) { + Reasons::All } else { - Zero::zero() + Reasons::Misc } } } +impl BitOr for Reasons { + type Output = Reasons; + fn bitor(self, other: Reasons) -> Reasons { + if self == other { return self } + Reasons::All + } +} + +/// A single lock on a balance. There can be many of these on an account and they "overlap", so the +/// same balance is frozen by multiple locks. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct BalanceLock { +pub struct BalanceLock { + /// An identifier for this lock. Only one lock may be in existence for each identifier. pub id: LockIdentifier, + /// The amount which the free balance may not drop below when this lock is in effect. pub amount: Balance, - pub until: BlockNumber, - pub reasons: WithdrawReasons, + /// If true, then the lock remains in effect even for payment of transaction fees. + pub reasons: Reasons, +} + +/// All balance information for an account. +#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug)] +pub struct AccountData { + /// Non-reserved part of the balance. There may still be restrictions on this, but it is the + /// total pool what may in principle be transferred, reserved and used for tipping. + /// + /// This is the only balance that matters in terms of most operations on tokens. It + /// alone is used to determine the balance when in the contract execution environment. + pub free: Balance, + /// Balance which is reserved and may not be used at all. + /// + /// This can still get slashed, but gets slashed last of all. + /// + /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens + /// that are still 'owned' by the account holder, but which are suspendable. + pub reserved: Balance, + /// The amount that `free` may not drop below when withdrawing for *anything except transaction + /// fee payment*. + pub misc_frozen: Balance, + /// The amount that `free` may not drop below when withdrawing specifically for transaction + /// fee payment. + pub fee_frozen: Balance, +} + +impl AccountData { + /// How much this account's balance can be reduced for the given `reasons`. + fn usable(&self, reasons: Reasons) -> Balance { + self.free.saturating_sub(self.frozen(reasons)) + } + /// The amount that this account's free balance may not be reduced beyond for the given + /// `reasons`. + fn frozen(&self, reasons: Reasons) -> Balance { + match reasons { + Reasons::All => self.misc_frozen.max(self.fee_frozen), + Reasons::Misc => self.misc_frozen, + Reasons::Fee => self.fee_frozen, + } + } + /// The total balance in this account including any that is reserved and ignoring any frozen. + fn total(&self) -> Balance { + self.free.saturating_add(self.reserved) + } } decl_storage! { @@ -349,75 +353,40 @@ decl_storage! { config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n) }): T::Balance; - /// Information regarding the vesting of a given account. - pub Vesting get(fn vesting) build(|config: &GenesisConfig| { - // Generate initial vesting configuration - // * who - Account which we are generating vesting configuration for - // * begin - Block when the account will start to vest - // * length - Number of blocks from `begin` until fully vested - // * liquid - Number of units which can be spent before vesting begins - config.vesting.iter().filter_map(|&(ref who, begin, length, liquid)| { - let length = >::from(length); - - config.balances.iter() - .find(|&&(ref w, _)| w == who) - .map(|&(_, balance)| { - // Total genesis `balance` minus `liquid` equals funds locked for vesting - let locked = balance.saturating_sub(liquid); - // Number of units unlocked per block after `begin` - let per_block = locked / length.max(sp_runtime::traits::One::one()); - - (who.clone(), VestingSchedule { - locked: locked, - per_block: per_block, - starting_block: begin - }) - }) - }).collect::>() - }): map T::AccountId => Option>; - - /// The 'free' balance of a given account. - /// - /// This is the only balance that matters in terms of most operations on tokens. It - /// alone is used to determine the balance when in the contract execution environment. When this - /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is - /// deleted: specifically `FreeBalance`. Further, the `OnFreeBalanceZero` callback - /// is invoked, giving a chance to external modules to clean up data associated with - /// the deleted account. - /// - /// `frame_system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets - /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. - pub FreeBalance get(fn free_balance) - build(|config: &GenesisConfig| config.balances.clone()): - map T::AccountId => T::Balance; - - /// The amount of the balance of a given account that is externally reserved; this can still get - /// slashed, but gets slashed last of all. + /// The balance of an account. /// - /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens - /// that are still 'owned' by the account holder, but which are suspendable. + /// NOTE: THIS MAY NEVER BE IN EXISTENCE AND YET HAVE A `total().is_zero()`. If the total + /// is ever zero, then the entry *MUST* be removed. /// - /// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account' - /// is deleted: specifically, `ReservedBalance`. - /// - /// `frame_system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets - /// collapsed to zero if it ever becomes less than `ExistentialDeposit`.) - pub ReservedBalance get(fn reserved_balance): map T::AccountId => T::Balance; + /// NOTE: This is only used in the case that this module is used to store balances. + pub Account: map hasher(blake2_256) T::AccountId => AccountData; /// Any liquidity locks on some account balances. - pub Locks get(fn locks): map T::AccountId => Vec>; + /// NOTE: Should only be accessed when setting, changing and freeing a lock. + pub Locks get(fn locks): map hasher(blake2_256) T::AccountId => Vec>; + + /// True if network has been upgraded to this version. + /// + /// True for new networks. + IsUpgraded build(|_: &GenesisConfig| true): bool; } add_extra_genesis { config(balances): Vec<(T::AccountId, T::Balance)>; - config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, T::Balance)>; // ^^ begin, length, amount liquid at genesis build(|config: &GenesisConfig| { + assert!( + >::ExistentialDeposit::get() > Zero::zero(), + "The existential deposit should be greater than zero." + ); for (_, balance) in &config.balances { assert!( *balance >= >::ExistentialDeposit::get(), "the balance of any account should always be more than existential deposit.", ) } + for &(ref who, free) in config.balances.iter() { + T::AccountStore::insert(who, AccountData { free, .. Default::default() }); + } }); } } @@ -429,12 +398,6 @@ decl_module! { /// The minimum amount required to keep an account open. const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get(); - /// The fee required to make a transfer. - const TransferFee: T::Balance = T::TransferFee::get(); - - /// The fee required to create an account. - const CreationFee: T::Balance = T::CreationFee::get(); - fn deposit_event() = default; /// Transfer some liquid free balance to another account. @@ -456,8 +419,7 @@ decl_module! { /// - `ensure_can_withdraw` is always called internally but has a bounded complexity. /// - Transferring balances to accounts that did not exist before will cause /// `T::OnNewAccount::on_new_account` to be called. - /// - Removing enough funds from an account will trigger - /// `T::DustRemoval::on_unbalanced` and `T::OnFreeBalanceZero::on_free_balance_zero`. + /// - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`. /// - `transfer_keep_alive` works the same way as `transfer`, but has an additional /// check that the transfer will not kill the origin account. /// @@ -497,26 +459,29 @@ decl_module! { let who = T::Lookup::lookup(who)?; let existential_deposit = T::ExistentialDeposit::get(); - let new_free = if new_free < existential_deposit { Zero::zero() } else { new_free }; - let new_reserved = if new_reserved < existential_deposit { Zero::zero() } else { new_reserved }; + let wipeout = new_free + new_reserved < existential_deposit; + let new_free = if wipeout { Zero::zero() } else { new_free }; + let new_reserved = if wipeout { Zero::zero() } else { new_reserved }; - let current_free = >::get(&who); - if new_free > current_free { - mem::drop(PositiveImbalance::::new(new_free - current_free)); - } else if new_free < current_free { - mem::drop(NegativeImbalance::::new(current_free - new_free)); - } - Self::set_free_balance(&who, new_free); + let (free, reserved) = Self::mutate_account(&who, |account| { + if new_free > account.free { + mem::drop(PositiveImbalance::::new(new_free - account.free)); + } else if new_free < account.free { + mem::drop(NegativeImbalance::::new(account.free - new_free)); + } - let current_reserved = >::get(&who); - if new_reserved > current_reserved { - mem::drop(PositiveImbalance::::new(new_reserved - current_reserved)); - } else if new_reserved < current_reserved { - mem::drop(NegativeImbalance::::new(current_reserved - new_reserved)); - } - Self::set_reserved_balance(&who, new_reserved); + if new_reserved > account.reserved { + mem::drop(PositiveImbalance::::new(new_reserved - account.reserved)); + } else if new_reserved < account.reserved { + mem::drop(NegativeImbalance::::new(account.reserved - new_reserved)); + } + + account.free = new_free; + account.reserved = new_reserved; - Self::deposit_event(RawEvent::BalanceSet(who, new_free, new_reserved)); + (account.free, account.reserved) + }); + Self::deposit_event(RawEvent::BalanceSet(who, free, reserved)); } /// Exactly as `transfer`, except the origin must be root and the source account may be @@ -548,123 +513,222 @@ decl_module! { ) { let transactor = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - >::transfer(&transactor, &dest, value, ExistenceRequirement::KeepAlive)?; + >::transfer(&transactor, &dest, value, KeepAlive)?; + } + + fn on_initialize() { + if !IsUpgraded::::get() { + IsUpgraded::::put(true); + Self::do_upgrade(); + } } + } +} + +#[derive(Decode)] +struct OldBalanceLock { + id: LockIdentifier, + amount: Balance, + until: BlockNumber, + reasons: WithdrawReasons, +} +impl OldBalanceLock { + fn upgraded(self) -> (BalanceLock, BlockNumber) { + (BalanceLock { + id: self.id, + amount: self.amount, + reasons: self.reasons.into(), + }, self.until) } } impl, I: Instance> Module { // PRIVATE MUTABLES - /// Set the reserved balance of an account to some new value. Will enforce `ExistentialDeposit` - /// law, annulling the account as needed. - /// - /// Doesn't do any preparatory work for creating a new account, so should only be used when it - /// is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - if balance < T::ExistentialDeposit::get() { - >::insert(who, balance); - Self::on_reserved_too_low(who); - UpdateBalanceOutcome::AccountKilled - } else { - >::insert(who, balance); - UpdateBalanceOutcome::Updated + // Upgrade from the pre-#4649 balances/vesting into the new balances. + pub fn do_upgrade() { + sp_runtime::print("Upgrading account balances..."); + // First, migrate from old FreeBalance to new Account. + // We also move all locks across since only accounts with FreeBalance values have locks. + // FreeBalance: map T::AccountId => T::Balance + for (hash, free) in StorageIterator::::new(b"Balances", b"FreeBalance").drain() { + let mut account = AccountData { free, ..Default::default() }; + // Locks: map T::AccountId => Vec + let old_locks = get_storage_value::>>(b"Balances", b"Locks", &hash); + if let Some(locks) = old_locks { + let locks = locks.into_iter() + .map(|i| { + let (result, expiry) = i.upgraded(); + if expiry != T::BlockNumber::max_value() { + // Any `until`s that are not T::BlockNumber::max_value come from + // democracy and need to be migrated over there. + // Democracy: Locks get(locks): map T::AccountId => Option; + put_storage_value(b"Democracy", b"Locks", &hash, expiry); + } + result + }) + .collect::>(); + for l in locks.iter() { + if l.reasons == Reasons::All || l.reasons == Reasons::Misc { + account.misc_frozen = account.misc_frozen.max(l.amount); + } + if l.reasons == Reasons::All || l.reasons == Reasons::Fee { + account.fee_frozen = account.fee_frozen.max(l.amount); + } + } + put_storage_value(b"Balances", b"Locks", &hash, locks); + } + put_storage_value(b"Balances", b"Account", &hash, account); + } + // Second, migrate old ReservedBalance into new Account. + // ReservedBalance: map T::AccountId => T::Balance + for (hash, reserved) in StorageIterator::::new(b"Balances", b"ReservedBalance").drain() { + let mut account = get_storage_value::>(b"Balances", b"Account", &hash).unwrap_or_default(); + account.reserved = reserved; + put_storage_value(b"Balances", b"Account", &hash, account); } - } - /// Set the free balance of an account to some new value. Will enforce `ExistentialDeposit` - /// law, annulling the account as needed. - /// - /// Doesn't do any preparatory work for creating a new account, so should only be used when it - /// is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - // Commented out for now - but consider it instructive. - // assert!(!Self::total_balance(who).is_zero()); - // assert!(Self::free_balance(who) > T::ExistentialDeposit::get()); - if balance < T::ExistentialDeposit::get() { - >::insert(who, balance); - Self::on_free_too_low(who); - UpdateBalanceOutcome::AccountKilled - } else { - >::insert(who, balance); - UpdateBalanceOutcome::Updated + // Finally, migrate vesting and ensure locks are in place. We will be lazy and just lock + // for the maximum amount (i.e. at genesis). Users will need to call "vest" to reduce the + // lock to something sensible. + // pub Vesting: map T::AccountId => Option; + for (hash, vesting) in StorageIterator::<(T::Balance, T::Balance, T::BlockNumber)>::new(b"Balances", b"Vesting").drain() { + let mut account = get_storage_value::>(b"Balances", b"Account", &hash).unwrap_or_default(); + let mut locks = get_storage_value::>>(b"Balances", b"Locks", &hash).unwrap_or_default(); + locks.push(BalanceLock { + id: *b"vesting ", + amount: vesting.0.clone(), + reasons: Reasons::Misc, + }); + account.misc_frozen = account.misc_frozen.max(vesting.0.clone()); + put_storage_value(b"Vesting", b"Vesting", &hash, vesting); + put_storage_value(b"Balances", b"Locks", &hash, locks); + put_storage_value(b"Balances", b"Account", &hash, account); + } + + for (hash, balances) in StorageIterator::>::new(b"Balances", b"Account").drain() { + let nonce = take_storage_value::(b"System", b"AccountNonce", &hash).unwrap_or_default(); + put_storage_value(b"System", b"Account", &hash, (nonce, balances)); } } - /// Register a new account (with existential balance). - /// - /// This just calls appropriate hooks. It doesn't (necessarily) make any state changes. - fn new_account(who: &T::AccountId, balance: T::Balance) { - T::OnNewAccount::on_new_account(&who); - Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone())); + /// Get the free balance of an account. + pub fn free_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).free } - /// Unregister an account. - /// - /// This just removes the nonce and leaves an event. - fn reap_account(who: &T::AccountId, dust: T::Balance) { - T::OnReapAccount::on_reap_account(who); - Self::deposit_event(RawEvent::ReapedAccount(who.clone(), dust)); + /// Get the balance of an account that can be used for transfers, reservations, or any other + /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. + pub fn usable_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).usable(Reasons::Misc) } - /// Account's free balance has dropped below existential deposit. Kill its - /// free side and the account completely if its reserved size is already dead. - /// - /// Will maintain total issuance. - fn on_free_too_low(who: &T::AccountId) { - let dust = >::take(who); - >::remove(who); + /// Get the balance of an account that can be used for paying transaction fees (not tipping, + /// or any other kind of fees, though). Will be at most `free_balance`. + pub fn usable_balance_for_fees(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).usable(Reasons::Fee) + } - T::OnFreeBalanceZero::on_free_balance_zero(who); + /// Get the reserved balance of an account. + pub fn reserved_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { + Self::account(who.borrow()).reserved + } - let mut reserved_balance = Self::reserved_balance(who); + /// Get both the free and reserved balances of an account. + fn account(who: &T::AccountId) -> AccountData { + T::AccountStore::get(&who) + } - if !dust.is_zero() { - if reserved_balance >= T::ExistentialDeposit::get() { - // any individual account cannot cause overflow in balance. - reserved_balance += dust; - Self::set_reserved_balance(who, reserved_balance); - } else { - // underflow should never happen, but if it does, there's not much we can do. - T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust)); + /// Places the `free` and `reserved` parts of `new` into `account`. Also does any steps needed + /// after mutating an account. This includes DustRemoval unbalancing, in the case than the `new` + /// account's total balance is non-zero but below ED. + /// + /// Returns the final free balance, iff the account was previously of total balance zero, known + /// as its "endowment". + fn post_mutation( + who: &T::AccountId, + new: AccountData, + ) -> Option> { + let total = new.total(); + if total < T::ExistentialDeposit::get() { + if !total.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance::new(total)); + Self::deposit_event(RawEvent::DustLost(who.clone(), total)); } - } - - if reserved_balance.is_zero() { - Self::reap_account(who, dust); + None + } else { + Some(new) } } - /// Account's reserved balance has dropped below existential deposit. Kill its - /// reserved side and the account completely if its free size is already dead. + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. /// - /// Will maintain total issuance. - fn on_reserved_too_low(who: &T::AccountId) { - let dust = >::take(who); + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData) -> R + ) -> R { + Self::try_mutate_account(who, |a| -> Result { Ok(f(a)) }) + .expect("Error is infallible; qed") + } - let mut free_balance = Self::free_balance(who); + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the + /// result of `f` is an `Err`. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn try_mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData) -> Result + ) -> Result { + T::AccountStore::try_mutate_exists(who, |maybe_account| { + let mut account = maybe_account.take().unwrap_or_default(); + let was_zero = account.total().is_zero(); + f(&mut account).map(move |result| { + let maybe_endowed = if was_zero { Some(account.free) } else { None }; + *maybe_account = Self::post_mutation(who, account); + (maybe_endowed, result) + }) + }).map(|(maybe_endowed, result)| { + if let Some(endowed) = maybe_endowed { + Self::deposit_event(RawEvent::Endowed(who.clone(), endowed)); + } + result + }) + } - if !dust.is_zero() { - if free_balance >= T::ExistentialDeposit::get() { - // any individual account cannot cause overflow in balance. - free_balance += dust; - Self::set_free_balance(who, free_balance); - } else { - // underflow should never happen, but it if does, there's nothing to be done here. - T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust)); + /// Update the account entry for `who`, given the locks. + fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { + Self::mutate_account(who, |b| { + b.misc_frozen = Zero::zero(); + b.fee_frozen = Zero::zero(); + for l in locks.iter() { + if l.reasons == Reasons::All || l.reasons == Reasons::Misc { + b.misc_frozen = b.misc_frozen.max(l.amount); + } + if l.reasons == Reasons::All || l.reasons == Reasons::Fee { + b.fee_frozen = b.fee_frozen.max(l.amount); + } } - } + }); + Locks::::insert(who, locks); + } +} - if free_balance.is_zero() { - Self::reap_account(who, dust); - } +impl, I: Instance> OnReapAccount for Module { + fn on_reap_account(who: &T::AccountId) { + Locks::::remove(who); + Account::::remove(who); } } @@ -831,9 +895,8 @@ mod imbalances { // its type declaration). // This works as long as `increase_total_issuance_by` doesn't use the Imbalance // types (basically for charging fees). -// This should eventually be refactored so that the three type items that do -// depend on the Imbalance type (TransferPayment, DustRemoval) -// are placed in their own SRML module. +// This should eventually be refactored so that the type item that +// depends on the Imbalance type (DustRemoval) is placed in its own SRML module. struct ElevatedTrait, I: Instance>(T, I); impl, I: Instance> Clone for ElevatedTrait { fn clone(&self) -> Self { unimplemented!() } @@ -859,22 +922,19 @@ impl, I: Instance> frame_system::Trait for ElevatedTrait { type AvailableBlockRatio = T::AvailableBlockRatio; type Version = T::Version; type ModuleToIndex = T::ModuleToIndex; + type OnNewAccount = T::OnNewAccount; + type OnReapAccount = T::OnReapAccount; + type AccountData = T::AccountData; } impl, I: Instance> Trait for ElevatedTrait { type Balance = T::Balance; - type OnFreeBalanceZero = T::OnFreeBalanceZero; - type OnReapAccount = T::OnReapAccount; - type OnNewAccount = T::OnNewAccount; type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = T::ExistentialDeposit; - type TransferFee = T::TransferFee; - type CreationFee = T::CreationFee; + type AccountStore = T::AccountStore; } -impl, I: Instance> Currency for Module -where +impl, I: Instance> Currency for Module where T::Balance: MaybeSerializeDeserialize + Debug { type Balance = T::Balance; @@ -882,11 +942,10 @@ where type NegativeImbalance = NegativeImbalance; fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::free_balance(who) + Self::reserved_balance(who) + Self::account(who).total() } // Check if `value` amount of free balance can be slashed from `who`. - // Is a no-op if value to be slashed is zero. fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { if value.is_zero() { return true } Self::free_balance(who) >= value @@ -900,10 +959,6 @@ where T::ExistentialDeposit::get() } - fn free_balance(who: &T::AccountId) -> Self::Balance { - >::get(who) - } - // Burn funds from the total issuance, returning a positive imbalance for the amount burned. // Is a no-op if amount to be burned is zero. fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { @@ -931,6 +986,10 @@ where NegativeImbalance::new(amount) } + fn free_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).free + } + // Ensure that an account can withdraw from their free balance given any existing withdrawal // restrictions like locks and vesting balance. // Is a no-op if amount to be withdrawn is zero. @@ -946,222 +1005,192 @@ where new_balance: T::Balance, ) -> DispatchResult { if amount.is_zero() { return Ok(()) } - if reasons.intersects(WithdrawReason::Reserve | WithdrawReason::Transfer) - && Self::vesting_balance(who) > new_balance - { - Err(Error::::VestingBalance)? - } - let locks = Self::locks(who); - if locks.is_empty() { - return Ok(()) - } - - let now = >::block_number(); - if locks.into_iter() - .all(|l| - now >= l.until - || new_balance >= l.amount - || !l.reasons.intersects(reasons) - ) - { - Ok(()) - } else { - Err(Error::::LiquidityRestrictions.into()) - } + let min_balance = Self::account(who).frozen(reasons.into()); + ensure!(new_balance >= min_balance, Error::::LiquidityRestrictions); + Ok(()) } // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. - // Is a no-op if value to be transferred is zero. + // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. fn transfer( transactor: &T::AccountId, dest: &T::AccountId, value: Self::Balance, existence_requirement: ExistenceRequirement, ) -> DispatchResult { - if value.is_zero() { return Ok(()) } - let from_balance = Self::free_balance(transactor); - let to_balance = Self::free_balance(dest); - let would_create = to_balance.is_zero(); - let fee = if would_create { T::CreationFee::get() } else { T::TransferFee::get() }; - let liability = value.checked_add(&fee).ok_or(Error::::Overflow)?; - let new_from_balance = from_balance.checked_sub(&liability).ok_or(Error::::InsufficientBalance)?; - - if would_create && value < T::ExistentialDeposit::get() { - Err(Error::::ExistentialDeposit)? - } - Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer.into(), new_from_balance)?; + if value.is_zero() || transactor == dest { return Ok(()) } - // NOTE: total stake being stored in the same type means that this could never overflow - // but better to be safe than sorry. - let new_to_balance = to_balance.checked_add(&value).ok_or(Error::::Overflow)?; + Self::try_mutate_account(dest, |to_account| -> DispatchResult { + Self::try_mutate_account(transactor, |from_account| -> DispatchResult { + from_account.free = from_account.free.checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; - if transactor != dest { - if existence_requirement == ExistenceRequirement::KeepAlive { - if new_from_balance < Self::minimum_balance() { - Err(Error::::KeepAlive)? - } - } + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + to_account.free = to_account.free.checked_add(&value).ok_or(Error::::Overflow)?; - Self::set_free_balance(transactor, new_from_balance); - if !>::exists(dest) { - Self::new_account(dest, new_to_balance); - } + let ed = T::ExistentialDeposit::get(); + ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); - // Emit transfer event. - Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee)); + Self::ensure_can_withdraw( + transactor, + value, + WithdrawReason::Transfer.into(), + from_account.free, + )?; - // Take action on the set_free_balance call. - // This will emit events that _resulted_ from the transfer. - Self::set_free_balance(dest, new_to_balance); - T::TransferPayment::on_unbalanced(NegativeImbalance::new(fee)); - } + let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; + ensure!(allow_death || from_account.free >= ed, Error::::KeepAlive); - Ok(()) - } + Ok(()) + }) + })?; - // Withdraw some free balance from an account, respecting existence requirements. - // Is a no-op if value to be withdrawn is zero. - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result { - if value.is_zero() { return Ok(NegativeImbalance::zero()); } + // Emit transfer event. + Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value)); - let old_balance = Self::free_balance(who); - if let Some(new_balance) = old_balance.checked_sub(&value) { - // if we need to keep the account alive... - if liveness == ExistenceRequirement::KeepAlive - // ...and it would be dead afterwards... - && new_balance < T::ExistentialDeposit::get() - // ...yet is was alive before - && old_balance >= T::ExistentialDeposit::get() - { - Err(Error::::KeepAlive)? - } - Self::ensure_can_withdraw(who, value, reasons, new_balance)?; - Self::set_free_balance(who, new_balance); - Ok(NegativeImbalance::new(value)) - } else { - Err(Error::::InsufficientBalance)? - } + Ok(()) } - // Slash an account, returning the negative imbalance created and any left over - // amount that could not be slashed. - // Is a no-op if value to be slashed is zero. + /// Slash a target account `who`, returning the negative imbalance created and any left over + /// amount that could not be slashed. + /// + /// Is a no-op if `value` to be slashed is zero. + /// + /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn + /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having + /// to draw from reserved funds, however we err on the side of punishment if things are inconsistent + /// or `can_slash` wasn't used appropriately. fn slash( who: &T::AccountId, value: Self::Balance ) -> (Self::NegativeImbalance, Self::Balance) { if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) } - let free_balance = Self::free_balance(who); - let free_slash = cmp::min(free_balance, value); - - Self::set_free_balance(who, free_balance - free_slash); - let remaining_slash = value - free_slash; - // NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn - // from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having - // to draw from reserved funds, however we err on the side of punishment if things are inconsistent - // or `can_slash` wasn't used appropriately. - if !remaining_slash.is_zero() { - let reserved_balance = Self::reserved_balance(who); - let reserved_slash = cmp::min(reserved_balance, remaining_slash); - Self::set_reserved_balance(who, reserved_balance - reserved_slash); - (NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash) - } else { - (NegativeImbalance::new(value), Zero::zero()) - } + Self::mutate_account(who, |account| { + let free_slash = cmp::min(account.free, value); + account.free -= free_slash; + + let remaining_slash = value - free_slash; + if !remaining_slash.is_zero() { + let reserved_slash = cmp::min(account.reserved, remaining_slash); + account.reserved -= reserved_slash; + (NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash) + } else { + (NegativeImbalance::new(value), Zero::zero()) + } + }) } - // Deposit some `value` into the free balance of an existing account. - // Is a no-op if the value to be deposited is zero. + /// Deposit some `value` into the free balance of an existing target account `who`. + /// + /// Is a no-op if the `value` to be deposited is zero. fn deposit_into_existing( who: &T::AccountId, value: Self::Balance - ) -> result::Result { + ) -> Result { if value.is_zero() { return Ok(PositiveImbalance::zero()) } - if Self::total_balance(who).is_zero() { - Err(Error::::DeadAccount)? - } - Self::set_free_balance(who, Self::free_balance(who) + value); - Ok(PositiveImbalance::new(value)) + Self::try_mutate_account(who, |account| -> Result { + ensure!(!account.total().is_zero(), Error::::DeadAccount); + account.free = account.free.checked_add(&value).ok_or(Error::::Overflow)?; + Ok(PositiveImbalance::new(value)) + }) } - // Deposit some `value` into the free balance of `who`, possibly creating a new account. - // Is a no-op if the value to be deposited is zero. + /// Deposit some `value` into the free balance of `who`, possibly creating a new account. + /// + /// This function is a no-op if: + /// - the `value` to be deposited is zero; or + /// - if the `value` to be deposited is less than the ED and the account does not yet exist; or + /// - `value` is so large it would cause the balance of `who` to overflow. fn deposit_creating( who: &T::AccountId, value: Self::Balance, ) -> Self::PositiveImbalance { if value.is_zero() { return Self::PositiveImbalance::zero() } - let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value); - if let SignedImbalance::Positive(p) = imbalance { - p - } else { - // Impossible, but be defensive. - Self::PositiveImbalance::zero() - } + Self::try_mutate_account(who, |account| -> Result { + // bail if not yet created and this operation wouldn't be enough to create it. + let ed = T::ExistentialDeposit::get(); + ensure!(value >= ed || !account.total().is_zero(), Self::PositiveImbalance::zero()); + + // defensive only: overflow should never happen, however in case it does, then this + // operation is a no-op. + account.free = account.free.checked_add(&value).ok_or(Self::PositiveImbalance::zero())?; + + Ok(PositiveImbalance::new(value)) + }).unwrap_or_else(|x| x) } - fn make_free_balance_be(who: &T::AccountId, balance: Self::Balance) -> ( - SignedImbalance, - UpdateBalanceOutcome - ) { - let original = Self::free_balance(who); - if balance < T::ExistentialDeposit::get() && original.is_zero() { + /// Withdraw some free balance from an account, respecting existence requirements. + /// + /// Is a no-op if value to be withdrawn is zero. + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> result::Result { + if value.is_zero() { return Ok(NegativeImbalance::zero()); } + + Self::try_mutate_account(who, |account| + -> Result + { + let new_free_account = account.free.checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + + // bail if we need to keep the account alive and this would kill it. + let ed = T::ExistentialDeposit::get(); + let would_be_dead = new_free_account + account.reserved < ed; + let would_kill = would_be_dead && account.free + account.reserved >= ed; + ensure!(liveness == AllowDeath || !would_kill, Error::::KeepAlive); + + Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; + + account.free = new_free_account; + + Ok(NegativeImbalance::new(value)) + }) + } + + /// Force the new free balance of a target account `who` to some new value `balance`. + fn make_free_balance_be(who: &T::AccountId, value: Self::Balance) + -> SignedImbalance + { + Self::try_mutate_account(who, |account| + -> Result, ()> + { + let ed = T::ExistentialDeposit::get(); // If we're attempting to set an existing account to less than ED, then // bypass the entire operation. It's a no-op if you follow it through, but // since this is an instance where we might account for a negative imbalance - // (in the dust cleaner of set_free_balance) before we account for its actual + // (in the dust cleaner of set_account) before we account for its actual // equal and opposite cause (returned as an Imbalance), then in the // instance that there's no other accounts on the system at all, we might // underflow the issuance and our arithmetic will be off. - return ( - SignedImbalance::Positive(Self::PositiveImbalance::zero()), - UpdateBalanceOutcome::AccountKilled, - ) - } - let imbalance = if original <= balance { - SignedImbalance::Positive(PositiveImbalance::new(balance - original)) - } else { - SignedImbalance::Negative(NegativeImbalance::new(original - balance)) - }; - // If the balance is too low, then the account is reaped. - // NOTE: There are two balances for every account: `reserved_balance` and - // `free_balance`. This contract subsystem only cares about the latter: whenever - // the term "balance" is used *here* it should be assumed to mean "free balance" - // in the rest of the module. - // Free balance can never be less than ED. If that happens, it gets reduced to zero - // and the account information relevant to this subsystem is deleted (i.e. the - // account is reaped). - let outcome = if balance < T::ExistentialDeposit::get() { - Self::set_free_balance(who, balance); - UpdateBalanceOutcome::AccountKilled - } else { - if !>::exists(who) { - Self::new_account(&who, balance); - } - Self::set_free_balance(who, balance); - UpdateBalanceOutcome::Updated - }; - (imbalance, outcome) + ensure!(value + account.reserved >= ed || !account.total().is_zero(), ()); + + let imbalance = if account.free <= value { + SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) + }; + account.free = value; + Ok(imbalance) + }).unwrap_or(SignedImbalance::Positive(Self::PositiveImbalance::zero())) } } -impl, I: Instance> ReservableCurrency for Module -where +impl, I: Instance> ReservableCurrency for Module where T::Balance: MaybeSerializeDeserialize + Debug { - // Check if `who` can reserve `value` from their free balance. - // Is a no-op if value to be reserved is zero. + /// Check if `who` can reserve `value` from their free balance. + /// + /// Always `true` if value to be reserved is zero. fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { if value.is_zero() { return true } - Self::free_balance(who) + Self::account(who).free .checked_sub(&value) .map_or(false, |new_balance| Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), new_balance).is_ok() @@ -1169,66 +1198,88 @@ where } fn reserved_balance(who: &T::AccountId) -> Self::Balance { - >::get(who) + Self::account(who).reserved } - // Move `value` from the free balance from `who` to their reserved balance. - // Is a no-op if value to be reserved is zero. - fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), DispatchError> { + /// Move `value` from the free balance from `who` to their reserved balance. + /// + /// Is a no-op if value to be reserved is zero. + fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { if value.is_zero() { return Ok(()) } - let b = Self::free_balance(who); - if b < value { - Err(Error::::InsufficientBalance)? - } - let new_balance = b - value; - Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), new_balance)?; - Self::set_reserved_balance(who, Self::reserved_balance(who) + value); - Self::set_free_balance(who, new_balance); - Ok(()) + + Self::try_mutate_account(who, |account| -> DispatchResult { + account.free = account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; + account.reserved = account.reserved.checked_add(&value).ok_or(Error::::Overflow)?; + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), account.free) + }) } - // Unreserve some funds, returning any amount that was unable to be unreserved. - // Is a no-op if the value to be unreserved is zero. + /// Unreserve some funds, returning any amount that was unable to be unreserved. + /// + /// Is a no-op if the value to be unreserved is zero. fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { if value.is_zero() { return Zero::zero() } - let b = Self::reserved_balance(who); - let actual = cmp::min(b, value); - Self::set_free_balance(who, Self::free_balance(who) + actual); - Self::set_reserved_balance(who, b - actual); - value - actual + + Self::mutate_account(who, |account| { + let actual = cmp::min(account.reserved, value); + account.reserved -= actual; + // defensive only: this can never fail since total issuance which is at least free+reserved + // fits into the same datatype. + account.free = account.free.saturating_add(actual); + value - actual + }) } - // Slash from reserved balance, returning the negative imbalance created, - // and any amount that was unable to be slashed. - // Is a no-op if the value to be slashed is zero. + /// Slash from reserved balance, returning the negative imbalance created, + /// and any amount that was unable to be slashed. + /// + /// Is a no-op if the value to be slashed is zero. fn slash_reserved( who: &T::AccountId, value: Self::Balance ) -> (Self::NegativeImbalance, Self::Balance) { if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) } - let b = Self::reserved_balance(who); - let slash = cmp::min(b, value); - // underflow should never happen, but it if does, there's nothing to be done here. - Self::set_reserved_balance(who, b - slash); - (NegativeImbalance::new(slash), value - slash) + + Self::mutate_account(who, |account| { + // underflow should never happen, but it if does, there's nothing to be done here. + let actual = cmp::min(account.reserved, value); + account.reserved -= actual; + (NegativeImbalance::new(actual), value - actual) + }) } - // Move the reserved balance of one account into the free balance of another. - // Is a no-op if the value to be moved is zero. + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. fn repatriate_reserved( slashed: &T::AccountId, beneficiary: &T::AccountId, value: Self::Balance, - ) -> result::Result { - if value.is_zero() { return Ok (Zero::zero()) } - if Self::total_balance(beneficiary).is_zero() { - Err(Error::::DeadAccount)? + status: Status, + ) -> Result { + if value.is_zero() { return Ok(Zero::zero()) } + + if slashed == beneficiary { + return match status { + Status::Free => Ok(Self::unreserve(slashed, value)), + Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), + }; } - let b = Self::reserved_balance(slashed); - let slash = cmp::min(b, value); - Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash); - Self::set_reserved_balance(slashed, b - slash); - Ok(value - slash) + + Self::try_mutate_account(beneficiary, |to_account| -> Result { + ensure!(!to_account.total().is_zero(), Error::::DeadAccount); + Self::try_mutate_account(slashed, |from_account| -> Result { + let actual = cmp::min(from_account.reserved, value); + match status { + Status::Free => to_account.free = to_account.free.checked_add(&actual).ok_or(Error::::Overflow)?, + Status::Reserved => to_account.reserved = to_account.reserved.checked_add(&actual).ok_or(Error::::Overflow)?, + } + from_account.reserved -= actual; + Ok(value - actual) + }) + }) } } @@ -1239,127 +1290,70 @@ where type Moment = T::BlockNumber; // Set a lock on the balance of `who`. - // Is a no-op if lock amount is zero. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. fn set_lock( id: LockIdentifier, who: &T::AccountId, amount: T::Balance, - until: T::BlockNumber, reasons: WithdrawReasons, ) { - if amount.is_zero() { return } - let now = >::block_number(); - let mut new_lock = Some(BalanceLock { id, amount, until, reasons }); + if amount.is_zero() || reasons.is_none() { return } + let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); let mut locks = Self::locks(who).into_iter().filter_map(|l| if l.id == id { new_lock.take() - } else if l.until > now { - Some(l) } else { - None + Some(l) }).collect::>(); if let Some(lock) = new_lock { locks.push(lock) } - >::insert(who, locks); + Self::update_locks(who, &locks[..]); } + // Extend a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. fn extend_lock( id: LockIdentifier, who: &T::AccountId, amount: T::Balance, - until: T::BlockNumber, reasons: WithdrawReasons, ) { - let now = >::block_number(); - let mut new_lock = Some(BalanceLock { id, amount, until, reasons }); + if amount.is_zero() || reasons.is_none() { return } + let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() }); let mut locks = Self::locks(who).into_iter().filter_map(|l| if l.id == id { new_lock.take().map(|nl| { BalanceLock { id: l.id, amount: l.amount.max(nl.amount), - until: l.until.max(nl.until), reasons: l.reasons | nl.reasons, } }) - } else if l.until > now { - Some(l) } else { - None + Some(l) }).collect::>(); if let Some(lock) = new_lock { locks.push(lock) } - >::insert(who, locks); + Self::update_locks(who, &locks[..]); } fn remove_lock( id: LockIdentifier, who: &T::AccountId, ) { - let now = >::block_number(); - let locks = Self::locks(who).into_iter().filter_map(|l| - if l.until > now && l.id != id { - Some(l) - } else { - None - }).collect::>(); - >::insert(who, locks); - } -} - -impl, I: Instance> VestingCurrency for Module -where - T::Balance: MaybeSerializeDeserialize + Debug -{ - type Moment = T::BlockNumber; - - /// Get the amount that is currently being vested and cannot be transferred out of this account. - fn vesting_balance(who: &T::AccountId) -> T::Balance { - if let Some(v) = Self::vesting(who) { - Self::free_balance(who) - .min(v.locked_at(>::block_number())) - } else { - Zero::zero() - } - } - - /// Adds a vesting schedule to a given account. - /// - /// If there already exists a vesting schedule for the given account, an `Err` is returned - /// and nothing is updated. - /// Is a no-op if the amount to be vested is zero. - fn add_vesting_schedule( - who: &T::AccountId, - locked: T::Balance, - per_block: T::Balance, - starting_block: T::BlockNumber - ) -> DispatchResult { - if locked.is_zero() { return Ok(()) } - if >::exists(who) { - Err(Error::::ExistingVestingSchedule)? - } - let vesting_schedule = VestingSchedule { - locked, - per_block, - starting_block - }; - >::insert(who, vesting_schedule); - Ok(()) - } - - /// Remove a vesting schedule for a given account. - fn remove_vesting_schedule(who: &T::AccountId) { - >::remove(who); + let mut locks = Self::locks(who); + locks.retain(|l| l.id != id); + Self::update_locks(who, &locks[..]); } } -impl, I: Instance> IsDeadAccount for Module -where +impl, I: Instance> IsDeadAccount for Module where T::Balance: MaybeSerializeDeserialize + Debug { fn is_dead_account(who: &T::AccountId) -> bool { - Self::total_balance(who).is_zero() + // this should always be exactly equivalent to `Self::account(who).total().is_zero()` + !T::AccountStore::is_explicit(who) } } diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 175e87aea4b4cd9c54fe8bfe3cd9f6161d0e3cc6..3d0c9e9207f5d82faf9b58ee3958fa11fdaf3b4c 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -14,797 +14,638 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Tests for the module. - -use super::*; -use mock::{Balances, ExtBuilder, Test, System, info_from_weight, CALL}; -use sp_runtime::traits::{SignedExtension, BadOrigin}; -use frame_support::{ - assert_noop, assert_ok, assert_err, - traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, - Currency, ReservableCurrency, ExistenceRequirement::AllowDeath} -}; -use pallet_transaction_payment::ChargeTransactionPayment; -use frame_system::RawOrigin; - -const ID_1: LockIdentifier = *b"1 "; -const ID_2: LockIdentifier = *b"2 "; -const ID_3: LockIdentifier = *b"3 "; - -#[test] -fn basic_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - assert_eq!(Balances::free_balance(&1), 10); - Balances::set_lock(ID_1, &1, 9, u64::max_value(), WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 5, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn partial_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_removal_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::max_value(), u64::max_value(), WithdrawReasons::all()); - Balances::remove_lock(ID_1, &1); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_replacement_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::max_value(), u64::max_value(), WithdrawReasons::all()); - Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn double_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - Balances::set_lock(ID_2, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn combination_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::max_value(), 0, WithdrawReasons::none()); - Balances::set_lock(ID_2, &1, 0, u64::max_value(), WithdrawReasons::none()); - Balances::set_lock(ID_3, &1, 0, 0, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_value_extension_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 2, u64::max_value(), WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 8, u64::max_value(), WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn lock_reasons_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build() - .execute_with(|| { - Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into()); - assert_noop!( - >::transfer(&1, &2, 1, AllowDeath), - Error::::LiquidityRestrictions - ); - assert_ok!(>::reserve(&1, 1)); - // NOTE: this causes a fee payment. - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - info_from_weight(1), - 0, - ).is_ok()); - - Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - assert_noop!( - >::reserve(&1, 1), - Error::::LiquidityRestrictions - ); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - info_from_weight(1), - 0, - ).is_ok()); - - Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - assert_ok!(>::reserve(&1, 1)); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - info_from_weight(1), - 0, - ).is_err()); - }); -} - -#[test] -fn lock_block_number_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, 2, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 1, AllowDeath), - Error::::LiquidityRestrictions - ); - - System::set_block_number(2); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_block_number_extension_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, 2, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, 1, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - System::set_block_number(2); - Balances::extend_lock(ID_1, &1, 10, 8, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn lock_reasons_extension_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, 10, WithdrawReason::Transfer.into()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, 10, WithdrawReasons::none()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, 10, WithdrawReason::Reserve.into()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn default_indexing_on_new_accounts_should_not_work2() { - ExtBuilder::default() - .existential_deposit(10) - .creation_fee(50) - .monied(true) - .build() - .execute_with(|| { - assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist - // ext_deposit is 10, value is 9, not satisfies for ext_deposit - assert_noop!( - Balances::transfer(Some(1).into(), 5, 9), - Error::::ExistentialDeposit, - ); - assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist - assert_eq!(Balances::free_balance(&1), 100); - }); -} - -#[test] -fn reserved_balance_should_prevent_reclaim_count() { - ExtBuilder::default() - .existential_deposit(256 * 1) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(Balances::is_dead_account(&2), false); - assert_eq!(Balances::is_dead_account(&5), true); - assert_eq!(Balances::total_balance(&2), 256 * 20); - - assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved - assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted." - assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. - assert_eq!(Balances::is_dead_account(&2), false); - assert_eq!(System::account_nonce(&2), 1); - - // account 4 tries to take index 1 for account 5. - assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); - assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); - assert_eq!(Balances::is_dead_account(&5), false); - - assert!(Balances::slash(&2, 256 * 19 + 2).1.is_zero()); // account 2 gets slashed - // "reserve" account reduced to 255 (below ED) so account deleted - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(System::account_nonce(&2), 0); // nonce zero - assert_eq!(Balances::is_dead_account(&2), true); - - // account 4 tries to take index 1 again for account 6. - assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); - assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); - assert_eq!(Balances::is_dead_account(&6), false); - }); -} - - -#[test] -fn reward_should_work() { - ExtBuilder::default().monied(true).build().execute_with(|| { - assert_eq!(Balances::total_balance(&1), 10); - assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); - assert_eq!(Balances::total_balance(&1), 20); - assert_eq!(>::get(), 120); - }); -} - -#[test] -fn dust_account_removal_should_work() { - ExtBuilder::default() - .existential_deposit(100) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 2000); - - assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); // index 1 (account 2) becomes zombie - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 1901); - assert_eq!(System::account_nonce(&2), 0); - }); -} - -#[test] -fn dust_account_removal_should_work2() { - ExtBuilder::default() - .existential_deposit(100) - .creation_fee(50) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 2000); - // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) - assert_ok!(Balances::transfer(Some(2).into(), 5, 1851)); - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 1851); - assert_eq!(System::account_nonce(&2), 0); - }); -} - -#[test] -fn balance_works() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - assert_eq!(Balances::free_balance(&1), 42); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::free_balance(&2), 0); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::total_balance(&2), 0); - }); -} - -#[test] -fn balance_transfer_works() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); -} - -#[test] -fn force_transfer_works() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_noop!( - Balances::force_transfer(Some(2).into(), 1, 2, 69), - BadOrigin, - ); - assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); -} - -#[test] -fn reserving_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(&1), 111); - assert_eq!(Balances::reserved_balance(&1), 0); - - assert_ok!(Balances::reserve(&1, 69)); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(&1), 42); - assert_eq!(Balances::reserved_balance(&1), 69); - }); -} - -#[test] -fn balance_transfer_when_reserved_should_not_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 69), - Error::::InsufficientBalance, - ); - }); -} - -#[test] -fn deducting_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_eq!(Balances::free_balance(&1), 42); - }); -} - -#[test] -fn refunding_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - Balances::set_reserved_balance(&1, 69); - Balances::unreserve(&1, 69); - assert_eq!(Balances::free_balance(&1), 111); - assert_eq!(Balances::reserved_balance(&1), 0); - }); -} - -#[test] -fn slashing_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert!(Balances::slash(&1, 69).1.is_zero()); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&1), 42); - assert_eq!(>::get(), 42); - }); -} - -#[test] -fn slashing_incomplete_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - assert_ok!(Balances::reserve(&1, 21)); - assert_eq!(Balances::slash(&1, 69).1, 27); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(>::get(), 0); - }); -} - -#[test] -fn unreserving_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - Balances::unreserve(&1, 42); - assert_eq!(Balances::reserved_balance(&1), 69); - assert_eq!(Balances::free_balance(&1), 42); - }); -} - -#[test] -fn slashing_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_eq!(Balances::slash_reserved(&1, 42).1, 0); - assert_eq!(Balances::reserved_balance(&1), 69); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(>::get(), 69); - }); -} - -#[test] -fn slashing_incomplete_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 42)); - assert_eq!(Balances::slash_reserved(&1, 69).1, 27); - assert_eq!(Balances::free_balance(&1), 69); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(>::get(), 69); - }); -} - -#[test] -fn transferring_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 110)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 41), 0); - assert_eq!(Balances::reserved_balance(&1), 69); - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::free_balance(&2), 42); - }); -} - -#[test] -fn transferring_reserved_balance_to_nonexistent_should_fail() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_noop!(Balances::repatriate_reserved(&1, &2, 42), Error::::DeadAccount); - }); -} - -#[test] -fn transferring_incomplete_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 41)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 69), 28); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::free_balance(&1), 69); - assert_eq!(Balances::reserved_balance(&2), 0); - assert_eq!(Balances::free_balance(&2), 42); - }); -} - -#[test] -fn transferring_too_high_value_should_not_panic() { - ExtBuilder::default().build().execute_with(|| { - >::insert(1, u64::max_value()); - >::insert(2, 1); - - assert_err!( - Balances::transfer(Some(1).into(), 2, u64::max_value()), - Error::::Overflow, - ); - - assert_eq!(Balances::free_balance(&1), u64::max_value()); - assert_eq!(Balances::free_balance(&2), 1); - }); -} - -#[test] -fn account_create_on_free_too_low_with_other() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 100); - assert_eq!(>::get(), 100); - - // No-op. - let _ = Balances::deposit_creating(&2, 50); - assert_eq!(Balances::free_balance(&2), 0); - assert_eq!(>::get(), 100); - }) -} - - -#[test] -fn account_create_on_free_too_low() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // No-op. - let _ = Balances::deposit_creating(&2, 50); - assert_eq!(Balances::free_balance(&2), 0); - assert_eq!(>::get(), 0); - }) -} - -#[test] -fn account_removal_on_free_too_low() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - assert_eq!(>::get(), 0); - - // Setup two accounts with free balance above the existential threshold. - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 110); - - assert_eq!(Balances::free_balance(&1), 110); - assert_eq!(Balances::free_balance(&2), 110); - assert_eq!(>::get(), 220); - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the existential threshold. - // This should lead to the removal of all balance of this account. - assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); - - // Verify free balance removal of account 1. - assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(Balances::free_balance(&2), 130); - - // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(>::get(), 130); - }); -} - -#[test] -fn transfer_overflow_isnt_exploitable() { - ExtBuilder::default().creation_fee(50).build().execute_with(|| { - // Craft a value that will overflow if summed with `creation_fee`. - let evil_value = u64::max_value() - 49; - - assert_err!( - Balances::transfer(Some(1).into(), 5, evil_value), - Error::::Overflow, - ); - }); -} - -#[test] -fn check_vesting_status() { - ExtBuilder::default() - .existential_deposit(256) - .monied(true) - .vesting(true) - .build() - .execute_with(|| { - assert_eq!(System::block_number(), 1); - let user1_free_balance = Balances::free_balance(&1); - let user2_free_balance = Balances::free_balance(&2); - let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance - assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance - let user1_vesting_schedule = VestingSchedule { - locked: 256 * 5, - per_block: 128, // Vesting over 10 blocks - starting_block: 0, - }; - let user2_vesting_schedule = VestingSchedule { - locked: 256 * 20, - per_block: 256, // Vesting over 20 blocks - starting_block: 10, - }; - let user12_vesting_schedule = VestingSchedule { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_eq!(Balances::vesting(&1), Some(user1_vesting_schedule)); // Account 1 has a vesting schedule - assert_eq!(Balances::vesting(&2), Some(user2_vesting_schedule)); // Account 2 has a vesting schedule - assert_eq!(Balances::vesting(&12), Some(user12_vesting_schedule)); // Account 12 has a vesting schedule - - // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 - assert_eq!(Balances::vesting_balance(&1), 128 * 9); - // Account 2 has their full balance locked - assert_eq!(Balances::vesting_balance(&2), user2_free_balance); - // Account 12 has only their illiquid funds locked - assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); - - System::set_block_number(10); - assert_eq!(System::block_number(), 10); - - // Account 1 has fully vested by block 10 - assert_eq!(Balances::vesting_balance(&1), 0); - // Account 2 has started vesting by block 10 - assert_eq!(Balances::vesting_balance(&2), user2_free_balance); - // Account 12 has started vesting by block 10 - assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); - - System::set_block_number(30); - assert_eq!(System::block_number(), 30); - - assert_eq!(Balances::vesting_balance(&1), 0); // Account 1 is still fully vested, and not negative - assert_eq!(Balances::vesting_balance(&2), 0); // Account 2 has fully vested by block 30 - assert_eq!(Balances::vesting_balance(&12), 0); // Account 2 has fully vested by block 30 - - }); -} - -#[test] -fn unvested_balance_should_not_transfer() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .vesting(true) - .build() - .execute_with(|| { - assert_eq!(System::block_number(), 1); - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Balances::vesting_balance(&1), 45); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 56), - Error::::VestingBalance, - ); // Account 1 cannot send more than vested amount - }); -} - -#[test] -fn vested_balance_should_transfer() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .vesting(true) - .build() - .execute_with(|| { - assert_eq!(System::block_number(), 1); - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) - assert_eq!(Balances::vesting_balance(&1), 45); - assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); - }); -} - -#[test] -fn extra_balance_should_transfer() { - ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .vesting(true) - .build() - .execute_with(|| { - assert_eq!(System::block_number(), 1); - assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); - assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); - - let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal - - let user2_free_balance = Balances::free_balance(&2); - assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal - - // Account 1 has only 5 units vested at block 1 (plus 150 unvested) - assert_eq!(Balances::vesting_balance(&1), 45); - assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained - - // Account 2 has no units vested at block 1, but gained 100 - assert_eq!(Balances::vesting_balance(&2), 200); - assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained - }); -} - -#[test] -fn liquid_funds_should_transfer_with_delayed_vesting() { - ExtBuilder::default() - .existential_deposit(256) - .monied(true) - .vesting(true) - .build() - .execute_with(|| { - assert_eq!(System::block_number(), 1); - let user12_free_balance = Balances::free_balance(&12); - - assert_eq!(user12_free_balance, 2560); // Account 12 has free balance - // Account 12 has liquid funds - assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); - - // Account 12 has delayed vesting - let user12_vesting_schedule = VestingSchedule { - locked: 256 * 5, - per_block: 64, // Vesting over 20 blocks - starting_block: 10, - }; - assert_eq!(Balances::vesting(&12), Some(user12_vesting_schedule)); - - // Account 12 can still send liquid funds - assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); - }); -} - -#[test] -fn burn_must_work() { - ExtBuilder::default().monied(true).build().execute_with(|| { - let init_total_issuance = Balances::total_issuance(); - let imbalance = Balances::burn(10); - assert_eq!(Balances::total_issuance(), init_total_issuance - 10); - drop(imbalance); - assert_eq!(Balances::total_issuance(), init_total_issuance); - }); -} - -#[test] -fn transfer_keep_alive_works() { - ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 100); - assert_err!( - Balances::transfer_keep_alive(Some(1).into(), 2, 100), - Error::::KeepAlive - ); - assert_eq!(Balances::is_dead_account(&1), false); - assert_eq!(Balances::total_balance(&1), 100); - assert_eq!(Balances::total_balance(&2), 0); - }); -} - -#[test] -#[should_panic="the balance of any account should always be more than existential deposit."] -fn cannot_set_genesis_value_below_ed() { - mock::EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = 11); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let _ = GenesisConfig:: { - balances: vec![(1, 10)], - vesting: vec![], - }.assimilate_storage(&mut t).unwrap(); -} - -#[test] -fn dust_moves_between_free_and_reserved() { - ExtBuilder::default() - .existential_deposit(100) - .build() - .execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 100)); - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 100, 100)); - // Check balance - assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::reserved_balance(1), 100); - assert_eq!(Balances::free_balance(2), 100); - assert_eq!(Balances::reserved_balance(2), 100); - - // Drop 1 free_balance below ED - assert_ok!(Balances::transfer(Some(1).into(), 2, 1)); - // Check balance, the other 99 should move to reserved_balance - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 199); - - // Reset accounts - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 100)); - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 2, 100, 100)); - - // Drop 2 reserved_balance below ED - Balances::unreserve(&2, 1); - // Check balance, all 100 should move to free_balance - assert_eq!(Balances::free_balance(2), 200); - assert_eq!(Balances::reserved_balance(2), 0); - - // An account with both too little free and reserved is completely killed - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 99, 99)); - // Check balance is 0 for everything - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - }); +//! Macro for creating the tests for the module. + +#[macro_export] +macro_rules! decl_tests { + ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { + + use crate::*; + use sp_runtime::{Fixed64, traits::{SignedExtension, BadOrigin}}; + use frame_support::{ + assert_noop, assert_ok, assert_err, + traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, + Currency, ReservableCurrency, ExistenceRequirement::AllowDeath} + }; + use pallet_transaction_payment::ChargeTransactionPayment; + use frame_system::RawOrigin; + + const ID_1: LockIdentifier = *b"1 "; + const ID_2: LockIdentifier = *b"2 "; + + pub type System = frame_system::Module<$test>; + pub type Balances = Module<$test>; + + pub const CALL: &<$test as frame_system::Trait>::Call = &(); + + /// create a transaction info struct from weight. Handy to avoid building the whole struct. + pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, pays_fee: true, ..Default::default() } + } + + #[test] + fn basic_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + Balances::set_lock(ID_1, &1, 9, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 5, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn partial_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_removal_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::all()); + Balances::remove_lock(ID_1, &1); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_replacement_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::all()); + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn double_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + Balances::set_lock(ID_2, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn combination_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::none()); + Balances::set_lock(ID_2, &1, 0, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_value_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 2, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 8, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn lock_reasons_should_work() { + <$ext_builder>::default() + .existential_deposit(1) + .monied(true) + .build() + .execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::put(Fixed64::from_natural(1)); + Balances::set_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); + assert_noop!( + >::transfer(&1, &2, 1, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + assert_noop!( + >::reserve(&1, 1), + Error::<$test, _>::LiquidityRestrictions + ); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + info_from_weight(1), + 1, + ).is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + info_from_weight(1), + 1, + ).is_ok()); + + Balances::set_lock(ID_1, &1, 10, WithdrawReason::TransactionPayment.into()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::reserve(&1, 1)); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + info_from_weight(1), + 1, + ).is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + info_from_weight(1), + 1, + ).is_err()); + }); + } + + #[test] + fn lock_block_number_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + System::set_block_number(2); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn lock_reasons_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReason::Transfer.into()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::none()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn default_indexing_on_new_accounts_should_not_work2() { + <$ext_builder>::default() + .existential_deposit(10) + .monied(true) + .build() + .execute_with(|| { + assert_eq!(Balances::is_dead_account(&5), true); + // account 5 should not exist + // ext_deposit is 10, value is 9, not satisfies for ext_deposit + assert_noop!( + Balances::transfer(Some(1).into(), 5, 9), + Error::<$test, _>::ExistentialDeposit, + ); + assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist + assert_eq!(Balances::free_balance(1), 100); + }); + } + + #[test] + fn reserved_balance_should_prevent_reclaim_count() { + <$ext_builder>::default() + .existential_deposit(256 * 1) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(Balances::is_dead_account(&2), false); + assert_eq!(Balances::is_dead_account(&5), true); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Balances::free_balance(2), 255); // "free" account deleted." + assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. + assert_eq!(Balances::is_dead_account(&2), false); + assert_eq!(System::account_nonce(&2), 1); + + // account 4 tries to take index 1 for account 5. + assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + assert_eq!(Balances::is_dead_account(&5), false); + + assert!(Balances::slash(&2, 256 * 19 + 2).1.is_zero()); // account 2 gets slashed + // "reserve" account reduced to 255 (below ED) so account deleted + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(System::account_nonce(&2), 0); // nonce zero + assert_eq!(Balances::is_dead_account(&2), true); + + // account 4 tries to take index 1 again for account 6. + assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::is_dead_account(&6), false); + }); + } + + #[test] + fn reward_should_work() { + <$ext_builder>::default().monied(true).build().execute_with(|| { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(>::get(), 120); + }); + } + + #[test] + fn dust_account_removal_should_work() { + <$ext_builder>::default() + .existential_deposit(100) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 2000); + // index 1 (account 2) becomes zombie + assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 1901); + assert_eq!(System::account_nonce(&2), 0); + }); + } + + #[test] + fn balance_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); + } + + #[test] + fn balance_transfer_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); + } + + #[test] + fn force_transfer_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_noop!( + Balances::force_transfer(Some(2).into(), 1, 2, 69), + BadOrigin, + ); + assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); + } + + #[test] + fn reserving_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 69); + }); + } + + #[test] + fn balance_transfer_when_reserved_should_not_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 69), + Error::<$test, _>::InsufficientBalance, + ); + }); + } + + #[test] + fn deducting_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(1), 42); + }); + } + + #[test] + fn refunding_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + Balances::mutate_account(&1, |a| a.reserved = 69); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn slashing_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 69).1.is_zero()); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 42); + assert_eq!(>::get(), 42); + }); + } + + #[test] + fn slashing_incomplete_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::reserve(&1, 21)); + assert_eq!(Balances::slash(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(>::get(), 0); + }); + } + + #[test] + fn unreserving_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + Balances::unreserve(&1, 42); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 42); + }); + } + + #[test] + fn slashing_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_eq!(Balances::slash_reserved(&1, 42).1, 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(>::get(), 69); + }); + } + + #[test] + fn slashing_incomplete_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 42)); + assert_eq!(Balances::slash_reserved(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(>::get(), 69); + }); + } + + #[test] + fn repatriating_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Free), 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); + } + + #[test] + fn transferring_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Reserved), 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(2), 41); + assert_eq!(Balances::free_balance(2), 1); + }); + } + + #[test] + fn transferring_reserved_balance_to_nonexistent_should_fail() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_noop!(Balances::repatriate_reserved(&1, &2, 42, Status::Free), Error::<$test, _>::DeadAccount); + }); + } + + #[test] + fn transferring_incomplete_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 69, Status::Free), 28); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); + } + + #[test] + fn transferring_too_high_value_should_not_panic() { + <$ext_builder>::default().build().execute_with(|| { + Balances::make_free_balance_be(&1, u64::max_value()); + Balances::make_free_balance_be(&2, 1); + + assert_err!( + Balances::transfer(Some(1).into(), 2, u64::max_value()), + Error::<$test, _>::Overflow, + ); + + assert_eq!(Balances::free_balance(1), u64::max_value()); + assert_eq!(Balances::free_balance(2), 1); + }); + } + + #[test] + fn account_create_on_free_too_low_with_other() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + assert_eq!(>::get(), 100); + + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(>::get(), 100); + }) + } + + #[test] + fn account_create_on_free_too_low() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(>::get(), 0); + }) + } + + #[test] + fn account_removal_on_free_too_low() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + assert_eq!(>::get(), 0); + + // Setup two accounts with free balance above the existential threshold. + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 110); + + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::free_balance(2), 110); + assert_eq!(>::get(), 220); + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the existential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::free_balance(2), 130); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 130); + }); + } + + #[test] + fn burn_must_work() { + <$ext_builder>::default().monied(true).build().execute_with(|| { + let init_total_issuance = Balances::total_issuance(); + let imbalance = Balances::burn(10); + assert_eq!(Balances::total_issuance(), init_total_issuance - 10); + drop(imbalance); + assert_eq!(Balances::total_issuance(), init_total_issuance); + }); + } + + #[test] + fn transfer_keep_alive_works() { + <$ext_builder>::default().existential_deposit(1).build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + assert_noop!( + Balances::transfer_keep_alive(Some(1).into(), 2, 100), + Error::<$test, _>::KeepAlive + ); + assert_eq!(Balances::is_dead_account(&1), false); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 0); + }); + } + + #[test] + #[should_panic = "the balance of any account should always be more than existential deposit."] + fn cannot_set_genesis_value_below_ed() { + ($existential_deposit).with(|v| *v.borrow_mut() = 11); + let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); + let _ = GenesisConfig::<$test> { + balances: vec![(1, 10)], + }.assimilate_storage(&mut t).unwrap(); + } + + #[test] + fn dust_moves_between_free_and_reserved() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); + // Check balance + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 0); + + // Reserve some free balance + assert_ok!(Balances::reserve(&1, 50)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 50); + + // Reserve the rest of the free balance + assert_ok!(Balances::reserve(&1, 50)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 100); + + // Unreserve everything + Balances::unreserve(&1, 100); + // Check balance, all 100 should move to free_balance + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn account_deleted_when_just_dust() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 50, 50)); + // Check balance + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 50); + + // Reserve some free balance + let _ = Balances::slash(&1, 1); + // The account should be dead. + assert!(Balances::is_dead_account(&1)); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + } } diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs new file mode 100644 index 0000000000000000000000000000000000000000..c566c9a9d009d69eb629f1d92b6801aaede7546b --- /dev/null +++ b/frame/balances/src/tests_composite.rs @@ -0,0 +1,136 @@ +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utilities + +use sp_runtime::{Perbill, traits::{ConvertInto, IdentityLookup}, testing::Header}; +use sp_core::H256; +use sp_io; +use frame_support::{impl_outer_origin, parameter_types}; +use frame_support::traits::Get; +use frame_support::weights::{Weight, DispatchInfo}; +use std::cell::RefCell; +use crate::{GenesisConfig, Module, Trait, decl_tests}; + +use frame_system as system; +impl_outer_origin!{ + pub enum Origin for Test {} +} + +thread_local! { + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl frame_system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = (); + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = super::AccountData; + type OnNewAccount = (); + type OnReapAccount = Module; +} +parameter_types! { + pub const TransactionBaseFee: u64 = 0; + pub const TransactionByteFee: u64 = 1; +} +impl pallet_transaction_payment::Trait for Test { + type Currency = Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); +} +impl Trait for Test { + type Balance = u64; + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = system::Module; +} + +pub struct ExtBuilder { + existential_deposit: u64, + monied: bool, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + monied: false, + } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + GenesisConfig:: { + balances: if self.monied { + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit) + ] + } else { + vec![] + }, + }.assimilate_storage(&mut t).unwrap(); + t.into() + } +} + +decl_tests!{ Test, ExtBuilder, EXISTENTIAL_DEPOSIT } diff --git a/frame/balances/src/mock.rs b/frame/balances/src/tests_local.rs similarity index 68% rename from frame/balances/src/mock.rs rename to frame/balances/src/tests_local.rs index 5a3d671e8dbc91aa191f7ae4d2aeb178f62d6b05..a63046e901d498eb8a1cfadb21976b6c8d4f3d37 100644 --- a/frame/balances/src/mock.rs +++ b/frame/balances/src/tests_local.rs @@ -20,10 +20,10 @@ use sp_runtime::{Perbill, traits::{ConvertInto, IdentityLookup}, testing::Header use sp_core::H256; use sp_io; use frame_support::{impl_outer_origin, parameter_types}; -use frame_support::traits::Get; +use frame_support::traits::{Get, StorageMapShim}; use frame_support::weights::{Weight, DispatchInfo}; use std::cell::RefCell; -use crate::{GenesisConfig, Module, Trait}; +use crate::{GenesisConfig, Module, Trait, decl_tests}; use frame_system as system; impl_outer_origin!{ @@ -31,9 +31,7 @@ impl_outer_origin!{ } thread_local! { - pub(crate) static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); - static TRANSFER_FEE: RefCell = RefCell::new(0); - static CREATION_FEE: RefCell = RefCell::new(0); + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); } pub struct ExistentialDeposit; @@ -41,16 +39,6 @@ impl Get for ExistentialDeposit { fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } } -pub struct TransferFee; -impl Get for TransferFee { - fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } -} - -pub struct CreationFee; -impl Get for CreationFee { - fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } -} - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Test; @@ -77,6 +65,9 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = super::AccountData; + type OnNewAccount = (); + type OnReapAccount = Module; } parameter_types! { pub const TransactionBaseFee: u64 = 0; @@ -92,32 +83,26 @@ impl pallet_transaction_payment::Trait for Test { } impl Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); - type Event = (); type DustRemoval = (); - type TransferPayment = (); + type Event = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = StorageMapShim< + super::Account, + system::CallOnCreatedAccount, + system::CallKillAccount, + u64, super::AccountData + >; } pub struct ExtBuilder { existential_deposit: u64, - transfer_fee: u64, - creation_fee: u64, monied: bool, - vesting: bool, } impl Default for ExtBuilder { fn default() -> Self { Self { - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, + existential_deposit: 1, monied: false, - vesting: false, } } } @@ -126,15 +111,6 @@ impl ExtBuilder { self.existential_deposit = existential_deposit; self } - #[allow(dead_code)] - pub fn transfer_fee(mut self, transfer_fee: u64) -> Self { - self.transfer_fee = transfer_fee; - self - } - pub fn creation_fee(mut self, creation_fee: u64) -> Self { - self.creation_fee = creation_fee; - self - } pub fn monied(mut self, monied: bool) -> Self { self.monied = monied; if self.existential_deposit == 0 { @@ -142,14 +118,8 @@ impl ExtBuilder { } self } - pub fn vesting(mut self, vesting: bool) -> Self { - self.vesting = vesting; - self - } pub fn set_associated_consts(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); - CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); } pub fn build(self) -> sp_io::TestExternalities { self.set_associated_consts(); @@ -166,26 +136,9 @@ impl ExtBuilder { } else { vec![] }, - vesting: if self.vesting && self.monied { - vec![ - (1, 0, 10, 5 * self.existential_deposit), - (2, 10, 20, 0), - (12, 10, 20, 5 * self.existential_deposit) - ] - } else { - vec![] - }, }.assimilate_storage(&mut t).unwrap(); t.into() } } -pub type System = frame_system::Module; -pub type Balances = Module; - -pub const CALL: &::Call = &(); - -/// create a transaction info struct from weight. Handy to avoid building the whole struct. -pub fn info_from_weight(w: Weight) -> DispatchInfo { - DispatchInfo { weight: w, ..Default::default() } -} +decl_tests!{ Test, ExtBuilder, EXISTENTIAL_DEPOSIT } diff --git a/frame/collective/Cargo.toml b/frame/collective/Cargo.toml index a2150b82dd5e619c3402dd3df80a3605c37ab672..8f7ffa76534908320aeed2761ef09b3d9d5a789c 100644 --- a/frame/collective/Cargo.toml +++ b/frame/collective/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-collective" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 0d1032cca429cdb3aaa5e13f92c93af04954c1ec..e9e6c75b836be27ebdf1dd56500c1192f15dd7d2 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -87,9 +87,11 @@ decl_storage! { /// The hashes of the active proposals. pub Proposals get(fn proposals): Vec; /// Actual proposal for a given hash, if it's current. - pub ProposalOf get(fn proposal_of): map T::Hash => Option<>::Proposal>; + pub ProposalOf get(fn proposal_of): + map hasher(blake2_256) T::Hash => Option<>::Proposal>; /// Votes on a given proposal, if it is ongoing. - pub Voting get(fn voting): map T::Hash => Option>; + pub Voting get(fn voting): + map hasher(blake2_256) T::Hash => Option>; /// Proposals so far. pub ProposalCount get(fn proposal_count): u32; /// The current members of the collective. This is stored sorted (just by value). @@ -189,7 +191,7 @@ decl_module! { let proposal_hash = T::Hashing::hash_of(&proposal); - ensure!(!>::exists(proposal_hash), Error::::DuplicateProposal); + ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); if threshold < 2 { let seats = Self::members().len() as MemberCount; @@ -431,6 +433,9 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { type Origin = Origin; @@ -452,7 +457,7 @@ mod tests { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Collective: collective::::{Module, Call, Event, Origin, Config}, DefaultCollective: collective::{Module, Call, Event, Origin, Config}, } diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index d583809f0bfa7c3f933b7ea333abac3cf13bfc35..159e9f9d0c199464d13f044b4ea92a475a38323a 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-contracts" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } @@ -17,6 +18,7 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../primitives sp-sandbox = { version = "0.8.0", default-features = false, path = "../../primitives/sandbox" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "common" } [dev-dependencies] wabt = "0.9.2" @@ -41,4 +43,5 @@ std = [ "parity-wasm/std", "pwasm-utils/std", "wasmi-validation/std", + "pallet-contracts-primitives/std", ] diff --git a/frame/contracts/common/Cargo.toml b/frame/contracts/common/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..6e4ee050bd1978b3da839e5f2d07a68192d05390 --- /dev/null +++ b/frame/contracts/common/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pallet-contracts-primitives" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +# This crate should not rely on any of the frame primitives. +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..e54b4c8d55393b6e23ba06a52776ac48a3ad13cc --- /dev/null +++ b/frame/contracts/common/src/lib.rs @@ -0,0 +1,47 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A crate that hosts a common definitions that are relevent for the pallet-contracts. + +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_std::prelude::*; + +/// A result type of a get storage call. +pub type GetStorageResult = Result>, ContractAccessError>; + +/// The possible errors that can happen querying the storage of a contract. +#[derive(Eq, PartialEq, codec::Encode, codec::Decode, sp_runtime::RuntimeDebug)] +pub enum ContractAccessError { + /// The given address doesn't point to a contract. + DoesntExist, + /// The specified contract is a tombstone and thus cannot have any storage. + IsTombstone, +} + +/// A result type of a `rent_projection` call. +pub type RentProjectionResult = + Result, ContractAccessError>; + +#[derive(Eq, PartialEq, codec::Encode, codec::Decode, sp_runtime::RuntimeDebug)] +pub enum RentProjection { + /// Eviction is projected to happen at the specified block number. + EvictionAt(BlockNumber), + /// No eviction is scheduled. + /// + /// E.g. because the contract accumulated enough funds to offset the rent storage costs. + NoEviction, +} diff --git a/frame/contracts/rpc/Cargo.toml b/frame/contracts/rpc/Cargo.toml index 61eb55368eb10eeba46a42affed6312dc961269b..d59260d11f536da807679531e4350edaf9d4e8d8 100644 --- a/frame/contracts/rpc/Cargo.toml +++ b/frame/contracts/rpc/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-contracts-rpc" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } @@ -15,6 +16,7 @@ sp-rpc = { version = "2.0.0", path = "../../../primitives/rpc" } serde = { version = "1.0.101", features = ["derive"] } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } sp-api = { version = "2.0.0", path = "../../../primitives/api" } +pallet-contracts-primitives = { version = "2.0.0", path = "../common" } pallet-contracts-rpc-runtime-api = { version = "0.8.0", path = "./runtime-api" } [dev-dependencies] diff --git a/frame/contracts/rpc/runtime-api/Cargo.toml b/frame/contracts/rpc/runtime-api/Cargo.toml index e0cbd73f807f2314d23d7fc52f42b9b587634d19..dad9b92f6a30891b30f5307a3d732b87f05b9f49 100644 --- a/frame/contracts/rpc/runtime-api/Cargo.toml +++ b/frame/contracts/rpc/runtime-api/Cargo.toml @@ -3,12 +3,14 @@ name = "pallet-contracts-rpc-runtime-api" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-api = { version = "2.0.0", default-features = false, path = "../../../../primitives/api" } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../../../../primitives/std" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../../../primitives/runtime" } +pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "../../common" } [features] default = ["std"] @@ -17,4 +19,5 @@ std = [ "codec/std", "sp-std/std", "sp-runtime/std", + "pallet-contracts-primitives/std", ] diff --git a/frame/contracts/rpc/runtime-api/src/lib.rs b/frame/contracts/rpc/runtime-api/src/lib.rs index 622cac8572141dc6bf96abab2e41e9b401e27c9b..fd83ba6534a4524c8c35c2e290f9138157698094 100644 --- a/frame/contracts/rpc/runtime-api/src/lib.rs +++ b/frame/contracts/rpc/runtime-api/src/lib.rs @@ -22,9 +22,10 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::vec::Vec; -use codec::{Encode, Decode, Codec}; +use codec::{Codec, Decode, Encode}; +use pallet_contracts_primitives::{GetStorageResult, RentProjectionResult}; use sp_runtime::RuntimeDebug; +use sp_std::vec::Vec; /// A result of execution of a contract. #[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] @@ -44,25 +45,12 @@ pub enum ContractExecResult { Error, } -/// A result type of the get storage call. -/// -/// See [`ContractsApi::get_storage`] for more info. -pub type GetStorageResult = Result>, GetStorageError>; - -/// The possible errors that can happen querying the storage of a contract. -#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] -pub enum GetStorageError { - /// The given address doesn't point on a contract. - ContractDoesntExist, - /// The specified contract is a tombstone and thus cannot have any storage. - IsTombstone, -} - sp_api::decl_runtime_apis! { /// The API to interact with contracts without using executive. - pub trait ContractsApi where + pub trait ContractsApi where AccountId: Codec, Balance: Codec, + BlockNumber: Codec, { /// Perform a call from a specified account to a given contract. /// @@ -85,5 +73,13 @@ sp_api::decl_runtime_apis! { address: AccountId, key: [u8; 32], ) -> GetStorageResult; + + /// Returns the projected time a given contract will be able to sustain paying its rent. + /// + /// The returned projection is relevent for the current block, i.e. it is as if the contract + /// was accessed at the current block. + /// + /// Returns `Err` if the contract is in a tombstone state or doesn't exist. + fn rent_projection(address: AccountId) -> RentProjectionResult; } } diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 4c39bc9516f55949b4fa6f0b4f8bb72aff00e27c..b0d6037416edb2e180608ce8bb5f853c41b15fd0 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -18,19 +18,23 @@ use std::sync::Arc; -use sp_blockchain::HeaderBackend; use codec::Codec; use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_derive::rpc; -use sp_core::{H256, Bytes}; -use sp_rpc::number; +use pallet_contracts_primitives::RentProjection; use serde::{Deserialize, Serialize}; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_core::{Bytes, H256}; +use sp_rpc::number; +use sp_runtime::{ + generic::BlockId, + traits::{Block as BlockT, Header as HeaderT}, +}; pub use self::gen_client::Client as ContractsClient; pub use pallet_contracts_rpc_runtime_api::{ - self as runtime_api, ContractExecResult, ContractsApi as ContractsRuntimeApi, GetStorageResult, + self as runtime_api, ContractExecResult, ContractsApi as ContractsRuntimeApi, }; const RUNTIME_ERROR: i64 = 1; @@ -46,13 +50,13 @@ const CONTRACT_IS_A_TOMBSTONE: i64 = 3; /// https://docs.google.com/spreadsheets/d/1h0RqncdqiWI4KgxO0z9JIpZEJESXjX_ZCK6LFX6veDo/view const GAS_PER_SECOND: u64 = 1_000_000_000; -/// A private newtype for converting `GetStorageError` into an RPC error. -struct GetStorageError(runtime_api::GetStorageError); -impl From for Error { - fn from(e: GetStorageError) -> Error { - use runtime_api::GetStorageError::*; +/// A private newtype for converting `ContractAccessError` into an RPC error. +struct ContractAccessError(pallet_contracts_primitives::ContractAccessError); +impl From for Error { + fn from(e: ContractAccessError) -> Error { + use pallet_contracts_primitives::ContractAccessError::*; match e.0 { - ContractDoesntExist => Error { + DoesntExist => Error { code: ErrorCode::ServerError(CONTRACT_DOESNT_EXIST), message: "The specified contract doesn't exist.".into(), data: None, @@ -61,7 +65,7 @@ impl From for Error { code: ErrorCode::ServerError(CONTRACT_IS_A_TOMBSTONE), message: "The contract is a tombstone and doesn't have any storage.".into(), data: None, - } + }, } } } @@ -97,19 +101,18 @@ pub enum RpcContractExecResult { impl From for RpcContractExecResult { fn from(r: ContractExecResult) -> Self { match r { - ContractExecResult::Success { status, data } => { - RpcContractExecResult::Success { status, data: data.into() } - }, - ContractExecResult::Error => { - RpcContractExecResult::Error(()) + ContractExecResult::Success { status, data } => RpcContractExecResult::Success { + status, + data: data.into(), }, + ContractExecResult::Error => RpcContractExecResult::Error(()), } } } /// Contracts RPC methods. #[rpc] -pub trait ContractsApi { +pub trait ContractsApi { /// Executes a call to a contract. /// /// This call is performed locally without submitting any transactions. Thus executing this @@ -132,6 +135,19 @@ pub trait ContractsApi { key: H256, at: Option, ) -> Result>; + + /// Returns the projected time a given contract will be able to sustain paying its rent. + /// + /// The returned projection is relevent for the given block, i.e. it is as if the contract was + /// accessed at the beginning of that block. + /// + /// Returns `None` if the contract is exempted from rent. + #[rpc(name = "contracts_rentProjection")] + fn rent_projection( + &self, + address: AccountId, + at: Option, + ) -> Result>; } /// An implementation of contract specific RPC methods. @@ -149,13 +165,22 @@ impl Contracts { } } } - -impl ContractsApi<::Hash, AccountId, Balance> - for Contracts +impl + ContractsApi< + ::Hash, + <::Header as HeaderT>::Number, + AccountId, + Balance, + > for Contracts where Block: BlockT, C: Send + Sync + 'static + ProvideRuntimeApi + HeaderBackend, - C::Api: ContractsRuntimeApi, + C::Api: ContractsRuntimeApi< + Block, + AccountId, + Balance, + <::Header as HeaderT>::Number, + >, AccountId: Codec, Balance: Codec, { @@ -188,8 +213,7 @@ where code: ErrorCode::InvalidParams, message: format!( "Requested gas limit is greater than maximum allowed: {} > {}", - gas_limit, - max_gas_limit + gas_limit, max_gas_limit ), data: None, }); @@ -197,11 +221,7 @@ where let exec_result = api .call(&at, origin, dest, value, gas_limit, input_data.to_vec()) - .map_err(|e| Error { - code: ErrorCode::ServerError(RUNTIME_ERROR), - message: "Runtime trapped while executing a contract.".into(), - data: Some(format!("{:?}", e).into()), - })?; + .map_err(|e| runtime_error_into_rpc_err(e))?; Ok(exec_result.into()) } @@ -217,19 +237,43 @@ where // If the block hash is not supplied assume the best block. self.client.info().best_hash)); - let get_storage_result = api + let result = api .get_storage(&at, address, key.into()) - .map_err(|e| - // Handle general API calling errors. - Error { - code: ErrorCode::ServerError(RUNTIME_ERROR), - message: "Runtime trapped while querying storage.".into(), - data: Some(format!("{:?}", e).into()), - })? - .map_err(GetStorageError)? + .map_err(|e| runtime_error_into_rpc_err(e))? + .map_err(ContractAccessError)? .map(Bytes); - Ok(get_storage_result) + Ok(result) + } + + fn rent_projection( + &self, + address: AccountId, + at: Option<::Hash>, + ) -> Result::Header as HeaderT>::Number>> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash)); + + let result = api + .rent_projection(&at, address) + .map_err(|e| runtime_error_into_rpc_err(e))? + .map_err(ContractAccessError)?; + + Ok(match result { + RentProjection::NoEviction => None, + RentProjection::EvictionAt(block_num) => Some(block_num), + }) + } +} + +/// Converts a runtime trap into an RPC error. +fn runtime_error_into_rpc_err(err: impl std::fmt::Debug) -> Error { + Error { + code: ErrorCode::ServerError(RUNTIME_ERROR), + message: "Runtime trapped".into(), + data: Some(format!("{:?}", err).into()), } } @@ -240,7 +284,7 @@ mod tests { #[test] fn should_serialize_deserialize_properly() { fn test(expected: &str) { - let res: RpcContractExecResult = serde_json::from_str(expected).unwrap(); + let res: RpcContractExecResult = serde_json::from_str(expected).unwrap(); let actual = serde_json::to_string(&res).unwrap(); assert_eq!(actual, expected); } diff --git a/frame/contracts/src/account_db.rs b/frame/contracts/src/account_db.rs index 3615673f2d9dcbfe5985bc357774e36e9f2fbfb4..5204f1003a6c5920714c483a4329de9c1cfdf386 100644 --- a/frame/contracts/src/account_db.rs +++ b/frame/contracts/src/account_db.rs @@ -26,7 +26,7 @@ use sp_std::collections::btree_map::{BTreeMap, Entry}; use sp_std::prelude::*; use sp_io::hashing::blake2_256; use sp_runtime::traits::{Bounded, Zero}; -use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance, UpdateBalanceOutcome}; +use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance}; use frame_support::{storage::child, StorageMap}; use frame_system; @@ -137,7 +137,7 @@ impl AccountDb for DirectAccountDb { >::get(account).and_then(|i| i.as_alive().map(|i| i.rent_allowance)) } fn contract_exists(&self, account: &T::AccountId) -> bool { - >::exists(account) + >::contains_key(account) } fn get_balance(&self, account: &T::AccountId) -> BalanceOf { T::Currency::free_balance(account) @@ -146,10 +146,12 @@ impl AccountDb for DirectAccountDb { let mut total_imbalance = SignedImbalance::zero(); for (address, changed) in s.into_iter() { if let Some(balance) = changed.balance() { - let (imbalance, outcome) = T::Currency::make_free_balance_be(&address, balance); + let existed = !T::Currency::total_balance(&address).is_zero(); + let imbalance = T::Currency::make_free_balance_be(&address, balance); + let exists = !T::Currency::total_balance(&address).is_zero(); total_imbalance = total_imbalance.merge(imbalance); - if let UpdateBalanceOutcome::AccountKilled = outcome { - // Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback + if existed && !exists { + // Account killed. This will ultimately lead to calling `OnReapAccount` callback // which will make removal of CodeHashOf and AccountStorage for this account. // In order to avoid writing over the deleted properties we `continue` here. continue; diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 9d786c320b5a90dfa15daef058f7525e6dd98725..ec9fad93b4d4f0bb7b002f6880e70364f4bf87e5 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -161,6 +161,9 @@ pub trait Ext { /// Returns the minimum balance that is required for creating an account. fn minimum_balance(&self) -> BalanceOf; + /// Returns the deposit required to create a tombstone upon contract eviction. + fn tombstone_deposit(&self) -> BalanceOf; + /// Returns a random number for the current block with the given subject. fn random(&self, subject: &[u8]) -> SeedOf; @@ -353,10 +356,10 @@ where }); } - // Assumption: pay_rent doesn't collide with overlay because - // pay_rent will be done on first call and dest contract and balance + // Assumption: `collect_rent` doesn't collide with overlay because + // `collect_rent` will be done on first call and destination contract and balance // cannot be changed before the first call - let contract_info = rent::pay_rent::(&dest); + let contract_info = rent::collect_rent::(&dest); // Calls to dead contracts always fail. if let Some(ContractInfo::Tombstone(_)) = contract_info { @@ -551,7 +554,6 @@ where #[derive(Copy, Clone)] pub enum TransferFeeKind { ContractInstantiate, - AccountCreate, Transfer, } @@ -569,8 +571,7 @@ impl Token for TransferFeeToken> { fn calculate_amount(&self, metadata: &Config) -> Gas { let balance_fee = match self.kind { TransferFeeKind::ContractInstantiate => metadata.contract_account_instantiate_fee, - TransferFeeKind::AccountCreate => metadata.account_create_fee, - TransferFeeKind::Transfer => metadata.transfer_fee, + TransferFeeKind::Transfer => return metadata.schedule.transfer_cost, }; approx_gas_for_balance(self.gas_price, balance_fee) } @@ -609,28 +610,14 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( use self::TransferCause::*; use self::TransferFeeKind::*; - let to_balance = ctx.overlay.get_balance(dest); - - // `would_create` indicates whether the account will be created if this transfer gets executed. - // This flag is orthogonal to `cause. - // For example, we can instantiate a contract at the address which already has some funds. In this - // `would_create` will be `false`. Another example would be when this function is called from `call`, - // and account with the address `dest` doesn't exist yet `would_create` will be `true`. - let would_create = to_balance.is_zero(); - let token = { let kind: TransferFeeKind = match cause { // If this function is called from `Instantiate` routine, then we always // charge contract account creation fee. Instantiate => ContractInstantiate, - // Otherwise the fee depends on whether we create a new account or transfer - // to an existing one. - Call => if would_create { - TransferFeeKind::AccountCreate - } else { - TransferFeeKind::Transfer - }, + // Otherwise the fee is to transfer to an account. + Call => TransferFeeKind::Transfer, }; TransferFeeToken { kind, @@ -648,7 +635,8 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( Some(b) => b, None => Err("balance too low to send value")?, }; - if would_create && value < ctx.config.existential_deposit { + let to_balance = ctx.overlay.get_balance(dest); + if to_balance.is_zero() && value < ctx.config.existential_deposit { Err("value too low to create account")? } T::Currency::ensure_can_withdraw( @@ -779,6 +767,10 @@ where self.ctx.config.existential_deposit } + fn tombstone_deposit(&self) -> BalanceOf { + self.ctx.config.tombstone_deposit + } + fn deposit_event(&mut self, topics: Vec, data: Vec) { self.ctx.deferred.push(DeferredAction::DepositEvent { topics, @@ -1002,7 +994,7 @@ mod tests { let mut gas_meter = GasMeter::::with_limit(1000, 1); - let result = ctx.instantiate(0, &mut gas_meter, &code, vec![]); + let result = ctx.instantiate(1, &mut gas_meter, &code, vec![]); assert_matches!(result, Ok(_)); let mut toks = gas_meter.tokens().iter(); @@ -1098,7 +1090,7 @@ mod tests { toks, ExecFeeToken::Call, TransferFeeToken { - kind: TransferFeeKind::AccountCreate, + kind: TransferFeeKind::Transfer, gas_price: 1u64 }, ); @@ -1295,8 +1287,10 @@ mod tests { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&ALICE, 1); + let result = ctx.instantiate( - 0, + 1, &mut GasMeter::::with_limit(10000, 1), &input_data_ch, vec![1, 2, 3, 4], @@ -1341,6 +1335,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&BOB, 1); ctx.overlay.instantiate_contract(&BOB, recurse_ch).unwrap(); let result = ctx.call( @@ -1654,8 +1649,10 @@ mod tests { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&ALICE, 1); + let result = ctx.instantiate( - 0, + 1, &mut GasMeter::::with_limit(10000, 1), &rent_allowance_ch, vec![], diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index e0cc7d4bfb4c7f522d266ffda3303a62bee18a16..c8572daaa43d487efd84725b26a6492beaa63794 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -17,7 +17,7 @@ use crate::{GasSpent, Module, Trait, BalanceOf, NegativeImbalanceOf}; use sp_std::convert::TryFrom; use sp_runtime::traits::{ - CheckedMul, Zero, SaturatedConversion, SimpleArithmetic, UniqueSaturatedInto, + CheckedMul, Zero, SaturatedConversion, AtLeast32Bit, UniqueSaturatedInto, }; use frame_support::{ traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReason}, StorageValue, @@ -248,7 +248,7 @@ pub fn refund_unused_gas( /// A little handy utility for converting a value in balance units into approximate value in gas units /// at the given gas price. pub fn approx_gas_for_balance(gas_price: Balance, balance: Balance) -> Gas - where Balance: SimpleArithmetic + where Balance: AtLeast32Bit { (balance / gas_price).saturated_into::() } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 40ce86518a5e445e1bf83245cc6cd917f0c79626..19e070bd03dc97f65db26084b14b34633eb085bc 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -125,9 +125,10 @@ use frame_support::{ parameter_types, IsSubType, weights::DispatchInfo, }; -use frame_support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get, Time, Randomness}; +use frame_support::traits::{OnReapAccount, OnUnbalanced, Currency, Get, Time, Randomness}; use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root}; use sp_core::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; +use pallet_contracts_primitives::{RentProjection, ContractAccessError}; pub type CodeHash = ::Hash; pub type TrieId = Vec; @@ -400,12 +401,6 @@ pub trait Trait: frame_system::Trait { /// to removal of a contract. type SurchargeReward: Get>; - /// The fee required to make a transfer. - type TransferFee: Get>; - - /// The fee required to create an account. - type CreationFee: Get>; - /// The fee to be paid for making a transaction; the base. type TransactionBaseFee: Get>; @@ -519,12 +514,6 @@ decl_module! { /// to removal of a contract. const SurchargeReward: BalanceOf = T::SurchargeReward::get(); - /// The fee required to make a transfer. - const TransferFee: BalanceOf = T::TransferFee::get(); - - /// The fee required to create an account. - const CreationFee: BalanceOf = T::CreationFee::get(); - /// The fee to be paid for making a transaction; the base. const TransactionBaseFee: BalanceOf = T::TransactionBaseFee::get(); @@ -669,7 +658,7 @@ decl_module! { }; // If poking the contract has lead to eviction of the contract, give out the rewards. - if rent::try_evict::(&dest, handicap) == rent::RentOutcome::Evicted { + if rent::snitch_contract_should_be_evicted::(&dest, handicap) { T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?; } } @@ -680,14 +669,6 @@ decl_module! { } } -/// The possible errors that can happen querying the storage of a contract. -pub enum GetStorageError { - /// The given address doesn't point on a contract. - ContractDoesntExist, - /// The specified contract is a tombstone and thus cannot have any storage. - IsTombstone, -} - /// Public APIs provided by the contracts module. impl Module { /// Perform a call to a specified contract. @@ -710,11 +691,11 @@ impl Module { pub fn get_storage( address: T::AccountId, key: [u8; 32], - ) -> sp_std::result::Result>, GetStorageError> { + ) -> sp_std::result::Result>, ContractAccessError> { let contract_info = >::get(&address) - .ok_or(GetStorageError::ContractDoesntExist)? + .ok_or(ContractAccessError::DoesntExist)? .get_alive() - .ok_or(GetStorageError::IsTombstone)?; + .ok_or(ContractAccessError::IsTombstone)?; let maybe_value = AccountDb::::get_storage( &DirectAccountDb, @@ -724,6 +705,12 @@ impl Module { ); Ok(maybe_value) } + + pub fn rent_projection( + address: T::AccountId, + ) -> sp_std::result::Result, ContractAccessError> { + rent::compute_rent_projection::(&address) + } } impl Module { @@ -942,20 +929,20 @@ decl_storage! { /// Current cost schedule for contracts. CurrentSchedule get(fn current_schedule) config(): Schedule = Schedule::default(); /// A mapping from an original code hash to the original code, untouched by instrumentation. - pub PristineCode: map CodeHash => Option>; + pub PristineCode: map hasher(blake2_256) CodeHash => Option>; /// A mapping between an original code hash and instrumented wasm code, ready for execution. - pub CodeStorage: map CodeHash => Option; + pub CodeStorage: map hasher(blake2_256) CodeHash => Option; /// The subtrie counter. pub AccountCounter: u64 = 0; /// The code associated with a given account. - pub ContractInfoOf: map T::AccountId => Option>; + pub ContractInfoOf: map hasher(blake2_256) T::AccountId => Option>; /// The price of one unit of gas. GasPrice get(fn gas_price) config(): BalanceOf = 1.into(); } } -impl OnFreeBalanceZero for Module { - fn on_free_balance_zero(who: &T::AccountId) { +impl OnReapAccount for Module { + fn on_reap_account(who: &T::AccountId) { if let Some(ContractInfo::Alive(info)) = >::take(who) { child::kill_storage(&info.trie_id, info.child_trie_unique_id()); } @@ -969,11 +956,10 @@ impl OnFreeBalanceZero for Module { pub struct Config { pub schedule: Schedule, pub existential_deposit: BalanceOf, + pub tombstone_deposit: BalanceOf, pub max_depth: u32, pub max_value_size: u32, pub contract_account_instantiate_fee: BalanceOf, - pub account_create_fee: BalanceOf, - pub transfer_fee: BalanceOf, } impl Config { @@ -981,11 +967,10 @@ impl Config { Config { schedule: >::current_schedule(), existential_deposit: T::Currency::minimum_balance(), + tombstone_deposit: T::TombstoneDeposit::get(), max_depth: T::MaxDepth::get(), max_value_size: T::MaxValueSize::get(), contract_account_instantiate_fee: T::ContractFee::get(), - account_create_fee: T::CreationFee::get(), - transfer_fee: T::TransferFee::get(), } } } @@ -1030,6 +1015,9 @@ pub struct Schedule { /// Gas cost per one byte written to the sandbox memory. pub sandbox_data_write_cost: Gas, + /// Cost for a simple balance transfer. + pub transfer_cost: Gas, + /// The maximum number of topics supported by an event. pub max_event_topics: u32, @@ -1068,6 +1056,7 @@ impl Default for Schedule { instantiate_base_cost: 175, sandbox_data_read_cost: 1, sandbox_data_write_cost: 1, + transfer_cost: 100, max_event_topics: 4, max_stack_height: 64 * 1024, max_memory_pages: 16, @@ -1101,6 +1090,7 @@ impl sp_std::fmt::Debug for CheckBlockGasLimit { } impl SignedExtension for CheckBlockGasLimit { + const IDENTIFIER: &'static str = "CheckBlockGasLimit"; type AccountId = T::AccountId; type Call = ::Call; type AdditionalSigned = (); diff --git a/frame/contracts/src/rent.rs b/frame/contracts/src/rent.rs index 508511da4cbe3b00d96b85fc444296906493404d..49beebbf0c20270137e4221ac33757993f432a10 100644 --- a/frame/contracts/src/rent.rs +++ b/frame/contracts/src/rent.rs @@ -14,102 +14,167 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{Module, RawEvent, BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo, - Trait, AliveContractInfo}; -use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero, - SaturatedConversion}; -use frame_support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason, OnUnbalanced}; -use frame_support::StorageMap; +//! A module responsible for computing the right amount of weight and charging it. + +use crate::{ + AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Module, RawEvent, + TombstoneContractInfo, Trait, +}; use frame_support::storage::child; +use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason}; +use frame_support::StorageMap; +use pallet_contracts_primitives::{ContractAccessError, RentProjection, RentProjectionResult}; +use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero}; -#[derive(PartialEq, Eq, Copy, Clone)] -#[must_use] -pub enum RentOutcome { - /// Exempted from rent iff: - /// * rent is offset completely by the `rent_deposit_offset`, - /// * or rent has already been paid for this block number, - /// * or account doesn't have a contract, - /// * or account has a tombstone. - Exempted, - /// Evicted iff: - /// * rent exceed rent allowance, - /// * or can't withdraw the rent, - /// * or go below subsistence threshold. - Evicted, - /// The outstanding dues were paid or were able to be paid. - Ok, +/// The amount to charge. +/// +/// This amount respects the contract's rent allowance and the subsistence deposit. +/// Because of that, charging the amount cannot remove the contract. +struct OutstandingAmount { + amount: BalanceOf, } -/// Evict and optionally pay dues (or check account can pay them otherwise) at the current -/// block number (modulo `handicap`, read on). +impl OutstandingAmount { + /// Create the new outstanding amount. + /// + /// The amount should be always withdrawable and it should not kill the account. + fn new(amount: BalanceOf) -> Self { + Self { amount } + } + + /// Returns the amount this instance wraps. + fn peek(&self) -> BalanceOf { + self.amount + } + + /// Withdraws the outstanding amount from the given account. + fn withdraw(self, account: &T::AccountId) { + if let Ok(imbalance) = T::Currency::withdraw( + account, + self.amount, + WithdrawReason::Fee.into(), + ExistenceRequirement::KeepAlive, + ) { + // This should never fail. However, let's err on the safe side. + T::RentPayment::on_unbalanced(imbalance); + } + } +} + +enum Verdict { + /// The contract is exempted from paying rent. + /// + /// For example, it already paid its rent in the current block, or it has enough deposit for not + /// paying rent at all. + Exempt, + /// Funds dropped below the subsistence deposit. + /// + /// Remove the contract along with it's storage. + Kill, + /// The contract cannot afford payment within its rent budget so it gets evicted. However, + /// because its balance is greater than the subsistence threshold it leaves a tombstone. + Evict { + amount: Option>, + }, + /// Everything is OK, we just only take some charge. + Charge { amount: OutstandingAmount }, +} + +/// Returns a fee charged per block from the contract. /// -/// `pay_rent` gives an ability to pay or skip paying rent. -/// `handicap` gives a way to check or pay the rent up to a moment in the past instead -/// of current block. +/// This function accounts for the storage rent deposit. I.e. if the contract possesses enough funds +/// then the fee can drop to zero. +fn compute_fee_per_block( + balance: &BalanceOf, + contract: &AliveContractInfo, +) -> BalanceOf { + let free_storage = balance + .checked_div(&T::RentDepositOffset::get()) + .unwrap_or_else(Zero::zero); + + let effective_storage_size = + >::from(contract.storage_size).saturating_sub(free_storage); + + effective_storage_size + .checked_mul(&T::RentByteFee::get()) + .unwrap_or(>::max_value()) +} + +/// Subsistence threshold is the extension of the minimum balance (aka existential deposit) by the +/// tombstone deposit, required for leaving a tombstone. /// -/// NOTE: This function acts eagerly, all modification are committed into the storage. -fn try_evict_or_and_pay_rent( - account: &T::AccountId, - handicap: T::BlockNumber, - pay_rent: bool, -) -> (RentOutcome, Option>) { - let contract_info = >::get(account); - let contract = match contract_info { - None | Some(ContractInfo::Tombstone(_)) => return (RentOutcome::Exempted, contract_info), - Some(ContractInfo::Alive(contract)) => contract, - }; +/// Rent mechanism cannot make the balance lower than subsistence threshold. +fn subsistence_threshold() -> BalanceOf { + T::Currency::minimum_balance() + T::TombstoneDeposit::get() +} - let current_block_number = >::block_number(); +/// Returns amount of funds available to consume by rent mechanism. +/// +/// Rent mechanism cannot consume more than `rent_allowance` set by the contract and it cannot make +/// the balance lower than [`subsistence_threshold`]. +/// +/// In case the balance is below the subsistence threshold, this function returns `None`. +fn rent_budget( + balance: &BalanceOf, + contract: &AliveContractInfo, +) -> Option> { + let subsistence_threshold = subsistence_threshold::(); + if *balance < subsistence_threshold { + return None; + } + let rent_allowed_to_charge = *balance - subsistence_threshold; + Some(>::min( + contract.rent_allowance, + rent_allowed_to_charge, + )) +} + +/// Consider the case for rent payment of the given account and returns a `Verdict`. +/// +/// Use `handicap` in case you want to change the reference block number. (To get more details see +/// `snitch_contract_should_be_evicted` ). +fn consider_case( + account: &T::AccountId, + current_block_number: T::BlockNumber, + handicap: T::BlockNumber, + contract: &AliveContractInfo, +) -> Verdict { // How much block has passed since the last deduction for the contract. let blocks_passed = { // Calculate an effective block number, i.e. after adjusting for handicap. let effective_block_number = current_block_number.saturating_sub(handicap); - let n = effective_block_number.saturating_sub(contract.deduct_block); - if n.is_zero() { - // Rent has already been paid - return (RentOutcome::Exempted, Some(ContractInfo::Alive(contract))); - } - n + effective_block_number.saturating_sub(contract.deduct_block) }; + if blocks_passed.is_zero() { + // Rent has already been paid + return Verdict::Exempt; + } let balance = T::Currency::free_balance(account); // An amount of funds to charge per block for storage taken up by the contract. - let fee_per_block = { - let free_storage = balance - .checked_div(&T::RentDepositOffset::get()) - .unwrap_or_else(Zero::zero); - - let effective_storage_size = - >::from(contract.storage_size).saturating_sub(free_storage); - - effective_storage_size - .checked_mul(&T::RentByteFee::get()) - .unwrap_or(>::max_value()) - }; - + let fee_per_block = compute_fee_per_block::(&balance, contract); if fee_per_block.is_zero() { // The rent deposit offset reduced the fee to 0. This means that the contract // gets the rent for free. - return (RentOutcome::Exempted, Some(ContractInfo::Alive(contract))); + return Verdict::Exempt; } - // The minimal amount of funds required for a contract not to be evicted. - let subsistence_threshold = T::Currency::minimum_balance() + T::TombstoneDeposit::get(); - - if balance < subsistence_threshold { - // The contract cannot afford to leave a tombstone, so remove the contract info altogether. - >::remove(account); - child::kill_storage(&contract.trie_id, contract.child_trie_unique_id()); - >::deposit_event(RawEvent::Evicted(account.clone(), false)); - return (RentOutcome::Evicted, None); - } + let rent_budget = match rent_budget::(&balance, contract) { + Some(rent_budget) => rent_budget, + None => { + // The contract's balance is already below subsistence threshold. That indicates that + // the contract cannot afford to leave a tombstone. + // + // So cleanly wipe the contract. + return Verdict::Kill; + } + }; let dues = fee_per_block .checked_mul(&blocks_passed.saturated_into::().into()) .unwrap_or(>::max_value()); - let rent_budget = contract.rent_allowance.min(balance - subsistence_threshold); let insufficient_rent = rent_budget < dues; // If the rent payment cannot be withdrawn due to locks on the account balance, then evict the @@ -127,75 +192,202 @@ fn try_evict_or_and_pay_rent( ) .is_ok(); - if can_withdraw_rent && (insufficient_rent || pay_rent) { - // Collect dues. - let imbalance = T::Currency::withdraw( - account, - dues_limited, - WithdrawReason::Fee.into(), - ExistenceRequirement::KeepAlive, - ) - .expect( - "Withdraw has been checked above; - dues_limited < rent_budget < balance - subsistence < balance - existential_deposit; - qed", - ); - - T::RentPayment::on_unbalanced(imbalance); - } - if insufficient_rent || !can_withdraw_rent { // The contract cannot afford the rent payment and has a balance above the subsistence // threshold, so it leaves a tombstone. + let amount = if can_withdraw_rent { + Some(OutstandingAmount::new(dues_limited)) + } else { + None + }; + return Verdict::Evict { amount }; + } - // Note: this operation is heavy. - let child_storage_root = child::child_root( - &contract.trie_id, - ); - - let tombstone = >::new( - &child_storage_root[..], - contract.code_hash, - ); - let tombstone_info = ContractInfo::Tombstone(tombstone); - >::insert(account, &tombstone_info); + return Verdict::Charge { + // We choose to use `dues_limited` here instead of `dues` just to err on the safer side. + amount: OutstandingAmount::new(dues_limited), + }; +} - child::kill_storage(&contract.trie_id, contract.child_trie_unique_id()); +/// Enacts the given verdict and returns the updated `ContractInfo`. +/// +/// `alive_contract_info` should be from the same address as `account`. +fn enact_verdict( + account: &T::AccountId, + alive_contract_info: AliveContractInfo, + current_block_number: T::BlockNumber, + verdict: Verdict, +) -> Option> { + match verdict { + Verdict::Exempt => return Some(ContractInfo::Alive(alive_contract_info)), + Verdict::Kill => { + >::remove(account); + child::kill_storage( + &alive_contract_info.trie_id, + alive_contract_info.child_trie_unique_id(), + ); + >::deposit_event(RawEvent::Evicted(account.clone(), false)); + None + } + Verdict::Evict { amount } => { + if let Some(amount) = amount { + amount.withdraw(account); + } - >::deposit_event(RawEvent::Evicted(account.clone(), true)); + // Note: this operation is heavy. + let child_storage_root = child::child_root(&alive_contract_info.trie_id); - return (RentOutcome::Evicted, Some(tombstone_info)); - } + let tombstone = >::new( + &child_storage_root[..], + alive_contract_info.code_hash, + ); + let tombstone_info = ContractInfo::Tombstone(tombstone); + >::insert(account, &tombstone_info); - if pay_rent { - let contract_info = ContractInfo::Alive(AliveContractInfo:: { - rent_allowance: contract.rent_allowance - dues, // rent_allowance is not exceeded - deduct_block: current_block_number, - ..contract - }); + child::kill_storage( + &alive_contract_info.trie_id, + alive_contract_info.child_trie_unique_id(), + ); - >::insert(account, &contract_info); + >::deposit_event(RawEvent::Evicted(account.clone(), true)); + Some(tombstone_info) + } + Verdict::Charge { amount } => { + let contract_info = ContractInfo::Alive(AliveContractInfo:: { + rent_allowance: alive_contract_info.rent_allowance - amount.peek(), + deduct_block: current_block_number, + ..alive_contract_info + }); + >::insert(account, &contract_info); - return (RentOutcome::Ok, Some(contract_info)); + amount.withdraw(account); + Some(contract_info) + } } - - (RentOutcome::Ok, Some(ContractInfo::Alive(contract))) } /// Make account paying the rent for the current block number /// -/// NOTE: This function acts eagerly. -pub fn pay_rent(account: &T::AccountId) -> Option> { - try_evict_or_and_pay_rent::(account, Zero::zero(), true).1 +/// NOTE this function performs eviction eagerly. All changes are read and written directly to +/// storage. +pub fn collect_rent(account: &T::AccountId) -> Option> { + let contract_info = >::get(account); + let alive_contract_info = match contract_info { + None | Some(ContractInfo::Tombstone(_)) => return contract_info, + Some(ContractInfo::Alive(contract)) => contract, + }; + + let current_block_number = >::block_number(); + let verdict = consider_case::( + account, + current_block_number, + Zero::zero(), + &alive_contract_info, + ); + enact_verdict(account, alive_contract_info, current_block_number, verdict) } -/// Evict the account if it should be evicted at the given block number. +/// Process a report that a contract under the given address should be evicted. +/// +/// Enact the eviction right away if the contract should be evicted and return true. +/// Otherwise, **do nothing** and return false. /// -/// `handicap` gives a way to check or pay the rent up to a moment in the past instead +/// The `handicap` parameter gives a way to check the rent to a moment in the past instead /// of current block. E.g. if the contract is going to be evicted at the current block, -/// `handicap=1` can defer the eviction for 1 block. +/// `handicap = 1` can defer the eviction for 1 block. This is useful to handicap certain snitchers +/// relative to others. +/// +/// NOTE this function performs eviction eagerly. All changes are read and written directly to +/// storage. +pub fn snitch_contract_should_be_evicted( + account: &T::AccountId, + handicap: T::BlockNumber, +) -> bool { + let contract_info = >::get(account); + let alive_contract_info = match contract_info { + None | Some(ContractInfo::Tombstone(_)) => return false, + Some(ContractInfo::Alive(contract)) => contract, + }; + let current_block_number = >::block_number(); + let verdict = consider_case::( + account, + current_block_number, + handicap, + &alive_contract_info, + ); + + // Enact the verdict only if the contract gets removed. + match verdict { + Verdict::Kill | Verdict::Evict { .. } => { + enact_verdict(account, alive_contract_info, current_block_number, verdict); + true + } + _ => false, + } +} + +/// Returns the projected time a given contract will be able to sustain paying its rent. The +/// returned projection is relevent for the current block, i.e. it is as if the contract was +/// accessed at the beginning of the current block. Returns `None` in case if the contract was +/// evicted before or as a result of the rent collection. /// -/// NOTE: This function acts eagerly. -pub fn try_evict(account: &T::AccountId, handicap: T::BlockNumber) -> RentOutcome { - try_evict_or_and_pay_rent::(account, handicap, false).0 +/// The returned value is only an estimation. It doesn't take into account any top ups, changing the +/// rent allowance, or any problems coming from withdrawing the dues. +/// +/// NOTE that this is not a side-effect free function! It will actually collect rent and then +/// compute the projection. This function is only used for implementation of an RPC method through +/// `RuntimeApi` meaning that the changes will be discarded anyway. +pub fn compute_rent_projection( + account: &T::AccountId, +) -> RentProjectionResult { + let contract_info = >::get(account); + let alive_contract_info = match contract_info { + None | Some(ContractInfo::Tombstone(_)) => return Err(ContractAccessError::IsTombstone), + Some(ContractInfo::Alive(contract)) => contract, + }; + let current_block_number = >::block_number(); + let verdict = consider_case::( + account, + current_block_number, + Zero::zero(), + &alive_contract_info, + ); + let new_contract_info = + enact_verdict(account, alive_contract_info, current_block_number, verdict); + + // Check what happened after enaction of the verdict. + let alive_contract_info = match new_contract_info { + None | Some(ContractInfo::Tombstone(_)) => return Err(ContractAccessError::IsTombstone), + Some(ContractInfo::Alive(contract)) => contract, + }; + + // Compute how much would the fee per block be with the *updated* balance. + let balance = T::Currency::free_balance(account); + let fee_per_block = compute_fee_per_block::(&balance, &alive_contract_info); + if fee_per_block.is_zero() { + return Ok(RentProjection::NoEviction); + } + + // Then compute how much the contract will sustain under these circumstances. + let rent_budget = rent_budget::(&balance, &alive_contract_info).expect( + "the contract exists and in the alive state; + the updated balance must be greater than subsistence deposit; + this function doesn't return `None`; + qed + ", + ); + let blocks_left = match rent_budget.checked_div(&fee_per_block) { + Some(blocks_left) => blocks_left, + None => { + // `fee_per_block` is not zero here, so `checked_div` can return `None` if + // there is an overflow. This cannot happen with integers though. Return + // `NoEviction` here just in case. + return Ok(RentProjection::NoEviction); + } + }; + + let blocks_left = blocks_left.saturated_into::().into(); + Ok(RentProjection::EvictionAt( + current_block_number + blocks_left, + )) } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 9a2ef36bb86f0665bcafd20e26e859f773bc802a..8cb854ac97d939ac7825c742b284180e499d96b9 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -41,7 +41,7 @@ use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}}; use sp_core::storage::well_known_keys; use frame_system::{self as system, EventRecord, Phase}; -mod contract { +mod contracts { // Re-export contents of the root. This basically // needs to give a name for the current crate. // This hack is required for `impl_outer_event!`. @@ -53,7 +53,9 @@ use pallet_balances as balances; impl_outer_event! { pub enum MetaEvent for Test { - balances, contract, + system, + balances, + contracts, } } impl_outer_origin! { @@ -62,7 +64,7 @@ impl_outer_origin! { impl_outer_dispatch! { pub enum Call for Test where origin: Origin { balances::Balances, - contract::Contract, + contracts::Contracts, } } @@ -83,11 +85,6 @@ impl Get for TransferFee { fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } } -pub struct CreationFee; -impl Get for CreationFee { - fn get() -> u64 { INSTANTIATION_FEE.with(|v| *v.borrow()) } -} - pub struct BlockGasLimit; impl Get for BlockGasLimit { fn get() -> u64 { BLOCK_GAS_LIMIT.with(|v| *v.borrow()) } @@ -118,18 +115,16 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Contracts); } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = Contract; - type OnReapAccount = System; - type OnNewAccount = (); type Event = MetaEvent; type DustRemoval = (); - type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const MinimumPeriod: u64 = 1; @@ -171,8 +166,6 @@ impl Trait for Test { type RentByteFee = RentByteFee; type RentDepositOffset = RentDepositOffset; type SurchargeReward = SurchargeReward; - type TransferFee = TransferFee; - type CreationFee = CreationFee; type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type ContractFee = ContractFee; @@ -185,7 +178,7 @@ impl Trait for Test { type Balances = pallet_balances::Module; type Timestamp = pallet_timestamp::Module; -type Contract = Module; +type Contracts = Module; type System = frame_system::Module; type Randomness = pallet_randomness_collective_flip::Module; @@ -238,7 +231,7 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { - existential_deposit: 0, + existential_deposit: 1, gas_price: 2, block_gas_limit: 100_000_000, transfer_fee: 0, @@ -278,7 +271,6 @@ impl ExtBuilder { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); GenesisConfig:: { current_schedule: Schedule { @@ -308,10 +300,10 @@ fn refunds_unused_gas() { ExtBuilder::default().gas_price(2).build().execute_with(|| { Balances::deposit_creating(&ALICE, 100_000_000); - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new())); // 2 * 135 - gas price multiplied by the call base fee. - assert_eq!(Balances::free_balance(&ALICE), 100_000_000 - (2 * 135)); + assert_eq!(Balances::free_balance(ALICE), 100_000_000 - (2 * 135)); }); } @@ -417,10 +409,10 @@ fn instantiate_and_call_and_deposit_event() { ExtBuilder::default().existential_deposit(100).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Check at the end to get hash on error easily - let creation = Contract::instantiate( + let creation = Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, @@ -431,40 +423,50 @@ fn instantiate_and_call_and_deposit_event() { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(BOB, 100) + pallet_balances::RawEvent::Endowed(BOB, 100) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])), + event: MetaEvent::contracts(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)), topics: vec![], } ]); assert_ok!(creation); - assert!(ContractInfoOf::::exists(BOB)); + assert!(ContractInfoOf::::contains_key(BOB)); }); } @@ -497,24 +499,29 @@ fn dispatch_call() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, @@ -522,7 +529,7 @@ fn dispatch_call() { vec![], )); - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, // newly created account 0, @@ -533,44 +540,59 @@ fn dispatch_call() { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(BOB, 100) + pallet_balances::RawEvent::Endowed(BOB, 100) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)), topics: vec![], }, // Dispatching the call. + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)), + topics: vec![], + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(CHARLIE, 50) + pallet_balances::RawEvent::Endowed(CHARLIE, 50) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0) + pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50) ), topics: vec![], }, @@ -578,7 +600,7 @@ fn dispatch_call() { // Event emited as a result of dispatch. EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)), + event: MetaEvent::contracts(RawEvent::Dispatched(BOB, true)), topics: vec![], } ]); @@ -615,24 +637,29 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, @@ -643,7 +670,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { // Call the newly instantiated contract. The contract is expected to dispatch a call // and then trap. assert_err!( - Contract::call( + Contracts::call( Origin::signed(ALICE), BOB, // newly created account 0, @@ -655,29 +682,39 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(BOB, 100) + pallet_balances::RawEvent::Endowed(BOB, 100) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)), topics: vec![], }, // ABSENCE of events which would be caused by dispatched Balances::transfer call @@ -814,19 +851,24 @@ fn test_set_rent_code_and_hash() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // If you ever need to update the wasm source this test will fail // and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); @@ -841,8 +883,8 @@ fn storage_size() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, code_hash.into(), @@ -851,11 +893,11 @@ fn storage_size() { let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte())); let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4 + 4); - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte())); let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); }); @@ -878,8 +920,8 @@ fn deduct_blocks() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, code_hash.into(), @@ -894,7 +936,7 @@ fn deduct_blocks() { initialize_block(5); // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Check result let rent = (8 + 4 - 3) // storage size = size_offset + deploy_set_storage - deposit_offset @@ -909,7 +951,7 @@ fn deduct_blocks() { initialize_block(12); // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Check result let rent_2 = (8 + 4 - 2) // storage size = size_offset + deploy_set_storage - deposit_offset @@ -921,7 +963,7 @@ fn deduct_blocks() { assert_eq!(Balances::free_balance(BOB), 30_000 - rent - rent_2); // Second call on same block should have no effect on rent - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2); @@ -934,34 +976,34 @@ fn deduct_blocks() { fn call_contract_removals() { removals(|| { // Call on already-removed account might fail, and this is fine. - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()); + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()); true }); } #[test] fn inherent_claim_surcharge_contract_removals() { - removals(|| Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok()); + removals(|| Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok()); } #[test] fn signed_claim_surcharge_contract_removals() { - removals(|| Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok()); + removals(|| Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok()); } #[test] fn claim_surcharge_malus() { // Test surcharge malus for inherent - claim_surcharge(4, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(3, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(2, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(1, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false); + claim_surcharge(4, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(3, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(2, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(1, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false); // Test surcharge malus for signed - claim_surcharge(4, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true); - claim_surcharge(3, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); - claim_surcharge(2, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); - claim_surcharge(1, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); + claim_surcharge(4, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true); + claim_surcharge(3, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); + claim_surcharge(2, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); + claim_surcharge(1, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); } /// Claim surcharge with the given trigger_call at the given blocks. @@ -972,8 +1014,8 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), @@ -1005,8 +1047,8 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), @@ -1018,7 +1060,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert_eq!(ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 1_000); - assert_eq!(Balances::free_balance(&BOB), 100); + assert_eq!(Balances::free_balance(BOB), 100); // Advance blocks initialize_block(10); @@ -1026,7 +1068,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent through call assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); + assert_eq!(Balances::free_balance(BOB), subsistence_threshold); // Advance blocks initialize_block(20); @@ -1034,15 +1076,15 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); + assert_eq!(Balances::free_balance(BOB), subsistence_threshold); }); // Allowance exceeded ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 1_000, 100_000, code_hash.into(), @@ -1052,7 +1094,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert_eq!(ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 100); - assert_eq!(Balances::free_balance(&BOB), 1_000); + assert_eq!(Balances::free_balance(BOB), 1_000); // Advance blocks initialize_block(10); @@ -1061,7 +1103,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); // Balance should be initial balance - initial rent_allowance - assert_eq!(Balances::free_balance(&BOB), 900); + assert_eq!(Balances::free_balance(BOB), 900); // Advance blocks initialize_block(20); @@ -1069,15 +1111,15 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - assert_eq!(Balances::free_balance(&BOB), 900); + assert_eq!(Balances::free_balance(BOB), 900); }); // Balance reached and inferior to subsistence threshold ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 50+Balances::minimum_balance(), 100_000, code_hash.into(), @@ -1087,12 +1129,12 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert_eq!(ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 1_000); - assert_eq!(Balances::free_balance(&BOB), 50 + Balances::minimum_balance()); + assert_eq!(Balances::free_balance(BOB), 50 + Balances::minimum_balance()); // Transfer funds - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer())); assert_eq!(ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 1_000); - assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); + assert_eq!(Balances::free_balance(BOB), Balances::minimum_balance()); // Advance blocks initialize_block(10); @@ -1100,7 +1142,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent through call assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).is_none()); - assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); + assert_eq!(Balances::free_balance(BOB), Balances::minimum_balance()); // Advance blocks initialize_block(20); @@ -1108,7 +1150,7 @@ fn removals(trigger_call: impl Fn() -> bool) { // Trigger rent must have no effect assert!(trigger_call()); assert!(ContractInfoOf::::get(BOB).is_none()); - assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); + assert_eq!(Balances::free_balance(BOB), Balances::minimum_balance()); }); } @@ -1120,8 +1162,8 @@ fn call_removed_contract() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), @@ -1129,28 +1171,28 @@ fn call_removed_contract() { )); // Calling contract should succeed. - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Advance blocks initialize_block(10); // Calling contract should remove contract and fail. assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), "contract has been evicted" ); // Calling a contract that is about to evict shall emit an event. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Evicted(BOB, true)), + event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)), topics: vec![], }, ]); // Subsequent contract calls should also fail. assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), "contract has been evicted" ); }) @@ -1213,8 +1255,8 @@ fn default_rent_allowance_on_instantiate() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, @@ -1230,7 +1272,7 @@ fn default_rent_allowance_on_instantiate() { initialize_block(5); // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Check contract is still alive let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive(); @@ -1326,32 +1368,37 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, restoration_wasm)); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, restoration_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm)); // If you ever need to update the wasm source this test will fail // and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(set_rent_code_hash.into())), + event: MetaEvent::contracts(RawEvent::CodeStored(restoration_code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(set_rent_code_hash.into())), topics: vec![], }, ]); // Create an account with address `BOB` with code `CODE_SET_RENT`. // The input parameter sets the rent allowance to 0. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, @@ -1365,7 +1412,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(bob_contract.rent_allowance, 0); if test_different_storage { - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte()) @@ -1381,14 +1428,14 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0 // we expect that it will get removed leaving tombstone. assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), "contract has been evicted" ); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract( + event: MetaEvent::contracts( RawEvent::Evicted(BOB.clone(), true) ), topics: vec![], @@ -1400,7 +1447,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: /// Note that we can't use `ALICE` for creating `DJANGO` so we create yet another /// account `CHARLIE` and create `DJANGO` with it. Balances::deposit_creating(&CHARLIE, 1_000_000); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(CHARLIE), 30_000, 100_000, @@ -1419,7 +1466,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Perform a call to `DJANGO`. This should either perform restoration successfully or // fail depending on the test parameters. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), DJANGO, 0, @@ -1441,7 +1488,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract( + event: MetaEvent::contracts( RawEvent::Restored(DJANGO, BOB, bob_code_hash, 50, false) ), topics: vec![], @@ -1452,32 +1499,42 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Evicted(BOB, true)), + event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(CHARLIE, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(CHARLIE, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(DJANGO)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(DJANGO, 30_000)), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(DJANGO, 30_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)), + event: MetaEvent::contracts(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(CHARLIE, DJANGO)), + event: MetaEvent::contracts(RawEvent::Instantiated(CHARLIE, DJANGO)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Restored( + event: MetaEvent::contracts(RawEvent::Restored( DJANGO, BOB, bob_code_hash, @@ -1504,12 +1561,12 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(balances::RawEvent::ReapedAccount(DJANGO, 0)), + event: MetaEvent::system(system::RawEvent::ReapedAccount(DJANGO)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract( + event: MetaEvent::contracts( RawEvent::Restored(DJANGO, BOB, bob_contract.code_hash, 50, true) ), topics: vec![], @@ -1590,8 +1647,8 @@ fn storage_max_value_limit() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, @@ -1604,7 +1661,7 @@ fn storage_max_value_limit() { assert_eq!(bob_contract.rent_allowance, >::max_value()); // Call contract with allowed storage value. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -1614,7 +1671,7 @@ fn storage_max_value_limit() { // Call contract with too large a storage value. assert_err!( - Contract::call( + Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -1954,10 +2011,10 @@ fn deploy_and_call_other_contract() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -1967,7 +2024,7 @@ fn deploy_and_call_other_contract() { // Call BOB contract, which attempts to instantiate and call the callee contract and // makes various assertions on the results from those calls. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2081,10 +2138,10 @@ fn self_destruct_by_draining_balance() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Instantiate the BOB contract. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -2099,7 +2156,7 @@ fn self_destruct_by_draining_balance() { ); // Call BOB with no input data, forcing it to self-destruct. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2117,10 +2174,10 @@ fn cannot_self_destruct_while_live() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Instantiate the BOB contract. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -2137,7 +2194,7 @@ fn cannot_self_destruct_while_live() { // Call BOB with input data, forcing it make a recursive call to itself to // self-destruct, resulting in a trap. assert_err!( - Contract::call( + Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2317,12 +2374,12 @@ fn destroy_contract_and_transfer_funds() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 200_000, 100_000, @@ -2337,7 +2394,7 @@ fn destroy_contract_and_transfer_funds() { ); // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2412,12 +2469,12 @@ fn cannot_self_destruct_in_constructor() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCTING_CONSTRUCTOR).unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Fail to instantiate the BOB contract since its final balance is below existential // deposit. assert_err!( - Contract::instantiate( + Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -2533,15 +2590,15 @@ fn get_runtime_storage() { 0x14144020u32.to_le_bytes().to_vec().as_ref() ); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), vec![], )); - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, diff --git a/frame/contracts/src/wasm/env_def/macros.rs b/frame/contracts/src/wasm/env_def/macros.rs index 89a147b2c6f478b3c80b4983e8c67e7729f5a3cc..335d35f1e7827271abc828727a87db5c2d6562df 100644 --- a/frame/contracts/src/wasm/env_def/macros.rs +++ b/frame/contracts/src/wasm/env_def/macros.rs @@ -126,7 +126,7 @@ macro_rules! define_func { ( < E: $ext_ty:tt > $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => { fn $name< E: $ext_ty >( $ctx: &mut $crate::wasm::Runtime, - args: &[sp_sandbox::TypedValue], + args: &[sp_sandbox::Value], ) -> Result { #[allow(unused)] let mut args = args.iter(); @@ -196,7 +196,7 @@ mod tests { use parity_wasm::elements::FunctionType; use parity_wasm::elements::ValueType; use sp_runtime::traits::Zero; - use sp_sandbox::{self, ReturnValue, TypedValue}; + use sp_sandbox::{ReturnValue, Value}; use crate::wasm::tests::MockExt; use crate::wasm::Runtime; use crate::exec::Ext; @@ -206,7 +206,7 @@ mod tests { fn macro_unmarshall_then_body_then_marshall_value_or_trap() { fn test_value( _ctx: &mut u32, - args: &[sp_sandbox::TypedValue], + args: &[sp_sandbox::Value], ) -> Result { let mut args = args.iter(); unmarshall_then_body_then_marshall!( @@ -224,17 +224,17 @@ mod tests { let ctx = &mut 0; assert_eq!( - test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(3)]).unwrap(), - ReturnValue::Value(TypedValue::I32(5)), + test_value(ctx, &[Value::I32(15), Value::I32(3)]).unwrap(), + ReturnValue::Value(Value::I32(5)), ); - assert!(test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(0)]).is_err()); + assert!(test_value(ctx, &[Value::I32(15), Value::I32(0)]).is_err()); } #[test] fn macro_unmarshall_then_body_then_marshall_unit() { fn test_unit( ctx: &mut u32, - args: &[sp_sandbox::TypedValue], + args: &[sp_sandbox::Value], ) -> Result { let mut args = args.iter(); unmarshall_then_body_then_marshall!( @@ -248,7 +248,7 @@ mod tests { } let ctx = &mut 0; - let result = test_unit(ctx, &[TypedValue::I32(2), TypedValue::I32(3)]).unwrap(); + let result = test_unit(ctx, &[Value::I32(2), Value::I32(3)]).unwrap(); assert_eq!(result, ReturnValue::Unit); assert_eq!(*ctx, 5); } @@ -263,7 +263,7 @@ mod tests { Err(sp_sandbox::HostError) } }); - let _f: fn(&mut Runtime, &[sp_sandbox::TypedValue]) + let _f: fn(&mut Runtime, &[sp_sandbox::Value]) -> Result = ext_gas::; } @@ -282,7 +282,7 @@ mod tests { #[test] fn macro_unmarshall_then_body() { - let args = vec![TypedValue::I32(5), TypedValue::I32(3)]; + let args = vec![Value::I32(5), Value::I32(3)]; let mut args = args.iter(); let ctx: &mut u32 = &mut 0; diff --git a/frame/contracts/src/wasm/env_def/mod.rs b/frame/contracts/src/wasm/env_def/mod.rs index 004308da42211ce12e6d2dd4a0642358183186d6..7b67f74ec95c8db4495931c9ab1a0318db7e8aa4 100644 --- a/frame/contracts/src/wasm/env_def/mod.rs +++ b/frame/contracts/src/wasm/env_def/mod.rs @@ -17,7 +17,7 @@ use super::Runtime; use crate::exec::Ext; -use sp_sandbox::{self, TypedValue}; +use sp_sandbox::Value; use parity_wasm::elements::{FunctionType, ValueType}; #[macro_use] @@ -26,28 +26,28 @@ pub(crate) mod macros; pub trait ConvertibleToWasm: Sized { const VALUE_TYPE: ValueType; type NativeType; - fn to_typed_value(self) -> TypedValue; - fn from_typed_value(_: TypedValue) -> Option; + fn to_typed_value(self) -> Value; + fn from_typed_value(_: Value) -> Option; } impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; - fn to_typed_value(self) -> TypedValue { - TypedValue::I32(self) + fn to_typed_value(self) -> Value { + Value::I32(self) } - fn from_typed_value(v: TypedValue) -> Option { + fn from_typed_value(v: Value) -> Option { v.as_i32() } } impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; - fn to_typed_value(self) -> TypedValue { - TypedValue::I32(self as i32) + fn to_typed_value(self) -> Value { + Value::I32(self as i32) } - fn from_typed_value(v: TypedValue) -> Option { + fn from_typed_value(v: Value) -> Option { match v { - TypedValue::I32(v) => Some(v as u32), + Value::I32(v) => Some(v as u32), _ => None, } } @@ -55,12 +55,12 @@ impl ConvertibleToWasm for u32 { impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: ValueType = ValueType::I64; - fn to_typed_value(self) -> TypedValue { - TypedValue::I64(self as i64) + fn to_typed_value(self) -> Value { + Value::I64(self as i64) } - fn from_typed_value(v: TypedValue) -> Option { + fn from_typed_value(v: Value) -> Option { match v { - TypedValue::I64(v) => Some(v as u64), + Value::I64(v) => Some(v as u64), _ => None, } } @@ -69,7 +69,7 @@ impl ConvertibleToWasm for u64 { pub(crate) type HostFunc = fn( &mut Runtime, - &[sp_sandbox::TypedValue] + &[sp_sandbox::Value] ) -> Result; pub(crate) trait FunctionImplProvider { diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 60402cf3a09cf0a383c1fd8e71b322bcd94f17bc..1ea2067a8d22e657066631d0fe50de8368149b84 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -299,6 +299,10 @@ mod tests { 666 } + fn tombstone_deposit(&self) -> u64 { + 16 + } + fn random(&self, subject: &[u8]) -> H256 { H256::from_slice(subject) } @@ -397,6 +401,9 @@ mod tests { fn minimum_balance(&self) -> u64 { (**self).minimum_balance() } + fn tombstone_deposit(&self) -> u64 { + (**self).tombstone_deposit() + } fn random(&self, subject: &[u8]) -> H256 { (**self).random(subject) } @@ -1271,6 +1278,65 @@ mod tests { ).unwrap(); } + const CODE_TOMBSTONE_DEPOSIT: &str = r#" +(module + (import "env" "ext_tombstone_deposit" (func $ext_tombstone_deposit)) + (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) + (import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + (call $ext_tombstone_deposit) + + ;; assert $ext_scratch_size == 8 + (call $assert + (i32.eq + (call $ext_scratch_size) + (i32.const 8) + ) + ) + + ;; copy contents of the scratch buffer into the contract's memory. + (call $ext_scratch_read + (i32.const 8) ;; Pointer in memory to the place where to copy. + (i32.const 0) ;; Offset from the start of the scratch buffer. + (i32.const 8) ;; Count of bytes to copy. + ) + + ;; assert that contents of the buffer is equal to the i64 value of 16. + (call $assert + (i64.eq + (i64.load + (i32.const 8) + ) + (i64.const 16) + ) + ) + ) + (func (export "deploy")) +) +"#; + + #[test] + fn tombstone_deposit() { + let mut gas_meter = GasMeter::with_limit(50_000, 1); + let _ = execute( + CODE_TOMBSTONE_DEPOSIT, + vec![], + MockExt::default(), + &mut gas_meter, + ).unwrap(); + } + const CODE_RANDOM: &str = r#" (module (import "env" "ext_random" (func $ext_random (param i32 i32))) diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 39e1fca79d6724f7504b572ad8b831fe68d066dd..ba934f353ec250340fd2bd1c14f69e6ee251379f 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -76,7 +76,7 @@ impl<'a> ContractModule<'a> { /// Ensures that tables declared in the module are not too big. fn ensure_table_size_limit(&self, limit: u32) -> Result<(), &'static str> { - if let Some(table_section) = self.module.table_section() { + if let Some(table_section) = self.module.table_section() { // In Wasm MVP spec, there may be at most one table declared. Double check this // explicitly just in case the Wasm version changes. if table_section.entries().len() > 1 { diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 75751b6d359a313ba820a6ceb31aa470af4e7cc8..66ae8a4996ca49ff39b3f23b586658493659309e 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -23,9 +23,7 @@ use crate::exec::{ use crate::gas::{Gas, GasMeter, Token, GasMeterResult, approx_gas_for_balance}; use sp_sandbox; use frame_system; -use sp_std::prelude::*; -use sp_std::convert::TryInto; -use sp_std::mem; +use sp_std::{prelude::*, mem, convert::TryInto}; use codec::{Decode, Encode}; use sp_runtime::traits::{Bounded, SaturatedConversion}; @@ -89,7 +87,7 @@ pub(crate) fn to_execution_result( buffer.clear(); Ok(ExecReturnValue { status: STATUS_SUCCESS, data: buffer }) } - Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(exit_code))) => { + Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::Value::I32(exit_code))) => { let status = (exit_code & 0xFF).try_into() .expect("exit_code is masked into the range of a u8; qed"); Ok(ExecReturnValue { status, data: runtime.scratch_buf }) @@ -623,6 +621,23 @@ define_env!(Env, , Ok(()) }, + // Stores the tombstone deposit into the scratch buffer. + // + // The data is encoded as T::Balance. The current contents of the scratch + // buffer are overwritten. + // + // # Note + // + // The tombstone deposit is on top of the existential deposit. So in order for + // a contract to leave a tombstone the balance of the contract must not go + // below the sum of existential deposit and the tombstone deposit. The sum + // is commonly referred as subsistence threshold in code. + ext_tombstone_deposit(ctx) => { + ctx.scratch_buf.clear(); + ctx.ext.tombstone_deposit().encode_to(&mut ctx.scratch_buf); + Ok(()) + }, + // Decodes the given buffer as a `T::Call` and adds it to the list // of to-be-dispatched calls. // diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index 02145e8589babe3464c7d213eda070ad94174f14..2428d2c1544dcf7309294f9238b2031cb8cd3210 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-democracy" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } @@ -16,6 +17,8 @@ frame-system = { version = "2.0.0", default-features = false, path = "../system" [dev-dependencies] sp-core = { version = "2.0.0", path = "../../primitives/core" } pallet-balances = { version = "2.0.0", path = "../balances" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } +hex-literal = "0.2.1" [features] default = ["std"] diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index d7790be8f7065c2397f8c55071d8d25b9aad633f..33ddf69427978eb96e5599d8bebbe39a098ea028 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -30,7 +30,7 @@ use frame_support::{ weights::SimpleDispatchInfo, traits::{ Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get, - OnFreeBalanceZero, OnUnbalanced + OnReapAccount, OnUnbalanced, BalanceStatus } }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -268,9 +268,12 @@ decl_storage! { pub PublicProps get(fn public_props): Vec<(PropIndex, T::Hash, T::AccountId)>; /// Map of hashes to the proposal preimage, along with who registered it and their deposit. /// The block number is the block at which it was deposited. - pub Preimages: map T::Hash => Option<(Vec, T::AccountId, BalanceOf, T::BlockNumber)>; + pub Preimages: + map hasher(blake2_256) T::Hash + => Option<(Vec, T::AccountId, BalanceOf, T::BlockNumber)>; /// Those who have locked a deposit. - pub DepositOf get(fn deposit_of): map PropIndex => Option<(BalanceOf, Vec)>; + pub DepositOf get(fn deposit_of): + map hasher(blake2_256) PropIndex => Option<(BalanceOf, Vec)>; /// The next free referendum index, aka the number of referenda started so far. pub ReferendumCount get(fn referendum_count) build(|_| 0 as ReferendumIndex): ReferendumIndex; @@ -279,25 +282,32 @@ decl_storage! { pub LowestUnbaked get(fn lowest_unbaked) build(|_| 0 as ReferendumIndex): ReferendumIndex; /// Information concerning any given referendum. pub ReferendumInfoOf get(fn referendum_info): - map ReferendumIndex => Option>; + map hasher(blake2_256) ReferendumIndex + => Option>; /// Queue of successful referenda to be dispatched. Stored ordered by block number. pub DispatchQueue get(fn dispatch_queue): Vec<(T::BlockNumber, T::Hash, ReferendumIndex)>; /// Get the voters for the current proposal. - pub VotersFor get(fn voters_for): map ReferendumIndex => Vec; + pub VotersFor get(fn voters_for): + map hasher(blake2_256) ReferendumIndex => Vec; /// Get the vote in a given referendum of a particular voter. The result is meaningful only /// if `voters_for` includes the voter when called with the referendum (you'll get the /// default `Vote` value otherwise). If you don't want to check `voters_for`, then you can - /// also check for simple existence with `VoteOf::exists` first. - pub VoteOf get(fn vote_of): map (ReferendumIndex, T::AccountId) => Vote; + /// also check for simple existence with `VoteOf::contains_key` first. + pub VoteOf get(fn vote_of): map hasher(blake2_256) (ReferendumIndex, T::AccountId) => Vote; /// Who is able to vote for whom. Value is the fund-holding account, key is the /// vote-transaction-sending account. - pub Proxy get(fn proxy): map T::AccountId => Option; + pub Proxy get(fn proxy): map hasher(blake2_256) T::AccountId => Option; /// Get the account (and lock periods) to which another account is delegating vote. - pub Delegations get(fn delegations): linked_map T::AccountId => (T::AccountId, Conviction); + pub Delegations get(fn delegations): + linked_map hasher(blake2_256) T::AccountId => (T::AccountId, Conviction); + + /// Accounts for which there are locks in action which may be removed at some point in the + /// future. The value is the block number at which the lock expires and may be removed. + pub Locks get(locks): map hasher(blake2_256) T::AccountId => Option; /// True if the last referendum tabled was submitted externally. False if it was a public /// proposal. @@ -311,10 +321,11 @@ decl_storage! { /// A record of who vetoed what. Maps proposal hash to a possible existent block number /// (until when it may not be resubmitted) and who vetoed it. - pub Blacklist get(fn blacklist): map T::Hash => Option<(T::BlockNumber, Vec)>; + pub Blacklist get(fn blacklist): + map hasher(blake2_256) T::Hash => Option<(T::BlockNumber, Vec)>; /// Record of all proposals that have been subject to emergency cancellation. - pub Cancellations: map T::Hash => bool; + pub Cancellations: map hasher(blake2_256) T::Hash => bool; } } @@ -357,6 +368,8 @@ decl_event! { PreimageMissing(Hash, ReferendumIndex), /// A registered preimage was removed and the deposit collected by the reaper (last item). PreimageReaped(Hash, AccountId, Balance, AccountId), + /// An account has been unlocked successfully. + Unlocked(AccountId), } } @@ -406,6 +419,10 @@ decl_error! { PreimageInvalid, /// No proposals waiting NoneWaiting, + /// The target account does not have a lock. + NotLocked, + /// The lock on the account to be unlocked has not yet expired. + NotExpired, } } @@ -520,7 +537,7 @@ decl_module! { let info = Self::referendum_info(ref_index).ok_or(Error::::BadIndex)?; let h = info.proposal_hash; - ensure!(!>::exists(h), Error::::AlreadyCanceled); + ensure!(!>::contains_key(h), Error::::AlreadyCanceled); >::insert(h, true); Self::clear_referendum(ref_index); @@ -650,7 +667,7 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(100_000)] fn set_proxy(origin, proxy: T::AccountId) { let who = ensure_signed(origin)?; - ensure!(!>::exists(&proxy), Error::::AlreadyProxy); + ensure!(!>::contains_key(&proxy), Error::::AlreadyProxy); >::insert(proxy, who) } @@ -694,9 +711,9 @@ decl_module! { DEMOCRACY_ID, &who, Bounded::max_value(), - T::BlockNumber::max_value(), WithdrawReason::Transfer.into() ); + Locks::::remove(&who); Self::deposit_event(RawEvent::Delegated(who, to)); } @@ -708,17 +725,17 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(500_000)] fn undelegate(origin) { let who = ensure_signed(origin)?; - ensure!(>::exists(&who), Error::::NotDelegated); + ensure!(>::contains_key(&who), Error::::NotDelegated); let (_, conviction) = >::take(&who); // Indefinite lock is reduced to the maximum voting lock that could be possible. let now = >::block_number(); let locked_until = now + T::EnactmentPeriod::get() * conviction.lock_periods().into(); + Locks::::insert(&who, locked_until); T::Currency::set_lock( DEMOCRACY_ID, &who, Bounded::max_value(), - locked_until, - WithdrawReason::Transfer.into() + WithdrawReason::Transfer.into(), ); Self::deposit_event(RawEvent::Undelegated(who)); } @@ -737,7 +754,7 @@ decl_module! { fn note_preimage(origin, encoded_proposal: Vec) { let who = ensure_signed(origin)?; let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - ensure!(!>::exists(&proposal_hash), Error::::DuplicatePreimage); + ensure!(!>::contains_key(&proposal_hash), Error::::DuplicatePreimage); let deposit = >::from(encoded_proposal.len() as u32) .saturating_mul(T::PreimageByteDeposit::get()); @@ -755,7 +772,7 @@ decl_module! { fn note_imminent_preimage(origin, encoded_proposal: Vec) { let who = ensure_signed(origin)?; let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - ensure!(!>::exists(&proposal_hash), Error::::DuplicatePreimage); + ensure!(!>::contains_key(&proposal_hash), Error::::DuplicatePreimage); let queue = >::get(); ensure!(queue.iter().any(|item| &item.1 == &proposal_hash), Error::::NotImminent); @@ -785,10 +802,22 @@ decl_module! { let queue = >::get(); ensure!(!queue.iter().any(|item| &item.1 == &proposal_hash), Error::::Imminent); - let _ = T::Currency::repatriate_reserved(&old, &who, deposit); + let _ = T::Currency::repatriate_reserved(&old, &who, deposit, BalanceStatus::Free); >::remove(&proposal_hash); Self::deposit_event(RawEvent::PreimageReaped(proposal_hash, old, deposit, who)); } + + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] + fn unlock(origin, target: T::AccountId) { + ensure_signed(origin)?; + + let expiry = Locks::::get(&target).ok_or(Error::::NotLocked)?; + ensure!(expiry <= system::Module::::block_number(), Error::::NotExpired); + + T::Currency::remove_lock(DEMOCRACY_ID, &target); + Locks::::remove(&target); + Self::deposit_event(RawEvent::Unlocked(target)); + } } } @@ -803,7 +832,7 @@ impl Module { /// Return true if `ref_index` is an on-going referendum. pub fn is_active_referendum(ref_index: ReferendumIndex) -> bool { - >::exists(ref_index) + >::contains_key(ref_index) } /// Get all referenda currently active. @@ -884,7 +913,7 @@ impl Module { if recursion_limit == 0 { return (Zero::zero(), Zero::zero()); } >::enumerate() .filter(|(delegator, (delegate, _))| - *delegate == to && !>::exists(&(ref_index, delegator.clone())) + *delegate == to && !>::contains_key(&(ref_index, delegator.clone())) ).fold( (Zero::zero(), Zero::zero()), |(votes_acc, turnout_acc), (delegator, (_delegate, max_conviction))| { @@ -934,7 +963,7 @@ impl Module { /// Actually enact a vote, if legit. fn do_vote(who: T::AccountId, ref_index: ReferendumIndex, vote: Vote) -> DispatchResult { ensure!(Self::is_active_referendum(ref_index), Error::::ReferendumInvalid); - if !>::exists((ref_index, &who)) { + if !>::contains_key((ref_index, &who)) { >::append_or_insert(ref_index, &[&who][..]); } >::insert((ref_index, &who), vote); @@ -1074,14 +1103,15 @@ impl Module { // now plus: the base lock period multiplied by the number of periods this voter // offered to lock should they win... let locked_until = now + T::EnactmentPeriod::get() * conviction.lock_periods().into(); + Locks::::insert(&a, locked_until); // ...extend their bondage until at least then. T::Currency::extend_lock( DEMOCRACY_ID, &a, Bounded::max_value(), - locked_until, WithdrawReason::Transfer.into() ); + } Self::clear_referendum(index); @@ -1132,8 +1162,8 @@ impl Module { } } -impl OnFreeBalanceZero for Module { - fn on_free_balance_zero(who: &T::AccountId) { +impl OnReapAccount for Module { + fn on_reap_account(who: &T::AccountId) { >::remove(who) } } @@ -1148,11 +1178,12 @@ mod tests { }; use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup, Bounded, BadOrigin}, + traits::{BlakeTwo256, IdentityLookup, Bounded, BadOrigin, OnInitialize}, testing::Header, Perbill, }; use pallet_balances::{BalanceLock, Error as BalancesError}; use frame_system::EnsureSignedBy; + use sp_storage::Storage; const AYE: Vote = Vote{ aye: true, conviction: Conviction::None }; const NAY: Vote = Vote{ aye: false, conviction: Conviction::None }; @@ -1196,23 +1227,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const LaunchPeriod: u64 = 2; @@ -1266,7 +1293,6 @@ mod tests { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig::{ balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); GenesisConfig::default().assimilate_storage(&mut t).unwrap(); sp_io::TestExternalities::new(t) @@ -1276,11 +1302,60 @@ mod tests { type Balances = pallet_balances::Module; type Democracy = Module; + #[test] + fn lock_info_via_migration_should_work() { + let mut s = Storage::default(); + use hex_literal::hex; + // A dump of data from the previous version for which we know account 1 has 5 of its 10 + // reserved and 3 of the rest is locked for misc. Account 2 has all 20 locked until block 5 + // for everything and additionally 3 locked for just fees. + let data = vec![ + (hex!["26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"].to_vec(), hex!["0100000000000000"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"].to_vec(), hex!["02000000"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"].to_vec(), hex!["08000000000000000000000000"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc"].to_vec(), hex!["4545454545454545454545454545454545454545454545454545454545454545"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c11874681e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c"].to_vec(), hex!["4545454545454545454545454545454545454545454545454545454545454545"].to_vec()), + (hex!["3a636f6465"].to_vec(), hex![""].to_vec()), + (hex!["3a65787472696e7369635f696e646578"].to_vec(), hex!["00000000"].to_vec()), + (hex!["3a686561707061676573"].to_vec(), hex!["0800000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc61dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b"].to_vec(), hex!["046d697363202020200300000000000000ffffffffffffffff04"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc66cddb367afbd583bb48f9bbd7d5ba3b1d0738b4881b1cddd38169526d8158137"].to_vec(), hex!["0474786665657320200300000000000000ffffffffffffffff01"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6e88b43fded6323ef02ffeffbd8c40846ee09bf316271bd22369659c959dd733a"].to_vec(), hex!["08616c6c20202020200300000000000000ffffffffffffffff1f64656d6f63726163ffffffffffffffff030000000000000002"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f3c22813def93ef32c365b55cb92f10f91dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b"].to_vec(), hex!["0500000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80"].to_vec(), hex!["d200000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f5f27b51b5ec208ee9cb25b55d8728243b8788bb218b185b63e3e92653953f29b6b143fb8cf5159fc908632e6fe490501"].to_vec(), hex!["1e0000000000000006000000000000000200000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b41dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b"].to_vec(), hex!["0500000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b46cddb367afbd583bb48f9bbd7d5ba3b1d0738b4881b1cddd38169526d8158137"].to_vec(), hex!["1e00000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4b8788bb218b185b63e3e92653953f29b6b143fb8cf5159fc908632e6fe490501"].to_vec(), hex!["3c00000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4e88b43fded6323ef02ffeffbd8c40846ee09bf316271bd22369659c959dd733a"].to_vec(), hex!["1400000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4e96760d274653a39b429a87ebaae9d3aa4fdf58b9096cf0bebc7c4e5a4c2ed8d"].to_vec(), hex!["2800000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4effb728943197fd12e694cbf3f3ede28fbf7498b0370c6dfa0013874b417c178"].to_vec(), hex!["3200000000000000"].to_vec()), + (hex!["f2794c22e353e9a839f12faab03a911b7f17cdfbfa73331856cca0acddd7842e"].to_vec(), hex!["00000000"].to_vec()), + (hex!["f2794c22e353e9a839f12faab03a911bbdcb0c5143a8617ed38ae3810dd45bc6"].to_vec(), hex!["00000000"].to_vec()), + (hex!["f2794c22e353e9a839f12faab03a911be2f6cb0456905c189bcb0458f9440f13"].to_vec(), hex!["00000000"].to_vec()), + ]; + s.top = data.into_iter().collect(); + sp_io::TestExternalities::new(s).execute_with(|| { + Balances::on_initialize(1); + assert_eq!(Balances::free_balance(1), 5); + assert_eq!(Balances::reserved_balance(1), 5); + assert_eq!(Balances::usable_balance(&1), 2); + assert_eq!(Balances::usable_balance_for_fees(&1), 5); + assert_eq!(Balances::free_balance(2), 20); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::usable_balance(&2), 0); + assert_eq!(Balances::usable_balance_for_fees(&2), 17); + fast_forward_to(5); + assert_ok!(Democracy::unlock(Origin::signed(2), 2)); + assert_eq!(Balances::usable_balance(&2), 17); + }); + } + #[test] fn params_should_work() { new_test_ext().execute_with(|| { assert_eq!(Democracy::referendum_count(), 0); - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); assert_eq!(Balances::total_issuance(), 210); }); } @@ -1346,7 +1421,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); }); } @@ -1398,8 +1473,8 @@ mod tests { next_block(); assert_ok!(Democracy::reap_preimage(Origin::signed(6), set_balance_proposal_hash(2))); - assert_eq!(Balances::reserved_balance(6), 0); assert_eq!(Balances::free_balance(6), 60); + assert_eq!(Balances::reserved_balance(6), 0); }); } @@ -1862,7 +1937,7 @@ mod tests { // referendum passes and wait another two blocks for enactment. fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -1935,7 +2010,7 @@ mod tests { assert_eq!(Democracy::tally(r), (1, 0, 1)); fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -1960,7 +2035,7 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -1986,7 +2061,7 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2013,7 +2088,7 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2041,7 +2116,7 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2073,7 +2148,7 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2086,9 +2161,9 @@ mod tests { assert_ok!(Democracy::second(Origin::signed(5), 0)); assert_ok!(Democracy::second(Origin::signed(5), 0)); assert_ok!(Democracy::second(Origin::signed(5), 0)); - assert_eq!(Balances::free_balance(&1), 5); - assert_eq!(Balances::free_balance(&2), 15); - assert_eq!(Balances::free_balance(&5), 35); + assert_eq!(Balances::free_balance(1), 5); + assert_eq!(Balances::free_balance(2), 15); + assert_eq!(Balances::free_balance(5), 35); }); } @@ -2102,9 +2177,9 @@ mod tests { assert_ok!(Democracy::second(Origin::signed(5), 0)); assert_ok!(Democracy::second(Origin::signed(5), 0)); fast_forward_to(3); - assert_eq!(Balances::free_balance(&1), 10); - assert_eq!(Balances::free_balance(&2), 20); - assert_eq!(Balances::free_balance(&5), 50); + assert_eq!(Balances::free_balance(1), 10); + assert_eq!(Balances::free_balance(2), 20); + assert_eq!(Balances::free_balance(5), 50); }); } @@ -2172,7 +2247,7 @@ mod tests { assert_eq!(Democracy::tally(r2), (1, 0, 1)); next_block(); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); assert_ok!(Democracy::vote(Origin::signed(1), r1, AYE)); assert_eq!(Democracy::voters_for(r1), vec![1]); @@ -2180,7 +2255,7 @@ mod tests { assert_eq!(Democracy::tally(r1), (1, 0, 1)); next_block(); - assert_eq!(Balances::free_balance(&42), 3); + assert_eq!(Balances::free_balance(42), 3); }); } @@ -2203,7 +2278,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2223,7 +2298,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); }); } @@ -2246,7 +2321,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); }); } @@ -2273,7 +2348,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2297,11 +2372,11 @@ mod tests { assert_eq!(Democracy::tally(r), (21, 0, 21)); next_block(); - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); next_block(); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2323,14 +2398,14 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); }); } #[test] fn passing_low_turnout_voting_should_work() { new_test_ext().execute_with(|| { - assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::free_balance(42), 0); assert_eq!(Balances::total_issuance(), 210); System::set_block_number(1); @@ -2349,7 +2424,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } @@ -2392,24 +2467,44 @@ mod tests { assert_eq!(Balances::locks(2), vec![BalanceLock { id: DEMOCRACY_ID, amount: u64::max_value(), - until: 18, - reasons: WithdrawReason::Transfer.into() + reasons: pallet_balances::Reasons::Misc, }]); + assert_eq!(Democracy::locks(2), Some(18)); assert_eq!(Balances::locks(3), vec![BalanceLock { id: DEMOCRACY_ID, amount: u64::max_value(), - until: 10, - reasons: WithdrawReason::Transfer.into() + reasons: pallet_balances::Reasons::Misc, }]); + assert_eq!(Democracy::locks(3), Some(10)); assert_eq!(Balances::locks(4), vec![BalanceLock { id: DEMOCRACY_ID, amount: u64::max_value(), - until: 6, - reasons: WithdrawReason::Transfer.into() + reasons: pallet_balances::Reasons::Misc, }]); + assert_eq!(Democracy::locks(4), Some(6)); assert_eq!(Balances::locks(5), vec![]); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); + + assert_noop!(Democracy::unlock(Origin::signed(1), 1), Error::::NotLocked); + + fast_forward_to(5); + assert_noop!(Democracy::unlock(Origin::signed(1), 4), Error::::NotExpired); + fast_forward_to(6); + assert_ok!(Democracy::unlock(Origin::signed(1), 4)); + assert_noop!(Democracy::unlock(Origin::signed(1), 4), Error::::NotLocked); + + fast_forward_to(9); + assert_noop!(Democracy::unlock(Origin::signed(1), 3), Error::::NotExpired); + fast_forward_to(10); + assert_ok!(Democracy::unlock(Origin::signed(1), 3)); + assert_noop!(Democracy::unlock(Origin::signed(1), 3), Error::::NotLocked); + + fast_forward_to(17); + assert_noop!(Democracy::unlock(Origin::signed(1), 2), Error::::NotExpired); + fast_forward_to(18); + assert_ok!(Democracy::unlock(Origin::signed(1), 2)); + assert_noop!(Democracy::unlock(Origin::signed(1), 2), Error::::NotLocked); }); } @@ -2446,7 +2541,7 @@ mod tests { next_block(); next_block(); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } } diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections-phragmen/Cargo.toml index 9d0c3ed61c3b94bb6c6204b86fd675cc1587aa92..f3cfc800f01c015738304e2281ba6180ffa4b339 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-elections-phragmen" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 1d9573cbbf3201b240e50278cdaed761f783cdcd..f250e771216e601f4129bd8043d7e86d97791bc3 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -83,12 +83,15 @@ #![cfg_attr(not(feature = "std"), no_std)] use sp_std::prelude::*; -use sp_runtime::{print, DispatchResult, DispatchError, traits::{Zero, StaticLookup, Bounded, Convert}}; +use sp_runtime::{ + print, DispatchResult, DispatchError, Perbill, + traits::{Zero, StaticLookup, Convert}, +}; use frame_support::{ decl_storage, decl_event, ensure, decl_module, decl_error, weights::SimpleDispatchInfo, traits::{ Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons, - ChangeMembers, OnUnbalanced, WithdrawReason, Contains + ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus } }; use sp_phragmen::ExtendedBalance; @@ -157,9 +160,9 @@ decl_storage! { pub ElectionRounds get(fn election_rounds): u32 = Zero::zero(); /// Votes of a particular voter, with the round index of the votes. - pub VotesOf get(fn votes_of): linked_map T::AccountId => Vec; + pub VotesOf get(fn votes_of): linked_map hasher(blake2_256) T::AccountId => Vec; /// Locked stake of a voter. - pub StakeOf get(fn stake_of): map T::AccountId => BalanceOf; + pub StakeOf get(fn stake_of): map hasher(blake2_256) T::AccountId => BalanceOf; /// The present candidate list. Sorted based on account-id. A current member or a runner can /// never enter this vector and is always implicitly assumed to be a candidate. @@ -260,7 +263,6 @@ decl_module! { MODULE_ID, &who, locked_balance, - T::BlockNumber::max_value(), WithdrawReasons::except(WithdrawReason::TransactionPayment), ); >::insert(&who, locked_balance); @@ -312,7 +314,7 @@ decl_module! { let valid = Self::is_defunct_voter(&target); if valid { // reporter will get the voting bond of the target - T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get())?; + T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get(), BalanceStatus::Free)?; // remove the target. They are defunct. Self::do_remove_voter(&target, false); } else { @@ -521,7 +523,7 @@ impl Module { /// /// State: O(1). fn is_voter(who: &T::AccountId) -> bool { - >::exists(who) + >::contains_key(who) } /// Check if `who` is currently an active member. @@ -638,7 +640,7 @@ impl Module { let voters_and_votes = >::enumerate() .map(|(v, i)| (v, i)) .collect::)>>(); - let maybe_phragmen_result = sp_phragmen::elect::<_, _, _, T::CurrencyToVote>( + let maybe_phragmen_result = sp_phragmen::elect::<_, _, _, T::CurrencyToVote, Perbill>( num_to_elect, 0, candidates, @@ -665,7 +667,7 @@ impl Module { .filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } ) .collect::>(); - let support_map = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote>( + let support_map = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote, Perbill>( &new_set, &phragmen_result.assignments, Self::locked_stake_of, @@ -812,26 +814,22 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; - } +} impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); - type OnFreeBalanceZero = (); - type OnReapAccount = System; type Event = Event; - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; - } + type AccountStore = frame_system::Module; +} parameter_types! { pub const CandidacyBond: u64 = 3; @@ -938,7 +936,7 @@ mod tests { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Balances: pallet_balances::{Module, Call, Event, Config}, Elections: elections::{Module, Call, Event}, } @@ -989,7 +987,6 @@ mod tests { (5, 50 * self.balance_factor), (6, 60 * self.balance_factor) ], - vesting: vec![], }), }.build_storage().unwrap().into() } @@ -1429,7 +1426,7 @@ mod tests { assert_ok!(Elections::report_defunct_voter(Origin::signed(5), 3)); assert_eq!( - System::events()[1].event, + System::events()[7].event, Event::elections(RawEvent::VoterReported(3, 5, true)) ); @@ -1458,7 +1455,7 @@ mod tests { assert_ok!(Elections::report_defunct_voter(Origin::signed(5), 4)); assert_eq!( - System::events()[1].event, + System::events()[7].event, Event::elections(RawEvent::VoterReported(4, 5, false)) ); @@ -1869,7 +1866,7 @@ mod tests { assert_eq!(balances(&5), (45, 2)); assert_eq!( - System::events()[0].event, + System::events()[6].event, Event::elections(RawEvent::NewTerm(vec![(4, 40), (5, 50)])), ); }) diff --git a/frame/elections/Cargo.toml b/frame/elections/Cargo.toml index 495151018e7d89aee06fc1408bd4883bdcffee0d..b7c98a65e15b45aa0a52113319102497236b58b5 100644 --- a/frame/elections/Cargo.toml +++ b/frame/elections/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-elections" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/elections/src/lib.rs b/frame/elections/src/lib.rs index e722c4db8050eee0b5f721ee6341b71a6d9d4080..a3fdd74426c3a1b94fca7a9eab7c6466d95b1e4a 100644 --- a/frame/elections/src/lib.rs +++ b/frame/elections/src/lib.rs @@ -26,13 +26,13 @@ use sp_std::prelude::*; use sp_runtime::{ RuntimeDebug, DispatchResult, print, - traits::{Zero, One, StaticLookup, Bounded, Saturating}, + traits::{Zero, One, StaticLookup, Saturating}, }; use frame_support::{ decl_storage, decl_event, ensure, decl_module, decl_error, weights::SimpleDispatchInfo, traits::{ - Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, + Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, BalanceStatus, OnUnbalanced, ReservableCurrency, WithdrawReason, WithdrawReasons, ChangeMembers } }; @@ -235,14 +235,17 @@ decl_storage! { // bit-wise manner. In order to get a human-readable representation (`Vec`), use // [`all_approvals_of`]. Furthermore, each vector of scalars is chunked with the cap of // `APPROVAL_SET_SIZE`. - pub ApprovalsOf get(fn approvals_of): map (T::AccountId, SetIndex) => Vec; + pub ApprovalsOf get(fn approvals_of): + map hasher(blake2_256) (T::AccountId, SetIndex) => Vec; /// The vote index and list slot that the candidate `who` was registered or `None` if they /// are not currently registered. - pub RegisterInfoOf get(fn candidate_reg_info): map T::AccountId => Option<(VoteIndex, u32)>; + pub RegisterInfoOf get(fn candidate_reg_info): + map hasher(blake2_256) T::AccountId => Option<(VoteIndex, u32)>; /// Basic information about a voter. - pub VoterInfoOf get(fn voter_info): map T::AccountId => Option>>; + pub VoterInfoOf get(fn voter_info): + map hasher(blake2_256) T::AccountId => Option>>; /// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]). - pub Voters get(fn voters): map SetIndex => Vec>; + pub Voters get(fn voters): map hasher(blake2_256) SetIndex => Vec>; /// the next free set to store a voter in. This will keep growing. pub NextVoterSet get(fn next_nonfull_voter_set): SetIndex = 0; /// Current number of Voters. @@ -263,7 +266,7 @@ decl_storage! { /// Who is able to vote for whom. Value is the fund-holding account, key is the /// vote-transaction-sending account. - pub Proxy get(fn proxy): map T::AccountId => Option; + pub Proxy get(fn proxy): map hasher(blake2_256) T::AccountId => Option; } } @@ -498,7 +501,7 @@ decl_module! { if valid { // This only fails if `reporter` doesn't exist, which it clearly must do since its // the origin. Still, it's no more harmful to propagate any error at this point. - T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get())?; + T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get(), BalanceStatus::Free)?; Self::deposit_event(RawEvent::VoterReaped(who, reporter)); } else { let imbalance = T::Currency::slash_reserved(&reporter, T::VotingBond::get()).0; @@ -522,7 +525,7 @@ decl_module! { let who = ensure_signed(origin)?; ensure!(!Self::presentation_active(), Error::::CannotRetractPresenting); - ensure!(>::exists(&who), Error::::RetractNonVoter); + ensure!(>::contains_key(&who), Error::::RetractNonVoter); let index = index as usize; let voter = Self::voter_at(index).ok_or(Error::::InvalidRetractionIndex)?; ensure!(voter == who, Error::::InvalidRetractionIndex); @@ -727,7 +730,7 @@ impl Module { /// If `who` a candidate at the moment? pub fn is_a_candidate(who: &T::AccountId) -> bool { - >::exists(who) + >::contains_key(who) } /// Iff the member `who` still has a seat at blocknumber `n` returns `true`. @@ -891,7 +894,6 @@ impl Module { MODULE_ID, &who, locked_balance, - T::BlockNumber::max_value(), WithdrawReasons::all(), ); diff --git a/frame/elections/src/mock.rs b/frame/elections/src/mock.rs index 178637e088883eebd33397a4ef2f13c8174fa267..c5af6ba0456b95a0b0fe23e72588adf89489d70b 100644 --- a/frame/elections/src/mock.rs +++ b/frame/elections/src/mock.rs @@ -39,9 +39,9 @@ parameter_types! { } impl frame_system::Trait for Test { type Origin = Origin; + type Call = (); type Index = u64; type BlockNumber = u64; - type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -54,24 +54,20 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type Event = Event; - type TransferPayment = (); type DustRemoval = (); + type Event = Event; type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -154,7 +150,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Balances: pallet_balances::{Module, Call, Event, Config}, Elections: elections::{Module, Call, Event, Config}, } @@ -222,7 +218,6 @@ impl ExtBuilder { (5, 50 * self.balance_factor), (6, 60 * self.balance_factor) ], - vesting: vec![], }), elections: Some(elections::GenesisConfig::{ members: vec![], diff --git a/frame/elections/src/tests.rs b/frame/elections/src/tests.rs index 48161bcee49ac12c3fe1da7529dc0bc924c0c650..e26f0312903fcb61c7b94fe2d93e7ac0da77e86b 100644 --- a/frame/elections/src/tests.rs +++ b/frame/elections/src/tests.rs @@ -1179,14 +1179,14 @@ fn election_present_when_presenter_is_poor_should_not_work() { // -3 assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Balances::free_balance(&1), 12); + assert_eq!(Balances::free_balance(1), 12); // -2 -5 assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 15)); assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_eq!(Balances::free_balance(&1), 5); - assert_eq!(Balances::reserved_balance(&1), 5); + assert_eq!(Balances::free_balance(1), 5); + assert_eq!(Balances::reserved_balance(1), 5); if p > 5 { assert_noop!(Elections::present_winner( Origin::signed(1), 1, 10, 0), diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index 933b450ee448a4945f23e8f4fd03c2a280d1c2af..66c809fa44c7ee76d148298e019fbade3aba8d60 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-evm" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } @@ -15,9 +16,9 @@ sp-core = { version = "2.0.0", default-features = false, path = "../../primitive sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } -primitive-types = { version = "0.6", default-features = false, features = ["rlp"] } +primitive-types = { version = "0.6.2", default-features = false, features = ["rlp"] } rlp = { version = "0.4", default-features = false } -evm = { version = "0.14", default-features = false } +evm = { version = "0.15", default-features = false } sha3 = { version = "0.8", default-features = false } [features] diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 8d504127245d0b18d9b14cf35e649dcd20daa2aa..a12d06c69b6057036d4cc9690204ff7eebc487ca 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -25,7 +25,7 @@ pub use crate::backend::{Account, Log, Vicinity, Backend}; use sp_std::{vec::Vec, marker::PhantomData}; use frame_support::{ensure, decl_module, decl_storage, decl_event, decl_error}; -use frame_support::weights::{Weight, WeighData, ClassifyDispatch, DispatchClass, PaysFee}; +use frame_support::weights::{Weight, DispatchClass, FunctionOf}; use frame_support::traits::{Currency, WithdrawReason, ExistenceRequirement}; use frame_system::{self as system, ensure_signed}; use sp_runtime::ModuleId; @@ -34,6 +34,7 @@ use sp_core::{U256, H256, H160, Hasher}; use sp_runtime::{ DispatchResult, traits::{UniqueSaturatedInto, AccountIdConversion, SaturatedConversion}, }; +use sha3::{Digest, Keccak256}; use evm::{ExitReason, ExitSucceed, ExitError}; use evm::executor::StackExecutor; use evm::backend::ApplyBackend; @@ -115,38 +116,6 @@ impl Precompiles for () { } } -struct WeightForCallCreate; - -impl WeighData<(&H160, &Vec, &U256, &u32, &U256)> for WeightForCallCreate { - fn weigh_data( - &self, - (_, _, _, gas_provided, gas_price): (&H160, &Vec, &U256, &u32, &U256) - ) -> Weight { - (*gas_price).saturated_into::().saturating_mul(*gas_provided) - } -} - -impl WeighData<(&Vec, &U256, &u32, &U256)> for WeightForCallCreate { - fn weigh_data( - &self, - (_, _, gas_provided, gas_price): (&Vec, &U256, &u32, &U256) - ) -> Weight { - (*gas_price).saturated_into::().saturating_mul(*gas_provided) - } -} - -impl ClassifyDispatch for WeightForCallCreate { - fn classify_dispatch(&self, _: T) -> DispatchClass { - DispatchClass::Normal - } -} - -impl PaysFee for WeightForCallCreate { - fn pays_fee(&self, _: T) -> bool { - true - } -} - /// EVM module trait pub trait Trait: frame_system::Trait + pallet_timestamp::Trait { /// Calculator for current gas price. @@ -163,9 +132,9 @@ pub trait Trait: frame_system::Trait + pallet_timestamp::Trait { decl_storage! { trait Store for Module as Example { - Accounts get(fn accounts) config(): map H160 => Account; - AccountCodes: map H160 => Vec; - AccountStorages: double_map H160, H256 => H256; + Accounts get(fn accounts) config(): map hasher(blake2_256) H160 => Account; + AccountCodes: map hasher(blake2_256) H160 => Vec; + AccountStorages: double_map hasher(blake2_256) H160, hasher(blake2_256) H256 => H256; } } @@ -174,6 +143,8 @@ decl_event! { pub enum Event { /// Ethereum events from contracts. Log(Log), + /// A contract has been created at given address. + Created(H160), } } @@ -195,6 +166,8 @@ decl_error! { ExitReasonRevert, /// Call returned VM fatal error ExitReasonFatal, + /// Nonce is invalid + InvalidNonce, } } @@ -248,7 +221,7 @@ decl_module! { } /// Issue an EVM call operation. This is similar to a message call transaction in Ethereum. - #[weight = WeightForCallCreate] + #[weight = FunctionOf(|(_, _, _, gas_limit, gas_price, _): (&H160, &Vec, &U256, &u32, &U256, &Option)| (*gas_price).saturated_into::().saturating_mul(*gas_limit), DispatchClass::Normal, true)] fn call( origin, target: H160, @@ -256,113 +229,99 @@ decl_module! { value: U256, gas_limit: u32, gas_price: U256, + nonce: Option, ) -> DispatchResult { let sender = ensure_signed(origin)?; - ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::::GasPriceTooLow); let source = T::ConvertAccountId::convert_account_id(&sender); - let vicinity = Vicinity { - gas_price, - origin: source, - }; - - let mut backend = Backend::::new(&vicinity); - let mut executor = StackExecutor::new_with_precompile( - &backend, - gas_limit as usize, - &backend::GASOMETER_CONFIG, - T::Precompiles::execute, - ); - - let total_fee = gas_price.checked_mul(U256::from(gas_limit)) - .ok_or(Error::::FeeOverflow)?; - if Accounts::get(&source).balance < - value.checked_add(total_fee).ok_or(Error::::PaymentOverflow)? - { - Err(Error::::BalanceLow)? - } - executor.withdraw(source, total_fee).map_err(|_| Error::::WithdrawFailed)?; - - let reason = executor.transact_call( + Self::execute_evm( source, - target, value, - input, - gas_limit as usize, - ); - - let ret = match reason { - ExitReason::Succeed(_) => Ok(()), - ExitReason::Error(_) => Err(Error::::ExitReasonFailed), - ExitReason::Revert(_) => Err(Error::::ExitReasonRevert), - ExitReason::Fatal(_) => Err(Error::::ExitReasonFatal), - }; - let actual_fee = executor.fee(gas_price); - executor.deposit(source, total_fee.saturating_sub(actual_fee)); - - let (values, logs) = executor.deconstruct(); - backend.apply(values, logs, true); - - ret.map_err(Into::into) + gas_limit, + gas_price, + nonce, + |executor| ((), executor.transact_call( + source, + target, + value, + input, + gas_limit as usize, + )), + ).map_err(Into::into) } /// Issue an EVM create operation. This is similar to a contract creation transaction in /// Ethereum. - #[weight = WeightForCallCreate] + #[weight = FunctionOf(|(_, _, gas_limit, gas_price, _): (&Vec, &U256, &u32, &U256, &Option)| (*gas_price).saturated_into::().saturating_mul(*gas_limit), DispatchClass::Normal, true)] fn create( origin, init: Vec, value: U256, gas_limit: u32, gas_price: U256, + nonce: Option, ) -> DispatchResult { let sender = ensure_signed(origin)?; - ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::::GasPriceTooLow); - let source = T::ConvertAccountId::convert_account_id(&sender); - let vicinity = Vicinity { + let create_address = Self::execute_evm( + source, + value, + gas_limit, gas_price, - origin: source, - }; - - let mut backend = Backend::::new(&vicinity); - let mut executor = StackExecutor::new_with_precompile( - &backend, - gas_limit as usize, - &backend::GASOMETER_CONFIG, - T::Precompiles::execute, - ); - - let total_fee = gas_price.checked_mul(U256::from(gas_limit)) - .ok_or(Error::::FeeOverflow)?; - if Accounts::get(&source).balance < - value.checked_add(total_fee).ok_or(Error::::PaymentOverflow)? - { - Err(Error::::BalanceLow)? - } - executor.withdraw(source, total_fee).map_err(|_| Error::::WithdrawFailed)?; - - let reason = executor.transact_create( + nonce, + |executor| { + (executor.create_address( + evm::CreateScheme::Legacy { caller: source }, + ), executor.transact_create( + source, + value, + init, + gas_limit as usize, + )) + }, + )?; + + Module::::deposit_event(Event::Created(create_address)); + Ok(()) + } + + /// Issue an EVM create2 operation. + #[weight = FunctionOf(|(_, _, _, gas_limit, gas_price, _): (&Vec, &H256, &U256, &u32, &U256, &Option)| (*gas_price).saturated_into::().saturating_mul(*gas_limit), DispatchClass::Normal, true)] + fn create2( + origin, + init: Vec, + salt: H256, + value: U256, + gas_limit: u32, + gas_price: U256, + nonce: Option, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let source = T::ConvertAccountId::convert_account_id(&sender); + + let code_hash = H256::from_slice(Keccak256::digest(&init).as_slice()); + let create_address = Self::execute_evm( source, value, - init, - gas_limit as usize, - ); - - let ret = match reason { - ExitReason::Succeed(_) => Ok(()), - ExitReason::Error(_) => Err(Error::::ExitReasonFailed), - ExitReason::Revert(_) => Err(Error::::ExitReasonRevert), - ExitReason::Fatal(_) => Err(Error::::ExitReasonFatal), - }; - let actual_fee = executor.fee(gas_price); - executor.deposit(source, total_fee.saturating_sub(actual_fee)); - - let (values, logs) = executor.deconstruct(); - backend.apply(values, logs, true); - - ret.map_err(Into::into) + gas_limit, + gas_price, + nonce, + |executor| { + (executor.create_address( + evm::CreateScheme::Create2 { caller: source, code_hash, salt }, + ), executor.transact_create2( + source, + value, + init, + salt, + gas_limit as usize, + )) + }, + )?; + + Module::::deposit_event(Event::Created(create_address)); + Ok(()) } } } @@ -399,4 +358,59 @@ impl Module { AccountCodes::remove(address); AccountStorages::remove_prefix(address); } + + /// Execute an EVM operation. + fn execute_evm( + source: H160, + value: U256, + gas_limit: u32, + gas_price: U256, + nonce: Option, + f: F, + ) -> Result> where + F: FnOnce(&mut StackExecutor>) -> (R, ExitReason), + { + ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::::GasPriceTooLow); + + let vicinity = Vicinity { + gas_price, + origin: source, + }; + + let mut backend = Backend::::new(&vicinity); + let mut executor = StackExecutor::new_with_precompile( + &backend, + gas_limit as usize, + &backend::GASOMETER_CONFIG, + T::Precompiles::execute, + ); + + let total_fee = gas_price.checked_mul(U256::from(gas_limit)) + .ok_or(Error::::FeeOverflow)?; + let total_payment = value.checked_add(total_fee).ok_or(Error::::PaymentOverflow)?; + let source_account = Accounts::get(&source); + ensure!(source_account.balance >= total_payment, Error::::BalanceLow); + executor.withdraw(source, total_fee).map_err(|_| Error::::WithdrawFailed)?; + + if let Some(nonce) = nonce { + ensure!(source_account.nonce == nonce, Error::::InvalidNonce); + } + + let (retv, reason) = f(&mut executor); + + let ret = match reason { + ExitReason::Succeed(_) => Ok(retv), + ExitReason::Error(_) => Err(Error::::ExitReasonFailed), + ExitReason::Revert(_) => Err(Error::::ExitReasonRevert), + ExitReason::Fatal(_) => Err(Error::::ExitReasonFatal), + }; + + let actual_fee = executor.fee(gas_price); + executor.deposit(source, total_fee.saturating_sub(actual_fee)); + + let (values, logs) = executor.deconstruct(); + backend.apply(values, logs, true); + + ret + } } diff --git a/frame/example/Cargo.toml b/frame/example/Cargo.toml index fd80d8b8df1793ffd408b3c3ca3f07c2944a15ac..0d37940fc73170feaae1eace009531ea9fb692d7 100644 --- a/frame/example/Cargo.toml +++ b/frame/example/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-example" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/example/src/lib.rs b/frame/example/src/lib.rs index 80569a1b573747d0748abb20c244c8778b28ec8e..fb8a162d41827a20d94609aef1017fe3d553ace1 100644 --- a/frame/example/src/lib.rs +++ b/frame/example/src/lib.rs @@ -329,7 +329,7 @@ decl_storage! { // `pub? Name get(fn getter_name)? [config()|config(myname)] [build(|_| {...})] : (= )?;` // where `` is either: // - `Type` (a basic value item); or - // - `map KeyType => ValueType` (a map item). + // - `map hasher(HasherKind) KeyType => ValueType` (a map item). // // Note that there are two optional modifiers for the storage type declaration. // - `Foo: Option`: @@ -339,7 +339,7 @@ decl_storage! { // - `Foo::put(1); Foo::get()` returns `1`; // - `Foo::kill(); Foo::get()` returns `0` (u32::default()). // e.g. Foo: u32; - // e.g. pub Bar get(fn bar): map T::AccountId => Vec<(T::Balance, u64)>; + // e.g. pub Bar get(fn bar): map hasher(blake2_256) T::AccountId => Vec<(T::Balance, u64)>; // // For basic value items, you'll get a type which implements // `frame_support::StorageValue`. For map items, you'll get a type which @@ -351,7 +351,7 @@ decl_storage! { Dummy get(fn dummy) config(): Option; // A map that has enumerable entries. - Bar get(fn bar) config(): linked_map T::AccountId => T::Balance; + Bar get(fn bar) config(): linked_map hasher(blake2_256) T::AccountId => T::Balance; // this one uses the default, we'll demonstrate the usage of 'mutate' API. Foo get(fn foo) config(): T::Balance; @@ -603,6 +603,7 @@ impl sp_std::fmt::Debug for WatchDummy { } impl SignedExtension for WatchDummy { + const IDENTIFIER: &'static str = "WatchDummy"; type AccountId = T::AccountId; // Note that this could also be assigned to the top-level call enum. It is passed into the // balances module directly and since `Trait: pallet_balances::Trait`, you could also use `T::Call`. @@ -650,7 +651,8 @@ mod tests { // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sp_runtime::{ - Perbill, testing::Header, + Perbill, + testing::Header, traits::{BlakeTwo256, OnInitialize, OnFinalize, IdentityLookup}, }; @@ -686,23 +688,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = pallet_balances::Module; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); - type Event = (); - type TransferPayment = (); type DustRemoval = (); + type Event = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } impl Trait for Test { type Event = (); diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index b2b30edd1cc580a876fce7f9a32338b976e70b74..514fb68d6dbc06b64f849ebe8ef35731b2a22e65 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-executive" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 0507946ef565b9c39b2770be6806598bb63851a3..2d1b306531ba1534188637b993e17fbb1a519c90 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -262,8 +262,7 @@ where fn apply_extrinsic_no_note(uxt: Block::Extrinsic) { let l = uxt.encode().len(); match Self::apply_extrinsic_with_len(uxt, l, None) { - Ok(Ok(())) => (), - Ok(Err(e)) => sp_runtime::print(e), + Ok(_) => (), Err(e) => { let err: &'static str = e.into(); panic!(err) }, } } @@ -358,7 +357,7 @@ mod tests { use sp_core::H256; use sp_runtime::{ generic::Era, Perbill, DispatchError, testing::{Digest, Header, Block}, - traits::{Bounded, Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto}, + traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto}, transaction_validity::{InvalidTransaction, UnknownTransaction, TransactionValidityError}, }; use frame_support::{ @@ -386,7 +385,7 @@ mod tests { fn some_root_operation(origin) { let _ = frame_system::ensure_root(origin); } - #[weight = SimpleDispatchInfo::FreeNormal] + #[weight = SimpleDispatchInfo::InsecureFreeNormal] fn some_unsigned_message(origin) { let _ = frame_system::ensure_none(origin); } @@ -417,6 +416,7 @@ mod tests { impl_outer_event!{ pub enum MetaEvent for Runtime { + system, balances, } } @@ -452,23 +452,19 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Runtime { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = MetaEvent; type DustRemoval = (); - type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -529,7 +525,6 @@ mod tests { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 211)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); let xt = sp_runtime::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(2, 69))); let weight = xt.get_dispatch_info().weight as u64; @@ -553,7 +548,6 @@ mod tests { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); t.into() } @@ -565,7 +559,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("c6b01b27df520ba23adb96e7fc032acb7c586ba1b477c6282de43184111f2091").into(), + state_root: hex!("96797237079b6d6ffab7a47f90ee257a439a0e8268bdab3fe2f1e52572b101de").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, @@ -712,7 +706,6 @@ mod tests { id, &1, 110, - Bounded::max_value(), lock, ); let xt = sp_runtime::testing::TestXt( diff --git a/frame/finality-tracker/Cargo.toml b/frame/finality-tracker/Cargo.toml index 22e1380e656972e238b943a6fe56babff2d26718..7c87df4e54811f2d4dfe2a25f8e14d3409d225d3 100644 --- a/frame/finality-tracker/Cargo.toml +++ b/frame/finality-tracker/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-finality-tracker" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", default-features = false, features = ["derive"] } diff --git a/frame/finality-tracker/src/lib.rs b/frame/finality-tracker/src/lib.rs index c9c9fbb0b5743f930320d2bb6de9fdba7f4f416b..f7bb7b856612ecdf815d877c2ebcbfb0b7368940 100644 --- a/frame/finality-tracker/src/lib.rs +++ b/frame/finality-tracker/src/lib.rs @@ -261,6 +261,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { pub const WindowSize: u64 = 11; diff --git a/frame/generic-asset/Cargo.toml b/frame/generic-asset/Cargo.toml index 87f4c9d1cf81ff2892aacb8efee9f7d5e37945bf..cd236d7b7d0aa6186e956b34e12a844ae7cc5e6e 100644 --- a/frame/generic-asset/Cargo.toml +++ b/frame/generic-asset/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-generic-asset" version = "2.0.0" authors = ["Centrality Developers "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/generic-asset/src/lib.rs b/frame/generic-asset/src/lib.rs index 3b509a683d705082f05b257d23a7fe65a7d9bdc7..59535de4475b39e14e40a79d91da1b04283f6435 100644 --- a/frame/generic-asset/src/lib.rs +++ b/frame/generic-asset/src/lib.rs @@ -156,7 +156,7 @@ use codec::{Decode, Encode, HasCompact, Input, Output, Error as CodecError}; use sp_runtime::{RuntimeDebug, DispatchResult, DispatchError}; use sp_runtime::traits::{ - CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Saturating, SimpleArithmetic, + CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Saturating, AtLeast32Bit, Zero, Bounded, }; @@ -166,7 +166,7 @@ use frame_support::{ decl_event, decl_module, decl_storage, ensure, decl_error, traits::{ Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, ReservableCurrency, - SignedImbalance, UpdateBalanceOutcome, WithdrawReason, WithdrawReasons, TryDrop, + SignedImbalance, WithdrawReason, WithdrawReasons, TryDrop, BalanceStatus, }, Parameter, StorageMap, }; @@ -180,24 +180,24 @@ pub use self::imbalances::{NegativeImbalance, PositiveImbalance}; pub trait Trait: frame_system::Trait { type Balance: Parameter + Member - + SimpleArithmetic + + AtLeast32Bit + Default + Copy + MaybeSerializeDeserialize + Debug; - type AssetId: Parameter + Member + SimpleArithmetic + Default + Copy; + type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy; type Event: From> + Into<::Event>; } pub trait Subtrait: frame_system::Trait { type Balance: Parameter + Member - + SimpleArithmetic + + AtLeast32Bit + Default + Copy + MaybeSerializeDeserialize + Debug; - type AssetId: Parameter + Member + SimpleArithmetic + Default + Copy; + type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy; } impl Subtrait for T { @@ -362,21 +362,7 @@ decl_module! { /// Create a new kind of asset. fn create(origin, options: AssetOptions) -> DispatchResult { let origin = ensure_signed(origin)?; - let id = Self::next_asset_id(); - - let permissions: PermissionVersions = options.permissions.clone().into(); - - // The last available id serves as the overflow mark and won't be used. - let next_id = id.checked_add(&One::one()).ok_or_else(|| Error::::NoIdAvailable)?; - - >::put(next_id); - >::insert(id, &options.initial_issuance); - >::insert(&id, &origin, &options.initial_issuance); - >::insert(&id, permissions); - - Self::deposit_event(RawEvent::Created(id, origin, options)); - - Ok(()) + Self::create_asset(None, Some(origin), options) } /// Transfer some liquid free balance to another account. @@ -441,10 +427,9 @@ decl_module! { } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct BalanceLock { +pub struct BalanceLock { pub id: LockIdentifier, pub amount: Balance, - pub until: BlockNumber, pub reasons: WithdrawReasons, } @@ -454,22 +439,26 @@ decl_storage! { pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { let issuance = config.initial_balance * (config.endowed_accounts.len() as u32).into(); config.assets.iter().map(|id| (id.clone(), issuance)).collect::>() - }): map T::AssetId => T::Balance; + }): map hasher(blake2_256) T::AssetId => T::Balance; /// The free balance of a given asset under an account. - pub FreeBalance: double_map T::AssetId, hasher(twox_128) T::AccountId => T::Balance; + pub FreeBalance: + double_map hasher(blake2_256) T::AssetId, hasher(twox_128) T::AccountId => T::Balance; /// The reserved balance of a given asset under an account. - pub ReservedBalance: double_map T::AssetId, hasher(twox_128) T::AccountId => T::Balance; + pub ReservedBalance: + double_map hasher(blake2_256) T::AssetId, hasher(twox_128) T::AccountId => T::Balance; /// Next available ID for user-created asset. pub NextAssetId get(fn next_asset_id) config(): T::AssetId; /// Permission options for a given asset. - pub Permissions get(fn get_permission): map T::AssetId => PermissionVersions; + pub Permissions get(fn get_permission): + map hasher(blake2_256) T::AssetId => PermissionVersions; /// Any liquidity locks on some account balances. - pub Locks get(fn locks): map T::AccountId => Vec>; + pub Locks get(fn locks): + map hasher(blake2_256) T::AccountId => Vec>; /// The identity of the asset which is the one that is designated for the chain's staking system. pub StakingAssetId get(fn staking_asset_id) config(): T::AssetId; @@ -591,7 +580,7 @@ impl Module { options: AssetOptions, ) -> DispatchResult { let asset_id = if let Some(asset_id) = asset_id { - ensure!(!>::exists(&asset_id), Error::::IdAlreadyTaken); + ensure!(!>::contains_key(&asset_id), Error::::IdAlreadyTaken); ensure!(asset_id < Self::next_asset_id(), Error::::IdUnavailable); asset_id } else { @@ -725,8 +714,8 @@ impl Module { } } - /// Move up to `amount` from reserved balance of account `who` to free balance of account - /// `beneficiary`. + /// Move up to `amount` from reserved balance of account `who` to balance of account + /// `beneficiary`, either free or reserved depending on `status`. /// /// As much funds up to `amount` will be moved as possible. If this is less than `amount`, then /// the `remaining` would be returned, else `Zero::zero()`. @@ -737,13 +726,23 @@ impl Module { who: &T::AccountId, beneficiary: &T::AccountId, amount: T::Balance, + status: BalanceStatus, ) -> T::Balance { let b = Self::reserved_balance(asset_id, who); let slash = sp_std::cmp::min(b, amount); - let original_free_balance = Self::free_balance(asset_id, beneficiary); - let new_free_balance = original_free_balance + slash; - Self::set_free_balance(asset_id, beneficiary, new_free_balance); + match status { + BalanceStatus::Free => { + let original_free_balance = Self::free_balance(asset_id, beneficiary); + let new_free_balance = original_free_balance + slash; + Self::set_free_balance(asset_id, beneficiary, new_free_balance); + } + BalanceStatus::Reserved => { + let original_reserved_balance = Self::reserved_balance(asset_id, beneficiary); + let new_reserved_balance = original_reserved_balance + slash; + Self::set_reserved_balance(asset_id, beneficiary, new_reserved_balance); + } + } let new_reserve_balance = b - slash; Self::set_reserved_balance(asset_id, who, new_reserve_balance); @@ -806,10 +805,8 @@ impl Module { if locks.is_empty() { return Ok(()); } - let now = >::block_number(); if Self::locks(who) - .into_iter() - .all(|l| now >= l.until || new_balance >= l.amount || !l.reasons.intersects(reasons)) + .into_iter().all(|l| new_balance >= l.amount || !l.reasons.intersects(reasons)) { Ok(()) } else { @@ -835,14 +832,11 @@ impl Module { id: LockIdentifier, who: &T::AccountId, amount: T::Balance, - until: T::BlockNumber, reasons: WithdrawReasons, ) { - let now = >::block_number(); let mut new_lock = Some(BalanceLock { id, amount, - until, reasons, }); let mut locks = >::locks(who) @@ -850,10 +844,8 @@ impl Module { .filter_map(|l| { if l.id == id { new_lock.take() - } else if l.until > now { - Some(l) } else { - None + Some(l) } }) .collect::>(); @@ -867,14 +859,11 @@ impl Module { id: LockIdentifier, who: &T::AccountId, amount: T::Balance, - until: T::BlockNumber, reasons: WithdrawReasons, ) { - let now = >::block_number(); let mut new_lock = Some(BalanceLock { id, amount, - until, reasons, }); let mut locks = >::locks(who) @@ -884,13 +873,10 @@ impl Module { new_lock.take().map(|nl| BalanceLock { id: l.id, amount: l.amount.max(nl.amount), - until: l.until.max(nl.until), reasons: l.reasons | nl.reasons, }) - } else if l.until > now { - Some(l) } else { - None + Some(l) } }) .collect::>(); @@ -901,11 +887,8 @@ impl Module { } fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - let now = >::block_number(); - let locks = >::locks(who) - .into_iter() - .filter_map(|l| if l.until > now && l.id != id { Some(l) } else { None }) - .collect::>(); + let mut locks = >::locks(who); + locks.retain(|l| l.id != id); >::insert(who, locks); } } @@ -1107,8 +1090,8 @@ mod imbalances { // its type declaration). // This works as long as `increase_total_issuance_by` doesn't use the Imbalance // types (basically for charging fees). -// This should eventually be refactored so that the three type items that do -// depend on the Imbalance type (TransactionPayment, TransferPayment, DustRemoval) +// This should eventually be refactored so that the two type items that do +// depend on the Imbalance type (TransactionPayment, DustRemoval) // are placed in their own SRML module. struct ElevatedTrait(T); impl Clone for ElevatedTrait { @@ -1133,12 +1116,15 @@ impl frame_system::Trait for ElevatedTrait { type Lookup = T::Lookup; type Header = T::Header; type Event = (); + type BlockHashCount = T::BlockHashCount; type MaximumBlockWeight = T::MaximumBlockWeight; type MaximumBlockLength = T::MaximumBlockLength; type AvailableBlockRatio = T::AvailableBlockRatio; - type BlockHashCount = T::BlockHashCount; type Version = T::Version; type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for ElevatedTrait { type Balance = T::Balance; @@ -1216,7 +1202,7 @@ where } fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value); + let imbalance = Self::make_free_balance_be(who, Self::free_balance(who) + value); if let SignedImbalance::Positive(p) = imbalance { p } else { @@ -1228,10 +1214,7 @@ where fn make_free_balance_be( who: &T::AccountId, balance: Self::Balance, - ) -> ( - SignedImbalance, - UpdateBalanceOutcome, - ) { + ) -> SignedImbalance { let original = >::free_balance(&U::asset_id(), who); let imbalance = if original <= balance { SignedImbalance::Positive(PositiveImbalance::new(balance - original)) @@ -1239,7 +1222,7 @@ where SignedImbalance::Negative(NegativeImbalance::new(original - balance)) }; >::set_free_balance(&U::asset_id(), who, balance); - (imbalance, UpdateBalanceOutcome::Updated) + imbalance } fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { @@ -1315,8 +1298,9 @@ where slashed: &T::AccountId, beneficiary: &T::AccountId, value: Self::Balance, + status: BalanceStatus, ) -> result::Result { - Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value)) + Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value, status)) } } @@ -1349,20 +1333,18 @@ where id: LockIdentifier, who: &T::AccountId, amount: T::Balance, - until: T::BlockNumber, reasons: WithdrawReasons, ) { - >::set_lock(id, who, amount, until, reasons) + >::set_lock(id, who, amount, reasons) } fn extend_lock( id: LockIdentifier, who: &T::AccountId, amount: T::Balance, - until: T::BlockNumber, reasons: WithdrawReasons, ) { - >::extend_lock(id, who, amount, until, reasons) + >::extend_lock(id, who, amount, reasons) } fn remove_lock(id: LockIdentifier, who: &T::AccountId) { diff --git a/frame/generic-asset/src/mock.rs b/frame/generic-asset/src/mock.rs index 6fdf2d2d884f8c4ae73a5db69bfd2422772a2ef3..141f7d00300da65ff88f2112971535a7c84224d9 100644 --- a/frame/generic-asset/src/mock.rs +++ b/frame/generic-asset/src/mock.rs @@ -31,7 +31,7 @@ use frame_support::{parameter_types, impl_outer_event, impl_outer_origin, weight use super::*; impl_outer_origin! { - pub enum Origin for Test where system = frame_system {} + pub enum Origin for Test where system = frame_system {} } // For testing the module, we construct most of a mock runtime. This means @@ -62,6 +62,9 @@ impl frame_system::Trait for Test { type BlockHashCount = BlockHashCount; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { @@ -77,6 +80,7 @@ mod generic_asset { use frame_system as system; impl_outer_event! { pub enum TestEvent for Test { + system, generic_asset, } } diff --git a/frame/generic-asset/src/tests.rs b/frame/generic-asset/src/tests.rs index 7c12c391bf3e3f97978352a2f8964d903ff01e80..ad8fc2bf1bfb1ea7aa8bcd65338283ad64fe6806 100644 --- a/frame/generic-asset/src/tests.rs +++ b/frame/generic-asset/src/tests.rs @@ -556,7 +556,7 @@ fn slash_reserved_should_return_none() { fn repatriate_reserved_return_amount_substracted_by_slash_amount() { ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130), 30); + assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130, BalanceStatus::Free), 30); }); } @@ -571,7 +571,7 @@ fn repatriate_reserved_return_amount_substracted_by_slash_amount() { fn repatriate_reserved_return_none() { ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90), 0); + assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90, BalanceStatus::Free), 0); }); } diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index ab5b34c91fd9f1f9edd6d183ccbc3044eb71f8ce..e67e64dd5f339532b5b7ff725a42964c005edb35 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-grandpa" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index f2ef16dc0eee0b4697e9e19cf1ad40aa1d793869..6164e5ab4b1f5dc23b2ea3c1a5d95ba03c240261 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -34,7 +34,7 @@ use sp_std::prelude::*; use codec::{self as codec, Encode, Decode}; use frame_support::{decl_event, decl_storage, decl_module, decl_error, storage}; use sp_runtime::{ - DispatchResult, generic::{DigestItem, OpaqueDigestItemId}, traits::Zero, Perbill, + DispatchResult, generic::{DigestItem, OpaqueDigestItemId}, traits::Zero, Perbill, PerThing, }; use sp_staking::{ SessionIndex, @@ -175,7 +175,7 @@ decl_storage! { CurrentSetId get(fn current_set_id) build(|_| fg_primitives::SetId::default()): SetId; /// A mapping from grandpa set ID to the index of the *most recent* session for which its members were responsible. - SetIdSession get(fn session_for_set): map SetId => Option; + SetIdSession get(fn session_for_set): map hasher(blake2_256) SetId => Option; } add_extra_genesis { config(authorities): AuthorityList; diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 0015dd1f504aa5038178e3d679c39e17f9a5f10c..77a19c7626594414d48c7897259ca9c741bf79a3 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -65,6 +65,9 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } mod grandpa { @@ -73,6 +76,7 @@ mod grandpa { impl_outer_event!{ pub enum TestEvent for Test { + system, grandpa, } } diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index 7962c2a1c62bfe539bb5f50bcf93cef23d5689b7..59e8f721c8dd2cb2b65197ec812ce7b09614188b 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-identity" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/identity/src/benchmarking.rs b/frame/identity/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..11e98101ec57d146741df905c3fefe0acb062085 --- /dev/null +++ b/frame/identity/src/benchmarking.rs @@ -0,0 +1,592 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Identity pallet benchmarking. + +use super::*; + +use frame_system::RawOrigin; +use sp_io::hashing::blake2_256; +use sp_runtime::{BenchmarkResults, BenchmarkParameter, selected_benchmark}; +use sp_runtime::traits::{Bounded, Benchmarking, BenchmarkingSetup, Dispatchable}; + +use crate::Module as Identity; + +// The maximum number of identity registrars we will test. +const MAX_REGISTRARS: u32 = 50; + +// Support Functions +fn account(name: &'static str, index: u32) -> T::AccountId { + let entropy = (name, index).using_encoded(blake2_256); + T::AccountId::decode(&mut &entropy[..]).unwrap_or_default() +} + +// Adds `r` registrars to the Identity Pallet. These registrars will have set fees and fields. +fn add_registrars(r: u32) -> Result<(), &'static str> { + for i in 0..r { + let _ = T::Currency::make_free_balance_be(&account::("registrar", i), BalanceOf::::max_value()); + Identity::::add_registrar(RawOrigin::Root.into(), account::("registrar", i))?; + Identity::::set_fee(RawOrigin::Signed(account::("registrar", i)).into(), i.into(), 10.into())?; + let fields = IdentityFields( + IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot + | IdentityField::Email | IdentityField::PgpFingerprint | IdentityField::Image | IdentityField::Twitter + ); + Identity::::set_fields(RawOrigin::Signed(account::("registrar", i)).into(), i.into(), fields)?; + } + + assert_eq!(Registrars::::get().len(), r as usize); + Ok(()) +} + +// Adds `s` sub-accounts to the identity of `who`. Each wil have 32 bytes of raw data added to it. +// This additionally returns the vector of sub-accounts to it can be modified if needed. +fn add_sub_accounts(who: T::AccountId, s: u32) -> Result, &'static str> { + let mut subs = Vec::new(); + let who_origin = RawOrigin::Signed(who.clone()); + let data = Data::Raw(vec![0; 32]); + + for i in 0..s { + let sub_account = account::("sub", i); + subs.push((sub_account, data.clone())); + } + Identity::::set_subs(who_origin.into(), subs.clone())?; + + return Ok(subs) +} + +// This creates an `IdentityInfo` object with `num_fields` extra fields. +// All data is pre-populated with some arbitrary bytes. +fn create_identity_info(num_fields: u32) -> IdentityInfo { + let data = Data::Raw(vec![0; 32]); + + let info = IdentityInfo { + additional: vec![(data.clone(), data.clone()); num_fields as usize], + display: data.clone(), + legal: data.clone(), + web: data.clone(), + riot: data.clone(), + email: data.clone(), + pgp_fingerprint: Some([0; 20]), + image: data.clone(), + twitter: data.clone(), + }; + + return info +} + +// Benchmark `add_registrar` extrinsic. +struct AddRegistrar; +impl BenchmarkingSetup, RawOrigin> for AddRegistrar { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Add r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Return the `add_registrar` r + 1 call + Ok((crate::Call::::add_registrar(account::("registrar", r + 1)), RawOrigin::Root)) + } +} + +// Benchmark `set_identity` extrinsic. +struct SetIdentity; +impl BenchmarkingSetup, RawOrigin> for SetIdentity { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + // Additional Field Count + (BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()) + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Add r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // The target user + let caller = account::("caller", r); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Add an initial identity + let initial_info = benchmarking::create_identity_info::(1); + Identity::::set_identity(caller_origin.clone(), initial_info)?; + + // User requests judgement from all the registrars, and they approve + for i in 0..r { + Identity::::request_judgement(caller_origin.clone(), i, 10.into())?; + Identity::::provide_judgement( + RawOrigin::Signed(account::("registrar", i)).into(), + i, + caller_lookup.clone(), + Judgement::Reasonable + )?; + } + + // Create identity info with x additional fields + let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; + // 32 byte data that we reuse below + let info = benchmarking::create_identity_info::(x); + + // Return the `set_identity` call + Ok((crate::Call::::set_identity(info), RawOrigin::Signed(caller))) + } +} + +// Benchmark `set_subs` extrinsic. +struct SetSubs; +impl BenchmarkingSetup, RawOrigin> for SetSubs { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Subs Count + (BenchmarkParameter::S, 1, T::MaxSubAccounts::get()), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Generic data to be used. + let data = Data::Raw(vec![0; 32]); + + // The target user + let caller = account::("caller", 0); + let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Create their main identity + let info = benchmarking::create_identity_info::(1); + Identity::::set_identity(caller_origin.clone(), info)?; + + // Give them s many sub accounts + let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1; + let mut subs = add_sub_accounts::(caller.clone(), s)?; + + // Create an s+1 sub account to add + subs.push((account::("sub", s+1), data)); + + // Return the `set_subs` call + Ok((crate::Call::::set_subs(subs), RawOrigin::Signed(caller))) + } +} + +// Benchmark `clear_identity` extrinsic. +struct ClearIdentity; +impl BenchmarkingSetup, RawOrigin> for ClearIdentity { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + // Subs Count + (BenchmarkParameter::S, 1, T::MaxSubAccounts::get()), + // Additional Field Count + (BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Create their main identity with x additional fields + let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; + let info = benchmarking::create_identity_info::(x); + Identity::::set_identity(caller_origin.clone(), info)?; + + // Give them s many sub accounts + let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1; + let _ = benchmarking::add_sub_accounts::(caller.clone(), s)?; + + // User requests judgement from all the registrars, and they approve + for i in 0..r { + Identity::::request_judgement(caller_origin.clone(), i, 10.into())?; + Identity::::provide_judgement( + RawOrigin::Signed(account::("registrar", i)).into(), + i, + caller_lookup.clone(), + Judgement::Reasonable + )?; + } + + // Return the `clear_identity` call + Ok((crate::Call::::clear_identity(), RawOrigin::Signed(caller))) + } +} + +// Benchmark `request_judgement` extrinsic. +struct RequestJudgement; +impl BenchmarkingSetup, RawOrigin> for RequestJudgement { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + // Additional Field Count + (BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Create their main identity with x additional fields + let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; + let info = benchmarking::create_identity_info::(x); + Identity::::set_identity(caller_origin.clone(), info)?; + + // Return the `request_judgement` call + Ok((crate::Call::::request_judgement(r-1, 10.into()), RawOrigin::Signed(caller))) + } +} + +// Benchmark `cancel_request` extrinsic. +struct CancelRequest; +impl BenchmarkingSetup, RawOrigin> for CancelRequest { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + // Additional Field Count + (BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Create their main identity with x additional fields + let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; + let info = benchmarking::create_identity_info::(x); + Identity::::set_identity(caller_origin.clone(), info)?; + + // Request judgement + Identity::::request_judgement(caller_origin.clone(), r-1, 10.into())?; + + Ok((crate::Call::::cancel_request(r-1), RawOrigin::Signed(caller))) + } +} + +// Benchmark `set_fee` extrinsic. +struct SetFee; +impl BenchmarkingSetup, RawOrigin> for SetFee { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Add caller as registrar + Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; + + // Return `set_fee` call + Ok((crate::Call::::set_fee(r, 10.into()), RawOrigin::Signed(caller))) + } +} + +// Benchmark `set_account_id` extrinsic. +struct SetAccountId; +impl BenchmarkingSetup, RawOrigin> for SetAccountId { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Add caller as registrar + Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; + + // Return `set_account_id` call + Ok((crate::Call::::set_account_id(r, account::("new", 0)), RawOrigin::Signed(caller))) + } +} + +// Benchmark `set_fields` extrinsic. +struct SetFields; +impl BenchmarkingSetup, RawOrigin> for SetFields { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Add caller as registrar + Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; + + let fields = IdentityFields( + IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot + | IdentityField::Email | IdentityField::PgpFingerprint | IdentityField::Image | IdentityField::Twitter + ); + + // Return `set_account_id` call + Ok((crate::Call::::set_fields(r, fields), RawOrigin::Signed(caller))) + } +} + +// Benchmark `provide_judgement` extrinsic.g +struct ProvideJudgement; +impl BenchmarkingSetup, RawOrigin> for ProvideJudgement { + + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + // Additional Field Count + (BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // Add r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // The user + let user = account::("user", r); + let user_origin: ::Origin = RawOrigin::Signed(user.clone()).into(); + let user_lookup: ::Source = T::Lookup::unlookup(user.clone()); + let _ = T::Currency::make_free_balance_be(&user, BalanceOf::::max_value()); + + // Create their main identity with x additional fields + let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; + let info = benchmarking::create_identity_info::(x); + Identity::::set_identity(user_origin.clone(), info)?; + + // The caller registrar + let caller = account::("caller", r); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Add caller as registrar + Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; + + // User requests judgement from caller registrar + Identity::::request_judgement(user_origin.clone(), r, 10.into())?; + + // Return `provide_judgement` call + Ok((crate::Call::::provide_judgement( + r, + user_lookup.clone(), + Judgement::Reasonable + ), RawOrigin::Signed(caller))) + } +} + +// Benchmark `kill_identity` extrinsic. +struct KillIdentity; +impl BenchmarkingSetup, RawOrigin> for KillIdentity { + + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Registrar Count + (BenchmarkParameter::R, 1, MAX_REGISTRARS), + // Subs Count + (BenchmarkParameter::S, 1, T::MaxSubAccounts::get()), + // Additional Field Count + (BenchmarkParameter::X, 1, T::MaxAdditionalFields::get()), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(crate::Call, RawOrigin), &'static str> + { + // The target user + let caller = account::("caller", 0); + let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); + let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); + let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + // Register r registrars + let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; + benchmarking::add_registrars::(r)?; + + // Create their main identity with x additional fields + let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; + let info = benchmarking::create_identity_info::(x); + Identity::::set_identity(caller_origin.clone(), info)?; + + // Give them s many sub accounts + let s = components.iter().find(|&c| c.0 == BenchmarkParameter::S).unwrap().1; + let _ = benchmarking::add_sub_accounts::(caller.clone(), s)?; + + // User requests judgement from all the registrars, and they approve + for i in 0..r { + Identity::::request_judgement(caller_origin.clone(), i, 10.into())?; + Identity::::provide_judgement( + RawOrigin::Signed(account::("registrar", i)).into(), + i, + caller_lookup.clone(), + Judgement::Reasonable + )?; + } + + // Return the `kill_identity` call + Ok((crate::Call::::kill_identity(caller_lookup), RawOrigin::Root)) + } +} + +// The list of available benchmarks for this pallet. +selected_benchmark!( + AddRegistrar, + SetIdentity, + SetSubs, + ClearIdentity, + RequestJudgement, + CancelRequest, + SetFee, + SetAccountId, + SetFields, + ProvideJudgement, + KillIdentity +); + +impl Benchmarking for Module { + fn run_benchmark(extrinsic: Vec, steps: u32, repeat: u32) -> Result, &'static str> { + // Map the input to the selected benchmark. + let selected_benchmark = match extrinsic.as_slice() { + b"add_registrar" => SelectedBenchmark::AddRegistrar, + b"set_identity" => SelectedBenchmark::SetIdentity, + b"set_subs" => SelectedBenchmark::SetSubs, + b"clear_identity" => SelectedBenchmark::ClearIdentity, + b"request_judgement" => SelectedBenchmark::RequestJudgement, + b"cancel_request" => SelectedBenchmark::CancelRequest, + b"set_fee" => SelectedBenchmark::SetFee, + b"set_account_id" => SelectedBenchmark::SetAccountId, + b"set_fields" => SelectedBenchmark::SetFields, + b"provide_judgement" => SelectedBenchmark::ProvideJudgement, + b"kill_identity" => SelectedBenchmark::KillIdentity, + _ => return Err("Could not find extrinsic."), + }; + + // Warm up the DB + sp_io::benchmarking::commit_db(); + sp_io::benchmarking::wipe_db(); + + // first one is set_identity. + let components = , RawOrigin>>::components(&selected_benchmark); + // results go here + let mut results: Vec = Vec::new(); + // Select the component we will be benchmarking. Each component will be benchmarked. + for (name, low, high) in components.iter() { + // Create up to `STEPS` steps for that component between high and low. + let step_size = ((high - low) / steps).max(1); + let num_of_steps = (high - low) / step_size; + for s in 0..num_of_steps { + // This is the value we will be testing for component `name` + let component_value = low + step_size * s; + + // Select the mid value for all the other components. + let c: Vec<(BenchmarkParameter, u32)> = components.iter() + .map(|(n, l, h)| + (*n, if n == name { component_value } else { (h - l) / 2 + l }) + ).collect(); + + // Run the benchmark `repeat` times. + for _ in 0..repeat { + // Set up the externalities environment for the setup we want to benchmark. + let (call, caller) = , RawOrigin>>::instance(&selected_benchmark, &c)?; + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + sp_io::benchmarking::commit_db(); + // Run the benchmark. + let start = sp_io::benchmarking::current_time(); + call.dispatch(caller.into())?; + let finish = sp_io::benchmarking::current_time(); + let elapsed = finish - start; + results.push((c.clone(), elapsed)); + // Wipe the DB back to the genesis state. + sp_io::benchmarking::wipe_db(); + } + } + } + return Ok(results); + } +} diff --git a/frame/identity/src/lib.rs b/frame/identity/src/lib.rs index b23407406b6a46bea45f8f6a36f14314cc5a422a..5470e3d4b08314c0b533be95a8dfbc4117473141 100644 --- a/frame/identity/src/lib.rs +++ b/frame/identity/src/lib.rs @@ -73,11 +73,13 @@ use sp_runtime::{DispatchResult, RuntimeDebug}; use sp_runtime::traits::{StaticLookup, EnsureOrigin, Zero, AppendZerosInput}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, - traits::{Currency, ReservableCurrency, OnUnbalanced, Get}, + traits::{Currency, ReservableCurrency, OnUnbalanced, Get, BalanceStatus}, weights::SimpleDispatchInfo, }; use frame_system::{self as system, ensure_signed, ensure_root}; +pub mod benchmarking; + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; @@ -100,7 +102,11 @@ pub trait Trait: frame_system::Trait { type SubAccountDeposit: Get>; /// The maximum number of sub-accounts allowed per identified account. - type MaximumSubAccounts: Get; + type MaxSubAccounts: Get; + + /// Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O + /// required to access an identity, but can be pretty high. + type MaxAdditionalFields: Get; /// What to do with slashed funds. type Slashed: OnUnbalanced>; @@ -377,16 +383,19 @@ pub struct RegistrarInfo< decl_storage! { trait Store for Module as Sudo { /// Information that is pertinent to identify the entity behind an account. - pub IdentityOf get(fn identity): map T::AccountId => Option>>; + pub IdentityOf get(fn identity): + map hasher(blake2_256) T::AccountId => Option>>; /// The super-identity of an alternative "sub" identity together with its name, within that /// context. If the account is not some other account's sub-identity, then just `None`. - pub SuperOf get(fn super_of): map T::AccountId => Option<(T::AccountId, Data)>; + pub SuperOf get(fn super_of): + map hasher(blake2_256) T::AccountId => Option<(T::AccountId, Data)>; /// Alternative "sub" identities of this account. /// /// The first item is the deposit, the second is a vector of the accounts. - pub SubsOf get(fn subs): map T::AccountId => (BalanceOf, Vec); + pub SubsOf get(fn subs): + map hasher(blake2_256) T::AccountId => (BalanceOf, Vec); /// The set of registrars. Not expected to get very big as can only be added through a /// special origin (likely a council motion). @@ -440,7 +449,9 @@ decl_error! { InvalidIndex, /// The target is invalid. InvalidTarget, - } + /// Too many additional fields. + TooManyFields, +} } decl_module! { @@ -490,15 +501,17 @@ decl_module! { /// Emits `IdentitySet` if successful. /// /// # - /// - `O(X + R)` where `X` additional-field-count (deposit-bounded). + /// - `O(X + X' + R)` where `X` additional-field-count (deposit-bounded and code-bounded). /// - At most two balance operations. - /// - One storage mutation (codec `O(X + R)`). + /// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`). /// - One event. /// # #[weight = SimpleDispatchInfo::FixedNormal(50_000)] fn set_identity(origin, info: IdentityInfo) { let sender = ensure_signed(origin)?; - let fd = >::from(info.additional.len() as u32) * T::FieldDeposit::get(); + let extra_fields = info.additional.len() as u32; + ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::::TooManyFields); + let fd = >::from(extra_fields) * T::FieldDeposit::get(); let mut id = match >::get(&sender) { Some(mut id) => { @@ -542,8 +555,8 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(50_000)] fn set_subs(origin, subs: Vec<(T::AccountId, Data)>) { let sender = ensure_signed(origin)?; - ensure!(>::exists(&sender), Error::::NotFound); - ensure!(subs.len() <= T::MaximumSubAccounts::get() as usize, Error::::TooManySubAccounts); + ensure!(>::contains_key(&sender), Error::::NotFound); + ensure!(subs.len() <= T::MaxSubAccounts::get() as usize, Error::::TooManySubAccounts); let (old_deposit, old_ids) = >::get(&sender); let new_deposit = T::SubAccountDeposit::get() * >::from(subs.len() as u32); @@ -810,7 +823,7 @@ decl_module! { match id.judgements.binary_search_by_key(®_index, |x| x.0) { Ok(position) => { if let Judgement::FeePaid(fee) = id.judgements[position].1 { - let _ = T::Currency::repatriate_reserved(&target, &sender, fee); + let _ = T::Currency::repatriate_reserved(&target, &sender, fee, BalanceStatus::Free); } id.judgements[position] = item } @@ -839,7 +852,7 @@ decl_module! { /// - `S + 2` storage mutations. /// - One event. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(100_000)] fn kill_identity(origin, target: ::Source) { T::ForceOrigin::try_origin(origin) .map(|_| ()) @@ -869,7 +882,7 @@ mod tests { use sp_runtime::traits::BadOrigin; use frame_support::{ assert_ok, assert_noop, impl_outer_origin, parameter_types, weights::Weight, - ord_parameter_types + ord_parameter_types, }; use sp_core::H256; use frame_system::EnsureSignedBy; @@ -911,29 +924,26 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const BasicDeposit: u64 = 10; pub const FieldDeposit: u64 = 10; pub const SubAccountDeposit: u64 = 10; - pub const MaximumSubAccounts: u32 = 2; + pub const MaxSubAccounts: u32 = 2; + pub const MaxAdditionalFields: u32 = 2; } ord_parameter_types! { pub const One: u64 = 1; @@ -946,7 +956,8 @@ mod tests { type BasicDeposit = BasicDeposit; type FieldDeposit = FieldDeposit; type SubAccountDeposit = SubAccountDeposit; - type MaximumSubAccounts = MaximumSubAccounts; + type MaxSubAccounts = MaxSubAccounts; + type MaxAdditionalFields = MaxAdditionalFields; type RegistrarOrigin = EnsureSignedBy; type ForceOrigin = EnsureSignedBy; } @@ -968,7 +979,6 @@ mod tests { (20, 100), (30, 100), ], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); t.into() } @@ -1009,6 +1019,14 @@ mod tests { new_test_ext().execute_with(|| { assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); + let mut three_fields = ten(); + three_fields.additional.push(Default::default()); + three_fields.additional.push(Default::default()); + three_fields.additional.push(Default::default()); + assert_noop!( + Identity::set_identity(Origin::signed(10), three_fields), + Error::::TooManyFields + ); assert_ok!(Identity::set_identity(Origin::signed(10), ten())); assert_eq!(Identity::identity(10).unwrap().info, ten()); assert_eq!(Balances::free_balance(10), 90); diff --git a/frame/im-online/Cargo.toml b/frame/im-online/Cargo.toml index 5045509c7723aa52fb5a5ba2361ca1aaf54d8956..46bff2dc3588f3a56f9c6d14ca56ee5c9f8ec5a6 100644 --- a/frame/im-online/Cargo.toml +++ b/frame/im-online/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-im-online" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 065ca7e3bfc16375c58a8711c07fed9d1e714060..839996da322743182b206a0060c84394082bbcff 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -72,13 +72,14 @@ mod tests; use sp_application_crypto::RuntimeAppPublic; use codec::{Encode, Decode}; -use sp_core::offchain::{OpaqueNetworkState, StorageKind}; +use sp_core::offchain::OpaqueNetworkState; use sp_std::prelude::*; use sp_std::convert::TryInto; use pallet_session::historical::IdentificationTuple; use sp_runtime::{ + offchain::storage::StorageValueRef, RuntimeDebug, - traits::{Convert, Member, Printable, Saturating}, Perbill, + traits::{Convert, Member, Saturating, AtLeast32Bit}, Perbill, PerThing, transaction_validity::{ TransactionValidity, ValidTransaction, InvalidTransaction, TransactionPriority, @@ -89,7 +90,7 @@ use sp_staking::{ offence::{ReportOffence, Offence, Kind}, }; use frame_support::{ - decl_module, decl_event, decl_storage, print, Parameter, debug, decl_error, + decl_module, decl_event, decl_storage, Parameter, debug, decl_error, traits::Get, }; use frame_system::{self as system, ensure_none}; @@ -129,37 +130,70 @@ pub mod ed25519 { pub type AuthorityId = app_ed25519::Public; } -/// The local storage database key under which the worker progress status -/// is tracked. -const DB_KEY: &[u8] = b"parity/im-online-worker-status"; +const DB_PREFIX: &[u8] = b"parity/im-online-heartbeat/"; +/// How many blocks do we wait for heartbeat transaction to be included +/// before sending another one. +const INCLUDE_THRESHOLD: u32 = 3; -/// It's important to persist the worker state, since e.g. the -/// server could be restarted while starting the gossip process, but before -/// finishing it. With every execution of the off-chain worker we check -/// if we need to recover and resume gossipping or if there is already -/// another off-chain worker in the process of gossipping. +/// Status of the offchain worker code. +/// +/// This stores the block number at which heartbeat was requested and when the worker +/// has actually managed to produce it. +/// Note we store such status for every `authority_index` separately. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -struct WorkerStatus { - done: bool, - gossipping_at: BlockNumber, +struct HeartbeatStatus { + /// An index of the session that we are supposed to send heartbeat for. + pub session_index: SessionIndex, + /// A block number at which the heartbeat for that session has been actually sent. + /// + /// It may be 0 in case the sending failed. In such case we should just retry + /// as soon as possible (i.e. in a worker running for the next block). + pub sent_at: BlockNumber, +} + +impl HeartbeatStatus { + /// Returns true if heartbeat has been recently sent. + /// + /// Parameters: + /// `session_index` - index of current session. + /// `now` - block at which the offchain worker is running. + /// + /// This function will return `true` iff: + /// 1. the session index is the same (we don't care if it went up or down) + /// 2. the heartbeat has been sent recently (within the threshold) + /// + /// The reasoning for 1. is that it's better to send an extra heartbeat than + /// to stall or not send one in case of a bug. + fn is_recent(&self, session_index: SessionIndex, now: BlockNumber) -> bool { + self.session_index == session_index && self.sent_at + INCLUDE_THRESHOLD.into() > now + } } /// Error which may occur while executing the off-chain code. -#[derive(RuntimeDebug)] -enum OffchainErr { - DecodeWorkerStatus, +#[cfg_attr(test, derive(PartialEq))] +enum OffchainErr { + TooEarly(BlockNumber), + WaitingForInclusion(BlockNumber), + AlreadyOnline(u32), FailedSigning, + FailedToAcquireLock, NetworkState, SubmitTransaction, } -impl Printable for OffchainErr { - fn print(&self) { - match self { - OffchainErr::DecodeWorkerStatus => print("Offchain error: decoding WorkerStatus failed!"), - OffchainErr::FailedSigning => print("Offchain error: signing failed!"), - OffchainErr::NetworkState => print("Offchain error: fetching network state failed!"), - OffchainErr::SubmitTransaction => print("Offchain error: submitting transaction failed!"), +impl sp_std::fmt::Debug for OffchainErr { + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + match *self { + OffchainErr::TooEarly(ref block) => + write!(fmt, "Too early to send heartbeat, next expected at {:?}", block), + OffchainErr::WaitingForInclusion(ref block) => + write!(fmt, "Heartbeat already sent at {:?}. Waiting for inclusion.", block), + OffchainErr::AlreadyOnline(auth_idx) => + write!(fmt, "Authority {} is already online", auth_idx), + OffchainErr::FailedSigning => write!(fmt, "Failed to sign heartbeat"), + OffchainErr::FailedToAcquireLock => write!(fmt, "Failed to acquire lock"), + OffchainErr::NetworkState => write!(fmt, "Failed to fetch network state"), + OffchainErr::SubmitTransaction => write!(fmt, "Failed to submit transaction"), } } } @@ -197,7 +231,9 @@ pub trait Trait: frame_system::Trait + pallet_session::historical::Trait { /// An expected duration of the session. /// /// This parameter is used to determine the longevity of `heartbeat` transaction - /// and a rough time when the heartbeat should be sent. + /// and a rough time when we should start considering sending hearbeats, + /// since the workers avoids sending them at the very beginning of the session, assuming + /// there is a chance the authority will produce a block and they won't be necessary. type SessionDuration: Get; /// A type that gives us the ability to submit unresponsiveness offence reports. @@ -225,20 +261,27 @@ decl_event!( decl_storage! { trait Store for Module as ImOnline { - /// The block number when we should gossip. - GossipAt get(fn gossip_at): T::BlockNumber; + /// The block number after which it's ok to send heartbeats in current session. + /// + /// At the beginning of each session we set this to a value that should + /// fall roughly in the middle of the session duration. + /// The idea is to first wait for the validators to produce a block + /// in the current session, so that the heartbeat later on will not be necessary. + HeartbeatAfter get(fn heartbeat_after): T::BlockNumber; /// The current set of keys that may issue a heartbeat. Keys get(fn keys): Vec; /// For each session index, we keep a mapping of `AuthIndex` /// to `offchain::OpaqueNetworkState`. - ReceivedHeartbeats get(fn received_heartbeats): double_map SessionIndex, AuthIndex + ReceivedHeartbeats get(fn received_heartbeats): + double_map hasher(blake2_256) SessionIndex, hasher(blake2_256) AuthIndex => Option>; /// For each session index, we keep a mapping of `T::ValidatorId` to the /// number of blocks authored by the given authority. - AuthoredBlocks get(fn authored_blocks): double_map SessionIndex, T::ValidatorId => u32; + AuthoredBlocks get(fn authored_blocks): + double_map hasher(blake2_256) SessionIndex, hasher(blake2_256) T::ValidatorId => u32; } add_extra_genesis { config(keys): Vec; @@ -272,7 +315,7 @@ decl_module! { ensure_none(origin)?; let current_session = >::current_index(); - let exists = ::exists( + let exists = ::contains_key( ¤t_session, &heartbeat.authority_index ); @@ -300,12 +343,29 @@ decl_module! { // Only send messages if we are a potential validator. if sp_io::offchain::is_validator() { - Self::offchain(now); + for res in Self::send_heartbeats(now).into_iter().flatten() { + if let Err(e) = res { + debug::debug!( + target: "imonline", + "Skipping heartbeat at {:?}: {:?}", + now, + e, + ) + } + } + } else { + debug::trace!( + target: "imonline", + "Skipping heartbeat at {:?}. Not a validator.", + now, + ) } } } } +type OffchainResult = Result::BlockNumber>>; + /// Keep track of number of authored blocks per authority, uncles are counted as /// well since they're a valid proof of onlineness. impl pallet_authorship::EventHandler for Module { @@ -338,7 +398,7 @@ impl Module { fn is_online_aux(authority_index: AuthIndex, authority: &T::ValidatorId) -> bool { let current_session = >::current_index(); - ::exists(¤t_session, &authority_index) || + ::contains_key(¤t_session, &authority_index) || >::get( ¤t_session, authority, @@ -349,7 +409,7 @@ impl Module { /// the authorities series, during the current session. Otherwise `false`. pub fn received_heartbeat_in_current_session(authority_index: AuthIndex) -> bool { let current_session = >::current_index(); - ::exists(¤t_session, &authority_index) + ::contains_key(¤t_session, &authority_index) } /// Note that the given authority has authored a block in the current session. @@ -363,156 +423,128 @@ impl Module { ); } - pub(crate) fn offchain(now: T::BlockNumber) { - let next_gossip = >::get(); - let check = Self::check_not_yet_gossipped(now, next_gossip); - let (curr_worker_status, not_yet_gossipped) = match check { - Ok((s, v)) => (s, v), - Err(err) => { - print(err); - return; - }, - }; - if next_gossip < now && not_yet_gossipped { - let value_set = Self::compare_and_set_worker_status(now, false, curr_worker_status); - if !value_set { - // value could not be set in local storage, since the value was - // different from `curr_worker_status`. this indicates that - // another worker was running in parallel. - return; - } - - match Self::do_gossip_at(now) { - Ok(_) => {}, - Err(err) => print(err), - } - } else { - debug::native::debug!( - target: "imonline", - "Skipping gossip at: {:?} >= {:?} || {:?}", - next_gossip, - now, - if not_yet_gossipped { "not gossipped" } else { "gossipped" } - ); + pub(crate) fn send_heartbeats(block_number: T::BlockNumber) + -> OffchainResult>> + { + let heartbeat_after = >::get(); + if block_number < heartbeat_after { + return Err(OffchainErr::TooEarly(heartbeat_after)) } + + let session_index = >::current_index(); + Ok(Self::local_authority_keys() + .map(move |(authority_index, key)| + Self::send_single_heartbeat(authority_index, key, session_index, block_number) + )) } - fn do_gossip_at(block_number: T::BlockNumber) -> Result<(), OffchainErr> { - // we run only when a local authority key is configured - let authorities = Keys::::get(); - let mut results = Vec::new(); - let mut local_keys = T::AuthorityId::all(); - local_keys.sort(); - - for (authority_index, key) in authorities.into_iter() - .enumerate() - .filter_map(|(index, authority)| { - local_keys.binary_search(&authority) - .ok() - .map(|location| (index as u32, &local_keys[location])) - }) - { - if Self::is_online(authority_index) { - debug::native::info!( - target: "imonline", - "[index: {:?}] Skipping sending heartbeat at block: {:?}. Already online.", - authority_index, - block_number - ); - continue; - } + fn send_single_heartbeat( + authority_index: u32, + key: T::AuthorityId, + session_index: SessionIndex, + block_number: T::BlockNumber + ) -> OffchainResult { + // A helper function to prepare heartbeat call. + let prepare_heartbeat = || -> OffchainResult> { let network_state = sp_io::offchain::network_state() .map_err(|_| OffchainErr::NetworkState)?; let heartbeat_data = Heartbeat { block_number, network_state, - session_index: >::current_index(), + session_index, authority_index, }; - let signature = key.sign(&heartbeat_data.encode()).ok_or(OffchainErr::FailedSigning)?; - let call = Call::heartbeat(heartbeat_data, signature); - - debug::info!( - target: "imonline", - "[index: {:?}] Reporting im-online at block: {:?}", - authority_index, - block_number - ); + Ok(Call::heartbeat(heartbeat_data, signature)) + }; - results.push( - T::SubmitTransaction::submit_unsigned(call) - .map_err(|_| OffchainErr::SubmitTransaction) - ); + if Self::is_online(authority_index) { + return Err(OffchainErr::AlreadyOnline(authority_index)); } - // fail only after trying all keys. - results.into_iter().collect::, OffchainErr>>()?; - - // once finished we set the worker status without comparing - // if the existing value changed in the meantime. this is - // because at this point the heartbeat was definitely submitted. - Self::set_worker_status(block_number, true); + // acquire lock for that authority at current heartbeat to make sure we don't + // send concurrent heartbeats. + Self::with_heartbeat_lock( + authority_index, + session_index, + block_number, + || { + let call = prepare_heartbeat()?; + debug::info!( + target: "imonline", + "[index: {:?}] Reporting im-online at block: {:?} (session: {:?}): {:?}", + authority_index, + block_number, + session_index, + call, + ); - Ok(()) - } + T::SubmitTransaction::submit_unsigned(call) + .map_err(|_| OffchainErr::SubmitTransaction)?; - fn compare_and_set_worker_status( - gossipping_at: T::BlockNumber, - done: bool, - curr_worker_status: Option>, - ) -> bool { - let enc = WorkerStatus { - done, - gossipping_at, - }; - sp_io::offchain::local_storage_compare_and_set( - StorageKind::PERSISTENT, - DB_KEY, - curr_worker_status, - &enc.encode() + Ok(()) + }, ) } - fn set_worker_status( - gossipping_at: T::BlockNumber, - done: bool, - ) { - let enc = WorkerStatus { - done, - gossipping_at, - }; - sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, DB_KEY, &enc.encode()); + fn local_authority_keys() -> impl Iterator { + // we run only when a local authority key is configured + let authorities = Keys::::get(); + let mut local_keys = T::AuthorityId::all(); + local_keys.sort(); + + authorities.into_iter() + .enumerate() + .filter_map(move |(index, authority)| { + local_keys.binary_search(&authority) + .ok() + .map(|location| (index as u32, local_keys[location].clone())) + }) } - // Checks if a heartbeat gossip already occurred at this block number. - // Returns a tuple of `(current worker status, bool)`, whereby the bool - // is true if not yet gossipped. - fn check_not_yet_gossipped( + fn with_heartbeat_lock( + authority_index: u32, + session_index: SessionIndex, now: T::BlockNumber, - next_gossip: T::BlockNumber, - ) -> Result<(Option>, bool), OffchainErr> { - let last_gossip = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, DB_KEY); - match last_gossip { - Some(last) => { - let worker_status: WorkerStatus = Decode::decode(&mut &last[..]) - .map_err(|_| OffchainErr::DecodeWorkerStatus)?; - - let was_aborted = !worker_status.done && worker_status.gossipping_at < now; + f: impl FnOnce() -> OffchainResult, + ) -> OffchainResult { + let key = { + let mut key = DB_PREFIX.to_vec(); + key.extend(authority_index.encode()); + key + }; + let storage = StorageValueRef::persistent(&key); + let res = storage.mutate(|status: Option>>| { + // Check if there is already a lock for that particular block. + // This means that the heartbeat has already been sent, and we are just waiting + // for it to be included. However if it doesn't get included for INCLUDE_THRESHOLD + // we will re-send it. + match status { + // we are still waiting for inclusion. + Some(Some(status)) if status.is_recent(session_index, now) => { + Err(OffchainErr::WaitingForInclusion(status.sent_at)) + }, + // attempt to set new status + _ => Ok(HeartbeatStatus { + session_index, + sent_at: now, + }), + } + })?; - // another off-chain worker is currently in the process of submitting - let already_submitting = - !worker_status.done && worker_status.gossipping_at == now; + let mut new_status = res.map_err(|_| OffchainErr::FailedToAcquireLock)?; - let not_yet_gossipped = - worker_status.done && worker_status.gossipping_at < next_gossip; + // we got the lock, let's try to send the heartbeat. + let res = f(); - let ret = (was_aborted && !already_submitting) || not_yet_gossipped; - Ok((Some(last), ret)) - }, - None => Ok((None, true)), + // clear the lock in case we have failed to send transaction. + if res.is_err() { + new_status.sent_at = 0.into(); + storage.set(&new_status); } + + res } fn initialize_keys(keys: &[T::AuthorityId]) { @@ -542,10 +574,10 @@ impl pallet_session::OneSessionHandler for Module { { // Tell the offchain worker to start making the next session's heartbeats. // Since we consider producing blocks as being online, - // the hearbeat is defered a bit to prevent spaming. + // the heartbeat is defered a bit to prevent spaming. let block_number = >::block_number(); let half_session = T::SessionDuration::get() / 2.into(); - >::put(block_number + half_session); + >::put(block_number + half_session); // Remember who the authorities are for the new session. Keys::::put(validators.map(|x| x.1).collect::>()); diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 5c428c38582ff0f65da03283ffc0dbb6874224eb..7ee4c89ab46d3956154848add0b07b5ab5ae92c5 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -115,6 +115,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 34eac233ae317e2767ce934d4167341bf815c020..adc126094b612752c028767c276e4d0e3499def8 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -210,7 +210,11 @@ fn should_generate_heartbeats() { // when UintAuthorityId::set_all_keys(vec![0, 1, 2]); - ImOnline::offchain(2); + ImOnline::send_heartbeats(2) + .unwrap() + // make sure to consume the iterator and check there are no errors. + .collect::, _>>().unwrap(); + // then let transaction = state.write().transactions.pop().unwrap(); @@ -315,7 +319,12 @@ fn should_not_send_a_report_if_already_online() { // when UintAuthorityId::set_all_keys(vec![0]); // all authorities use pallet_session key 0 - ImOnline::offchain(4); + // we expect error, since the authority is already online. + let mut res = ImOnline::send_heartbeats(4).unwrap(); + assert_eq!(res.next().unwrap().unwrap(), ()); + assert_eq!(res.next().unwrap().unwrap_err(), OffchainErr::AlreadyOnline(1)); + assert_eq!(res.next().unwrap().unwrap_err(), OffchainErr::AlreadyOnline(2)); + assert_eq!(res.next(), None); // then let transaction = pool_state.write().transactions.pop().unwrap(); diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml index bd83761ff1a25a208df8a7483f9d60808db8db7a..eb5298dcfa569fa691121002fd5d736b34816f89 100644 --- a/frame/indices/Cargo.toml +++ b/frame/indices/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-indices" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } @@ -15,6 +16,9 @@ sp-core = { version = "2.0.0", default-features = false, path = "../../primitive frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } +[dev-dependencies] +pallet-balances = { version = "2.0.0", path = "../balances" } + [features] default = ["std"] std = [ diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index 8d7e37d5dfbe86d6dde7f90568cde170ef8fafd9..ad1a7f300fdb8d38149b39f703fbe70b113d9d63 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -19,61 +19,53 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; -use codec::{Encode, Codec}; -use frame_support::{Parameter, decl_module, decl_event, decl_storage}; -use sp_runtime::traits::{One, SimpleArithmetic, StaticLookup, Member, LookupError}; -use frame_system::{IsDeadAccount, OnNewAccount}; - +use sp_std::prelude::*; +use codec::Codec; +use sp_runtime::traits::{ + StaticLookup, Member, LookupError, Zero, One, BlakeTwo256, Hash, Saturating, AtLeast32Bit +}; +use frame_support::{Parameter, decl_module, decl_error, decl_event, decl_storage, ensure}; +use frame_support::dispatch::DispatchResult; +use frame_support::traits::{Currency, ReservableCurrency, Get, BalanceStatus::Reserved}; +use frame_support::storage::migration::take_storage_value; +use frame_system::{ensure_signed, ensure_root}; use self::address::Address as RawAddress; mod mock; - pub mod address; mod tests; -/// Number of account IDs stored per enum set. -const ENUM_SET_SIZE: u32 = 64; - pub type Address = RawAddress<::AccountId, ::AccountIndex>; - -/// Turn an Id into an Index, or None for the purpose of getting -/// a hint at a possibly desired index. -pub trait ResolveHint { - /// Turn an Id into an Index, or None for the purpose of getting - /// a hint at a possibly desired index. - fn resolve_hint(who: &AccountId) -> Option; -} - -/// Simple encode-based resolve hint implementation. -pub struct SimpleResolveHint(PhantomData<(AccountId, AccountIndex)>); -impl> - ResolveHint for SimpleResolveHint -{ - fn resolve_hint(who: &AccountId) -> Option { - Some(AccountIndex::from(who.using_encoded(|e| e[0] as u32 + e[1] as u32 * 256))) - } -} +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The module's config trait. pub trait Trait: frame_system::Trait { /// Type used for storing an account's index; implies the maximum number of accounts the system /// can hold. - type AccountIndex: Parameter + Member + Codec + Default + SimpleArithmetic + Copy; + type AccountIndex: Parameter + Member + Codec + Default + AtLeast32Bit + Copy; - /// Whether an account is dead or not. - type IsDeadAccount: IsDeadAccount; + /// The currency trait. + type Currency: ReservableCurrency; - /// How to turn an id into an index. - type ResolveHint: ResolveHint; + /// The deposit needed for reserving an index. + type Deposit: Get>; /// The overarching event type. type Event: From> + Into<::Event>; } -decl_module! { - pub struct Module for enum Call where origin: T::Origin, system = frame_system { - fn deposit_event() = default; +decl_storage! { + trait Store for Module as Indices { + /// The lookup from index to account. + pub Accounts build(|config: &GenesisConfig| + config.indices.iter() + .cloned() + .map(|(a, b)| (a, (b, Zero::zero()))) + .collect::>() + ): map hasher(blake2_128_concat) T::AccountIndex => Option<(T::AccountId, BalanceOf)>; + } + add_extra_genesis { + config(indices): Vec<(T::AccountIndex, T::AccountId)>; } } @@ -82,36 +74,146 @@ decl_event!( ::AccountId, ::AccountIndex { - /// A new account index was assigned. - /// - /// This event is not triggered when an existing index is reassigned - /// to another `AccountId`. - NewAccountIndex(AccountId, AccountIndex), + /// A account index was assigned. + IndexAssigned(AccountId, AccountIndex), + /// A account index has been freed up (unassigned). + IndexFreed(AccountIndex), } ); -decl_storage! { - trait Store for Module as Indices { - /// The next free enumeration set. - pub NextEnumSet get(fn next_enum_set) build(|config: &GenesisConfig| { - (config.ids.len() as u32 / ENUM_SET_SIZE).into() - }): T::AccountIndex; - - /// The enumeration sets. - pub EnumSet get(fn enum_set) build(|config: &GenesisConfig| { - (0..((config.ids.len() as u32) + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE) - .map(|i| ( - i.into(), - config.ids[ - (i * ENUM_SET_SIZE) as usize.. - config.ids.len().min(((i + 1) * ENUM_SET_SIZE) as usize) - ].to_owned(), - )) - .collect::>() - }): map T::AccountIndex => Vec; +decl_error! { + pub enum Error for Module { + /// The index was not already assigned. + NotAssigned, + /// The index is assigned to another account. + NotOwner, + /// The index was not available. + InUse, + /// The source and destination accounts are identical. + NotTransfer, } - add_extra_genesis { - config(ids): Vec; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin, system = frame_system { + fn deposit_event() = default; + + fn on_initialize() { + Self::migrations(); + } + + /// Assign an previously unassigned index. + /// + /// Payment: `Deposit` is reserved from the sender account. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `index`: the index to be claimed. This must not be in use. + /// + /// Emits `IndexAssigned` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - One reserve operation. + /// - One event. + /// # + fn claim(origin, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + + Accounts::::try_mutate(index, |maybe_value| { + ensure!(maybe_value.is_none(), Error::::InUse); + *maybe_value = Some((who.clone(), T::Deposit::get())); + T::Currency::reserve(&who, T::Deposit::get()) + })?; + Self::deposit_event(RawEvent::IndexAssigned(who, index)); + } + + /// Assign an index already owned by the sender to another account. The balance reservation + /// is effectively transfered to the new account. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `index`: the index to be re-assigned. This must be owned by the sender. + /// - `new`: the new owner of the index. This function is a no-op if it is equal to sender. + /// + /// Emits `IndexAssigned` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - One transfer operation. + /// - One event. + /// # + fn transfer(origin, new: T::AccountId, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + ensure!(who != new, Error::::NotTransfer); + + Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { + let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(&account == &who, Error::::NotOwner); + let lost = T::Currency::repatriate_reserved(&who, &new, amount, Reserved)?; + *maybe_value = Some((new.clone(), amount.saturating_sub(lost))); + Ok(()) + })?; + Self::deposit_event(RawEvent::IndexAssigned(new, index)); + } + + /// Free up an index owned by the sender. + /// + /// Payment: Any previous deposit placed for the index is unreserved in the sender account. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must own the index. + /// + /// - `index`: the index to be freed. This must be owned by the sender. + /// + /// Emits `IndexFreed` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - One reserve operation. + /// - One event. + /// # + fn free(origin, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + + Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { + let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(&account == &who, Error::::NotOwner); + T::Currency::unreserve(&who, amount); + Ok(()) + })?; + Self::deposit_event(RawEvent::IndexFreed(index)); + } + + /// Force an index to an account. This doesn't require a deposit. If the index is already + /// held, then any deposit is reimbursed to its current owner. + /// + /// The dispatch origin for this call must be _Root_. + /// + /// - `index`: the index to be (re-)assigned. + /// - `new`: the new owner of the index. This function is a no-op if it is equal to sender. + /// + /// Emits `IndexAssigned` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - Up to one reserve operation. + /// - One event. + /// # + fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex) { + ensure_root(origin)?; + + Accounts::::mutate(index, |maybe_value| { + if let Some((account, amount)) = maybe_value.take() { + T::Currency::unreserve(&account, amount); + } + *maybe_value = Some((new.clone(), Zero::zero())); + }); + Self::deposit_event(RawEvent::IndexAssigned(new, index)); + } } } @@ -120,22 +222,7 @@ impl Module { /// Lookup an T::AccountIndex to get an Id, if there's one there. pub fn lookup_index(index: T::AccountIndex) -> Option { - let enum_set_size = Self::enum_set_size(); - let set = Self::enum_set(index / enum_set_size); - let i: usize = (index % enum_set_size).try_into().ok()?; - set.get(i).cloned() - } - - /// `true` if the account `index` is ready for reclaim. - pub fn can_reclaim(try_index: T::AccountIndex) -> bool { - let enum_set_size = Self::enum_set_size(); - let try_set = Self::enum_set(try_index / enum_set_size); - let maybe_usize: Result = (try_index % enum_set_size).try_into(); - if let Ok(i) = maybe_usize { - i < try_set.len() && T::IsDeadAccount::is_dead_account(&try_set[i]) - } else { - false - } + Accounts::::get(index).map(|x| x.0) } /// Lookup an address to get an Id, if there's one there. @@ -148,76 +235,28 @@ impl Module { } } - // PUBLIC MUTABLES (DANGEROUS) - - fn enum_set_size() -> T::AccountIndex { - ENUM_SET_SIZE.into() - } -} - -impl OnNewAccount for Module { - // Implementation of the config type managing the creation of new accounts. - // See Balances module for a concrete example. - // - // # - // - Independent of the arguments. - // - Given the correct value of `Self::next_enum_set`, it always has a limited - // number of reads and writes and no complex computation. - // - // As for storage, calling this function with _non-dead-indices_ will linearly grow the length of - // of `Self::enum_set`. Appropriate economic incentives should exist to make callers of this - // function provide a `who` argument that reclaims a dead account. - // - // At the time of this writing, only the Balances module calls this function upon creation - // of new accounts. - // # - fn on_new_account(who: &T::AccountId) { - let enum_set_size = Self::enum_set_size(); - let next_set_index = Self::next_enum_set(); - - if let Some(try_index) = T::ResolveHint::resolve_hint(who) { - // then check to see if this account id identifies a dead account index. - let set_index = try_index / enum_set_size; - let mut try_set = Self::enum_set(set_index); - if let Ok(item_index) = (try_index % enum_set_size).try_into() { - if item_index < try_set.len() { - if T::IsDeadAccount::is_dead_account(&try_set[item_index]) { - // yup - this index refers to a dead account. can be reused. - try_set[item_index] = who.clone(); - >::insert(set_index, try_set); - - return + /// Do any migrations. + fn migrations() { + if let Some(set_count) = take_storage_value::(b"Indices", b"NextEnumSet", b"") { + // migrations need doing. + let set_size: T::AccountIndex = 64.into(); + + let mut set_index: T::AccountIndex = Zero::zero(); + while set_index < set_count { + let maybe_accounts = take_storage_value::>(b"Indices", b"EnumSet", BlakeTwo256::hash_of(&set_index).as_ref()); + if let Some(accounts) = maybe_accounts { + for (item_index, target) in accounts.into_iter().enumerate() { + if target != T::AccountId::default() && !T::Currency::total_balance(&target).is_zero() { + let index = set_index * set_size + T::AccountIndex::from(item_index as u32); + Accounts::::insert(index, (target, BalanceOf::::zero())); + } } + } else { + break; } + set_index += One::one(); } } - - // insert normally as a back up - let mut set_index = next_set_index; - // defensive only: this loop should never iterate since we keep NextEnumSet up to date - // later. - let mut set = loop { - let set = Self::enum_set(set_index); - if set.len() < ENUM_SET_SIZE as usize { - break set; - } - set_index += One::one(); - }; - - let index = set_index * enum_set_size + T::AccountIndex::from(set.len() as u32); - - // update set. - set.push(who.clone()); - - // keep NextEnumSet up to date - if set.len() == ENUM_SET_SIZE as usize { - >::put(set_index + One::one()); - } - - // write set. - >::insert(set_index, set); - - Self::deposit_event(RawEvent::NewAccountIndex(who.clone(), index)); } } diff --git a/frame/indices/src/mock.rs b/frame/indices/src/mock.rs index b8c9b0a0adfd8a387fb4e54a3b53d931446bc014..fe01b680bcc0187b9b53e550e8f056c47ae01fbb 100644 --- a/frame/indices/src/mock.rs +++ b/frame/indices/src/mock.rs @@ -18,51 +18,29 @@ #![cfg(test)] -use std::{cell::RefCell, collections::HashSet}; use sp_runtime::testing::Header; use sp_runtime::Perbill; use sp_core::H256; -use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; -use crate::{GenesisConfig, Module, Trait, IsDeadAccount, OnNewAccount, ResolveHint}; +use frame_support::{impl_outer_origin, impl_outer_event, parameter_types, weights::Weight}; +use crate::{self as indices, Module, Trait}; +use frame_system as system; +use pallet_balances as balances; impl_outer_origin!{ - pub enum Origin for Runtime where system = frame_system {} + pub enum Origin for Test where system = frame_system {} } - -thread_local! { - static ALIVE: RefCell> = Default::default(); -} - -pub fn make_account(who: u64) { - ALIVE.with(|a| a.borrow_mut().insert(who)); - Indices::on_new_account(&who); -} - -pub fn kill_account(who: u64) { - ALIVE.with(|a| a.borrow_mut().remove(&who)); -} - -pub struct TestIsDeadAccount {} -impl IsDeadAccount for TestIsDeadAccount { - fn is_dead_account(who: &u64) -> bool { - !ALIVE.with(|a| a.borrow_mut().contains(who)) - } -} - -pub struct TestResolveHint; -impl ResolveHint for TestResolveHint { - fn resolve_hint(who: &u64) -> Option { - if *who < 256 { - None - } else { - Some(*who - 256) - } +impl_outer_event!{ + pub enum MetaEvent for Test { + system, + balances, + indices, } } // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Runtime; +pub struct Test; + parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = 1024; @@ -70,46 +48,59 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } -impl frame_system::Trait for Runtime { +impl frame_system::Trait for Test { type Origin = Origin; + type Call = (); type Index = u64; type BlockNumber = u64; - type Call = (); type Hash = H256; type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = u64; type Lookup = Indices; type Header = Header; - type Event = (); + type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Trait for Test { + type Balance = u64; + type DustRemoval = (); + type Event = MetaEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; } -impl Trait for Runtime { +parameter_types! { + pub const Deposit: u64 = 1; +} + +impl Trait for Test { type AccountIndex = u64; - type IsDeadAccount = TestIsDeadAccount; - type ResolveHint = TestResolveHint; - type Event = (); + type Currency = Balances; + type Deposit = Deposit; + type Event = MetaEvent; } pub fn new_test_ext() -> sp_io::TestExternalities { - { - ALIVE.with(|a| { - let mut h = a.borrow_mut(); - h.clear(); - for i in 1..5 { h.insert(i); } - }); - } - - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - GenesisConfig:: { - ids: vec![1, 2, 3, 4] + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig::{ + balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], }.assimilate_storage(&mut t).unwrap(); t.into() } -pub type Indices = Module; +pub type System = frame_system::Module; +pub type Balances = pallet_balances::Module; +pub type Indices = Module; diff --git a/frame/indices/src/tests.rs b/frame/indices/src/tests.rs index 95afcef734c544879d27cc312195bd2d3d4141d9..9e434cfbe2a1c299ff2f16988c3b350af494d2fc 100644 --- a/frame/indices/src/tests.rs +++ b/frame/indices/src/tests.rs @@ -19,49 +19,85 @@ #![cfg(test)] use super::*; -use crate::mock::{Indices, new_test_ext, make_account, kill_account, TestIsDeadAccount}; +use super::mock::*; +use frame_support::{assert_ok, assert_noop}; +use pallet_balances::Error as BalancesError; #[test] -fn indexing_lookup_should_work() { +fn claiming_should_work() { new_test_ext().execute_with(|| { - assert_eq!(Indices::lookup_index(0), Some(1)); - assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(2), Some(3)); - assert_eq!(Indices::lookup_index(3), Some(4)); - assert_eq!(Indices::lookup_index(4), None); + assert_noop!(Indices::claim(Some(0).into(), 0), BalancesError::::InsufficientBalance); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_noop!(Indices::claim(Some(2).into(), 0), Error::::InUse); + assert_eq!(Balances::reserved_balance(1), 1); }); } #[test] -fn default_indexing_on_new_accounts_should_work() { +fn freeing_should_work() { new_test_ext().execute_with(|| { - assert_eq!(Indices::lookup_index(4), None); - make_account(5); - assert_eq!(Indices::lookup_index(4), Some(5)); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::claim(Some(2).into(), 1)); + assert_noop!(Indices::free(Some(0).into(), 0), Error::::NotOwner); + assert_noop!(Indices::free(Some(1).into(), 1), Error::::NotOwner); + assert_noop!(Indices::free(Some(1).into(), 2), Error::::NotAssigned); + assert_ok!(Indices::free(Some(1).into(), 0)); + assert_eq!(Balances::reserved_balance(1), 0); + assert_noop!(Indices::free(Some(1).into(), 0), Error::::NotAssigned); }); } #[test] -fn reclaim_indexing_on_new_accounts_should_work() { +fn indexing_lookup_should_work() { new_test_ext().execute_with(|| { + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::claim(Some(2).into(), 1)); + assert_eq!(Indices::lookup_index(0), Some(1)); assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(4), None); + assert_eq!(Indices::lookup_index(2), None); + }); +} - kill_account(2); // index 1 no longer locked to id 2 +#[test] +fn reclaim_index_on_accounts_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::free(Some(1).into(), 0)); + assert_ok!(Indices::claim(Some(2).into(), 0)); + assert_eq!(Indices::lookup_index(0), Some(2)); + assert_eq!(Balances::reserved_balance(2), 1); + }); +} - make_account(1 + 256); // id 257 takes index 1. - assert_eq!(Indices::lookup_index(1), Some(257)); +#[test] +fn transfer_index_on_accounts_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_noop!(Indices::transfer(Some(1).into(), 2, 1), Error::::NotAssigned); + assert_noop!(Indices::transfer(Some(2).into(), 3, 0), Error::::NotOwner); + assert_ok!(Indices::transfer(Some(1).into(), 3, 0)); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::reserved_balance(3), 1); + assert_eq!(Indices::lookup_index(0), Some(3)); }); } #[test] -fn alive_account_should_prevent_reclaim() { +fn force_transfer_index_on_preowned_should_work() { new_test_ext().execute_with(|| { - assert!(!TestIsDeadAccount::is_dead_account(&2)); - assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(4), None); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0)); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::reserved_balance(3), 0); + assert_eq!(Indices::lookup_index(0), Some(3)); + }); +} - make_account(1 + 256); // id 257 takes index 1. - assert_eq!(Indices::lookup_index(4), Some(257)); +#[test] +fn force_transfer_index_on_free_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0)); + assert_eq!(Balances::reserved_balance(3), 0); + assert_eq!(Indices::lookup_index(0), Some(3)); }); } diff --git a/frame/membership/Cargo.toml b/frame/membership/Cargo.toml index cc45cfb94cb49dd1fd50d8707da00d722f1d86c1..719718505e2e42beb2cbdd1a74d26de1ea0ed3b5 100644 --- a/frame/membership/Cargo.toml +++ b/frame/membership/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-membership" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 3880b6cbb8fd00b627c594082c86b7d70f7e4880..62d1315ee29881ecf03ec567150ab06309b294ca 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -269,6 +269,9 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } ord_parameter_types! { pub const One: u64 = 1; diff --git a/frame/metadata/Cargo.toml b/frame/metadata/Cargo.toml index bcb90d2368928185b85436597cd37c0e8ad765a3..e245f04849a40b9e1a9c1c1250fdf659c3fe8db7 100644 --- a/frame/metadata/Cargo.toml +++ b/frame/metadata/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "frame-metadata" -version = "10.0.0" +version = "11.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/frame/metadata/src/lib.rs b/frame/metadata/src/lib.rs index f6e280bed1e7927a2d5550de8f449f162c8c4510..a7410bbfbc3e4955659420aab4899d41a723c22c 100644 --- a/frame/metadata/src/lib.rs +++ b/frame/metadata/src/lib.rs @@ -316,11 +316,21 @@ pub struct StorageMetadata { pub entries: DecodeDifferent<&'static [StorageEntryMetadata], Vec>, } +/// Metadata prefixed by a u32 for reserved usage #[derive(Eq, Encode, PartialEq, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Decode, Serialize))] -/// Metadata prefixed by a u32 for reserved usage pub struct RuntimeMetadataPrefixed(pub u32, pub RuntimeMetadata); +/// Metadata of the extrinsic used by the runtime. +#[derive(Eq, Encode, PartialEq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] +pub struct ExtrinsicMetadata { + /// Extrinsic version. + pub version: u8, + /// The signed extensions in the order they appear in the extrinsic. + pub signed_extensions: Vec, +} + /// The metadata of a runtime. /// The version ID encoded/decoded through /// the enum nature of `RuntimeMetadata`. @@ -347,8 +357,10 @@ pub enum RuntimeMetadata { V8(RuntimeMetadataDeprecated), /// Version 9 for runtime metadata. No longer used. V9(RuntimeMetadataDeprecated), - /// Version 10 for runtime metadata. - V10(RuntimeMetadataV10), + /// Version 10 for runtime metadata. No longer used. + V10(RuntimeMetadataDeprecated), + /// Version 11 for runtime metadata. + V11(RuntimeMetadataV11), } /// Enum that should fail. @@ -372,12 +384,15 @@ impl Decode for RuntimeMetadataDeprecated { /// The metadata of a runtime. #[derive(Eq, Encode, PartialEq, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Decode, Serialize))] -pub struct RuntimeMetadataV10 { +pub struct RuntimeMetadataV11 { + /// Metadata of all the modules. pub modules: DecodeDifferentArray, + /// Metadata of the extrinsic. + pub extrinsic: ExtrinsicMetadata, } /// The latest version of the metadata. -pub type RuntimeMetadataLastVersion = RuntimeMetadataV10; +pub type RuntimeMetadataLastVersion = RuntimeMetadataV11; /// All metadata about an runtime module. #[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] @@ -402,6 +417,6 @@ impl Into for RuntimeMetadataPrefixed { impl Into for RuntimeMetadataLastVersion { fn into(self) -> RuntimeMetadataPrefixed { - RuntimeMetadataPrefixed(META_RESERVED, RuntimeMetadata::V10(self)) + RuntimeMetadataPrefixed(META_RESERVED, RuntimeMetadata::V11(self)) } } diff --git a/frame/nicks/Cargo.toml b/frame/nicks/Cargo.toml index 0dce684f0df2186fcb3845b25721f535d514fe1f..7c05080d49110a6bf6cc0636d46ef046fe175c44 100644 --- a/frame/nicks/Cargo.toml +++ b/frame/nicks/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-nicks" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 1d33d12ae4600b85111e39d393ce22b3c57e6841..6a86f08093bdbead61befcae50d1bbcb6d9a9479 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -78,7 +78,7 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as Sudo { /// The lookup table for names. - NameOf: map T::AccountId => Option<(Vec, BalanceOf)>; + NameOf: map hasher(blake2_256) T::AccountId => Option<(Vec, BalanceOf)>; } } @@ -194,7 +194,7 @@ decl_module! { /// - One storage read/write. /// - One event. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(70_000)] fn kill_name(origin, target: ::Source) { T::ForceOrigin::try_origin(origin) .map(|_| ()) @@ -222,7 +222,7 @@ decl_module! { /// - One storage read/write. /// - One event. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(70_000)] fn force_name(origin, target: ::Source, name: Vec) { T::ForceOrigin::try_origin(origin) .map(|_| ()) @@ -285,23 +285,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const ReservationFee: u64 = 2; @@ -334,7 +330,6 @@ mod tests { (1, 10), (2, 10), ], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); t.into() } @@ -359,9 +354,9 @@ mod tests { ); assert_ok!(Nicks::set_name(Origin::signed(2), b"Dave".to_vec())); - assert_eq!(Balances::reserved_balance(&2), 2); + assert_eq!(Balances::reserved_balance(2), 2); assert_ok!(Nicks::force_name(Origin::signed(1), 2, b"Dr. David Brubeck, III".to_vec())); - assert_eq!(Balances::reserved_balance(&2), 2); + assert_eq!(Balances::reserved_balance(2), 2); assert_eq!(>::get(2).unwrap(), (b"Dr. David Brubeck, III".to_vec(), 2)); }); } @@ -370,18 +365,18 @@ mod tests { fn normal_operation_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nicks::set_name(Origin::signed(1), b"Gav".to_vec())); - assert_eq!(Balances::reserved_balance(&1), 2); - assert_eq!(Balances::free_balance(&1), 8); + assert_eq!(Balances::reserved_balance(1), 2); + assert_eq!(Balances::free_balance(1), 8); assert_eq!(>::get(1).unwrap().0, b"Gav".to_vec()); assert_ok!(Nicks::set_name(Origin::signed(1), b"Gavin".to_vec())); - assert_eq!(Balances::reserved_balance(&1), 2); - assert_eq!(Balances::free_balance(&1), 8); + assert_eq!(Balances::reserved_balance(1), 2); + assert_eq!(Balances::free_balance(1), 8); assert_eq!(>::get(1).unwrap().0, b"Gavin".to_vec()); assert_ok!(Nicks::clear_name(Origin::signed(1))); - assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(Balances::free_balance(&1), 10); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 10); }); } diff --git a/frame/offences/Cargo.toml b/frame/offences/Cargo.toml index 36d057f4e9fd9c892f1b07db501ad3cf79302b7b..f5486f35ffd95f204929842906940c0b1119e186 100644 --- a/frame/offences/Cargo.toml +++ b/frame/offences/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-offences" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] pallet-balances = { version = "2.0.0", default-features = false, path = "../balances" } diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 310c018d76134aca38862aec70d5b6c1006a5f7d..ea6f96cbc3ca495636a58b95d7be12fa9c3c1e0d 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -54,10 +54,12 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as Offences { /// The primary structure that holds all offence records keyed by report identifiers. - Reports get(fn reports): map ReportIdOf => Option>; + Reports get(fn reports): map hasher(blake2_256) ReportIdOf => Option>; /// A vector of reports of the same kind that happened at the same time slot. - ConcurrentReportsIndex: double_map Kind, OpaqueTimeSlot => Vec>; + ConcurrentReportsIndex: + double_map hasher(blake2_256) Kind, hasher(blake2_256) OpaqueTimeSlot + => Vec>; /// Enumerates all reports of a kind along with the time they happened. /// @@ -65,7 +67,7 @@ decl_storage! { /// /// Note that the actual type of this mapping is `Vec`, this is because values of /// different types are not supported at the moment so we are doing the manual serialization. - ReportsByKindIndex: map Kind => Vec; // (O::TimeSlot, ReportIdOf) + ReportsByKindIndex: map hasher(blake2_256) Kind => Vec; // (O::TimeSlot, ReportIdOf) } } @@ -148,7 +150,7 @@ impl Module { for offender in offenders { let report_id = Self::report_id::(time_slot, &offender); - if !>::exists(&report_id) { + if !>::contains_key(&report_id) { any_new = true; >::insert( &report_id, diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index f344206f5ccdda633d0898ba9913f7ecf24cd71f..f2e19b63f5a47d21670336619ed9a12f812c9d1b 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -89,6 +89,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Runtime { @@ -103,6 +106,7 @@ mod offences { impl_outer_event! { pub enum TestEvent for Runtime { + system, offences, } } diff --git a/frame/randomness-collective-flip/Cargo.toml b/frame/randomness-collective-flip/Cargo.toml index 9e25ba4c5708285a56b49bdd9fdb0266173aa2b8..56ff12d2dfe810584c67e3fd0ae56b2c59365d90 100644 --- a/frame/randomness-collective-flip/Cargo.toml +++ b/frame/randomness-collective-flip/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-randomness-collective-flip" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] safe-mix = { version = "1.0", default-features = false } diff --git a/frame/randomness-collective-flip/src/lib.rs b/frame/randomness-collective-flip/src/lib.rs index c7f7bb0db78b8bbbba3fe7634f05619458dc5945..64d8f40099d1ba9073cc275c2e8f8da5d0d0da75 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/randomness-collective-flip/src/lib.rs @@ -154,7 +154,9 @@ mod tests { use super::*; use sp_core::H256; use sp_runtime::{ - Perbill, traits::{BlakeTwo256, OnInitialize, Header as _, IdentityLookup}, testing::Header, + Perbill, + testing::Header, + traits::{BlakeTwo256, OnInitialize, Header as _, IdentityLookup}, }; use frame_support::{impl_outer_origin, parameter_types, weights::Weight, traits::Randomness}; @@ -189,6 +191,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } type System = frame_system::Module; diff --git a/frame/recovery/Cargo.toml b/frame/recovery/Cargo.toml index 9f2f50ab06f84c28af354be4d9745c47092c73d1..645b1f2e98a34b74d909ec1947515d9289d2c980 100644 --- a/frame/recovery/Cargo.toml +++ b/frame/recovery/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-recovery" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/recovery/src/lib.rs b/frame/recovery/src/lib.rs index d0b5783587251ed71275bd26ba8b6b5cbaba0667..d293e1da9a072f6f6c128dbda215a0f5e22a201a 100644 --- a/frame/recovery/src/lib.rs +++ b/frame/recovery/src/lib.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . //! # Recovery Pallet -//! +//! //! - [`recovery::Trait`](./trait.Trait.html) //! - [`Call`](./enum.Call.html) //! @@ -82,7 +82,7 @@ //! permissionless. However, the recovery deposit is an economic deterrent that //! should disincentivize would-be attackers from trying to maliciously recover //! accounts. -//! +//! //! The recovery deposit can always be claimed by the account which is trying to //! to be recovered. In the case of a malicious recovery attempt, the account //! owner who still has access to their account can claim the deposit and @@ -121,14 +121,14 @@ //! change as well. //! //! ## Interface -//! +//! //! ### Dispatchable Functions -//! +//! //! #### For General Users -//! +//! //! * `create_recovery` - Create a recovery configuration for your account and make it recoverable. //! * `initiate_recovery` - Start the recovery process for a recoverable account. -//! +//! //! #### For Friends of a Recoverable Account //! * `vouch_recovery` - As a `friend` of a recoverable account, vouch for a recovery attempt on the account. //! @@ -141,7 +141,7 @@ //! //! * `close_recovery` - Close an active recovery process for your account and reclaim the recovery deposit. //! * `remove_recovery` - Remove the recovery configuration from the account, making it un-recoverable. -//! +//! //! #### For Super Users //! //! * `set_recovered` - The ROOT origin is able to skip the recovery process and directly allow @@ -164,7 +164,7 @@ use frame_support::{ GetDispatchInfo, PaysFee, DispatchClass, ClassifyDispatch, Weight, WeighData, SimpleDispatchInfo, }, - traits::{Currency, ReservableCurrency, Get, OnReapAccount}, + traits::{Currency, ReservableCurrency, Get, OnReapAccount, BalanceStatus}, }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -242,7 +242,8 @@ decl_storage! { trait Store for Module as Recovery { /// The set of recoverable accounts and their recovery configuration. pub Recoverable get(fn recovery_config): - map T::AccountId => Option, T::AccountId>>; + map hasher(blake2_256) T::AccountId + => Option, T::AccountId>>; /// Active recovery attempts. /// /// First account is the account to be recovered, and the second account @@ -253,7 +254,8 @@ decl_storage! { /// The final list of recovered accounts. /// /// Map from the recovered account to the user who can access it. - pub Recovered get(fn recovered_account): map T::AccountId => Option; + pub Recovered get(fn recovered_account): + map hasher(blake2_256) T::AccountId => Option; } } @@ -314,9 +316,11 @@ decl_error! { decl_module! { pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; - + /// Send a call through a recovered account. /// /// The dispatch origin for this call must be _Signed_ and registered to @@ -340,7 +344,7 @@ decl_module! { ensure!(Self::recovered_account(&account) == Some(who), Error::::NotAllowed); call.dispatch(frame_system::RawOrigin::Signed(account).into()) } - + /// Allow ROOT to bypass the recovery process and set an a rescuer account /// for a lost account directly. /// @@ -361,7 +365,7 @@ decl_module! { >::insert(&lost, &rescuer); Self::deposit_event(RawEvent::AccountRecovered(lost, rescuer)); } - + /// Create a recovery configuration for your account. This makes your account recoverable. /// /// Payment: `ConfigDepositBase` + `FriendDepositFactor` * #_of_friends balance @@ -397,7 +401,7 @@ decl_module! { ) { let who = ensure_signed(origin)?; // Check account is not already set up for recovery - ensure!(!>::exists(&who), Error::::AlreadyRecoverable); + ensure!(!>::contains_key(&who), Error::::AlreadyRecoverable); // Check user input is valid ensure!(threshold >= 1, Error::::ZeroThreshold); ensure!(!friends.is_empty(), Error::::NotEnoughFriends); @@ -425,7 +429,7 @@ decl_module! { >::insert(&who, recovery_config); Self::deposit_event(RawEvent::RecoveryCreated(who)); } - + /// Initiate the process for recovering a recoverable account. /// /// Payment: `RecoveryDeposit` balance will be reserved for initiating the @@ -452,9 +456,9 @@ decl_module! { fn initiate_recovery(origin, account: T::AccountId) { let who = ensure_signed(origin)?; // Check that the account is recoverable - ensure!(>::exists(&account), Error::::NotRecoverable); + ensure!(>::contains_key(&account), Error::::NotRecoverable); // Check that the recovery process has not already been started - ensure!(!>::exists(&account, &who), Error::::AlreadyStarted); + ensure!(!>::contains_key(&account, &who), Error::::AlreadyStarted); // Take recovery deposit let recovery_deposit = T::RecoveryDeposit::get(); T::Currency::reserve(&who, recovery_deposit)?; @@ -468,7 +472,7 @@ decl_module! { >::insert(&account, &who, recovery_status); Self::deposit_event(RawEvent::RecoveryInitiated(account, who)); } - + /// Allow a "friend" of a recoverable account to vouch for an active recovery /// process for that account. /// @@ -512,7 +516,7 @@ decl_module! { >::insert(&lost, &rescuer, active_recovery); Self::deposit_event(RawEvent::RecoveryVouched(lost, rescuer, who)); } - + /// Allow a successful rescuer to claim their recovered account. /// /// The dispatch origin for this call must be _Signed_ and must be a "rescuer" @@ -555,7 +559,7 @@ decl_module! { >::insert(&account, &who); Self::deposit_event(RawEvent::AccountRecovered(account, who)); } - + /// As the controller of a recoverable account, close an active recovery /// process for your account. /// @@ -583,10 +587,10 @@ decl_module! { let active_recovery = >::take(&who, &rescuer).ok_or(Error::::NotStarted)?; // Move the reserved funds from the rescuer to the rescued account. // Acts like a slashing mechanism for those who try to maliciously recover accounts. - let _ = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit); + let _ = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit, BalanceStatus::Free); Self::deposit_event(RawEvent::RecoveryClosed(who, rescuer)); } - + /// Remove the recovery process for your account. /// /// NOTE: The user must make sure to call `close_recovery` on all active diff --git a/frame/recovery/src/mock.rs b/frame/recovery/src/mock.rs index be9ed5c97e59823233ca21838c6831cd72eb56bb..e6bc84f76b997980ec8d8f8503fbe94a119c9f78 100644 --- a/frame/recovery/src/mock.rs +++ b/frame/recovery/src/mock.rs @@ -36,6 +36,7 @@ impl_outer_origin! { impl_outer_event! { pub enum TestEvent for Test { + system, pallet_balances, recovery, } @@ -62,10 +63,10 @@ parameter_types! { impl frame_system::Trait for Test { type Origin = Origin; + type Call = Call; type Index = u64; type BlockNumber = u64; type Hash = H256; - type Call = Call; type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; @@ -77,25 +78,21 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Recovery); } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u128; - type OnFreeBalanceZero = (); - type OnReapAccount = (System, Recovery); - type OnNewAccount = (); - type Event = TestEvent; - type TransferPayment = (); type DustRemoval = (); + type Event = TestEvent; type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -126,7 +123,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); t.into() } diff --git a/frame/recovery/src/tests.rs b/frame/recovery/src/tests.rs index 76f05156ef5c63a01aa8f21470dd6f8f881e9a1d..af4eadb58fa392f1bd1a6bdcf94bd96c93691994 100644 --- a/frame/recovery/src/tests.rs +++ b/frame/recovery/src/tests.rs @@ -35,7 +35,7 @@ fn basic_setup_works() { assert_eq!(Recovery::active_recovery(&1, &2), None); assert_eq!(Recovery::recovery_config(&1), None); // Everyone should have starting balance of 100 - assert_eq!(Balances::free_balance(&1), 100); + assert_eq!(Balances::free_balance(1), 100); }); } @@ -92,9 +92,9 @@ fn recovery_lifecycle_works() { assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(5), 0); // All storage items are removed from the module - assert!(!>::exists(&5, &1)); - assert!(!>::exists(&5)); - assert!(!>::exists(&5)); + assert!(!>::contains_key(&5, &1)); + assert!(!>::contains_key(&5)); + assert!(!>::contains_key(&5)); }); } @@ -219,7 +219,7 @@ fn initiate_recovery_handles_basic_errors() { assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5)); assert_noop!(Recovery::initiate_recovery(Origin::signed(1), 5), Error::::AlreadyStarted); // No double deposit - assert_eq!(Balances::reserved_balance(&1), 10); + assert_eq!(Balances::reserved_balance(1), 10); }); } @@ -234,7 +234,7 @@ fn initiate_recovery_works() { // Recovery can be initiated assert_ok!(Recovery::initiate_recovery(Origin::signed(1), 5)); // Deposit is reserved - assert_eq!(Balances::reserved_balance(&1), 10); + assert_eq!(Balances::reserved_balance(1), 10); // Recovery status object is created correctly let recovery_status = ActiveRecovery { created: 1, diff --git a/frame/scored-pool/Cargo.toml b/frame/scored-pool/Cargo.toml index 0b4c2768dfcb553046c9767c057217393b43a89a..c0935cd88be925dbe59fc0ba7ecf253ca354d124 100644 --- a/frame/scored-pool/Cargo.toml +++ b/frame/scored-pool/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-scored-pool" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/frame/scored-pool/src/lib.rs b/frame/scored-pool/src/lib.rs index a8fc69971aae06dcbbb37a2fe0a77311e456e8ff..e3854c752441cf1501194f411bbf59c23c5e419b 100644 --- a/frame/scored-pool/src/lib.rs +++ b/frame/scored-pool/src/lib.rs @@ -99,7 +99,7 @@ use frame_support::{ }; use frame_system::{self as system, ensure_root, ensure_signed}; use sp_runtime::{ - traits::{EnsureOrigin, SimpleArithmetic, MaybeSerializeDeserialize, Zero, StaticLookup}, + traits::{EnsureOrigin, AtLeast32Bit, MaybeSerializeDeserialize, Zero, StaticLookup}, }; type BalanceOf = <>::Currency as Currency<::AccountId>>::Balance; @@ -121,7 +121,7 @@ pub trait Trait: frame_system::Trait { /// The score attributed to a member or candidate. type Score: - SimpleArithmetic + Clone + Copy + Default + FullCodec + MaybeSerializeDeserialize + Debug; + AtLeast32Bit + Clone + Copy + Default + FullCodec + MaybeSerializeDeserialize + Debug; /// The overarching event type. type Event: From> + Into<::Event>; @@ -165,7 +165,7 @@ decl_storage! { /// check if a candidate is already in the pool, without having to /// iterate over the entire pool (the `Pool` is not sorted by /// `T::AccountId`, but by `T::Score` instead). - CandidateExists get(fn candidate_exists): map T::AccountId => bool; + CandidateExists get(fn candidate_exists): map hasher(blake2_256) T::AccountId => bool; /// The current membership, stored as an ordered Vec. Members get(fn members): Vec; @@ -265,7 +265,7 @@ decl_module! { /// the index of the transactor in the `Pool`. pub fn submit_candidacy(origin) { let who = ensure_signed(origin)?; - ensure!(!>::exists(&who), Error::::AlreadyInPool); + ensure!(!>::contains_key(&who), Error::::AlreadyInPool); let deposit = T::CandidateDeposit::get(); T::Currency::reserve(&who, deposit)?; diff --git a/frame/scored-pool/src/mock.rs b/frame/scored-pool/src/mock.rs index dd59bbc84fe6445ce3e9f4618080f268de1eb64c..38a01a69afa803be619bf3e245a94f2684b29a25 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -46,9 +46,7 @@ parameter_types! { pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } ord_parameter_types! { pub const KickOrigin: u64 = 2; @@ -72,19 +70,17 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } thread_local! { @@ -144,7 +140,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { (40, 500_000), (99, 1), ], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); GenesisConfig::{ pool: vec![ diff --git a/frame/scored-pool/src/tests.rs b/frame/scored-pool/src/tests.rs index d8c887f4ec6837783ee57444204222161dff4590..4b21339505ded6b55d9a520595af2df1c8277fca 100644 --- a/frame/scored-pool/src/tests.rs +++ b/frame/scored-pool/src/tests.rs @@ -30,8 +30,8 @@ type Balances = pallet_balances::Module; fn query_membership_works() { new_test_ext().execute_with(|| { assert_eq!(ScoredPool::members(), vec![20, 40]); - assert_eq!(Balances::reserved_balance(&31), CandidateDeposit::get()); - assert_eq!(Balances::reserved_balance(&40), CandidateDeposit::get()); + assert_eq!(Balances::reserved_balance(31), CandidateDeposit::get()); + assert_eq!(Balances::reserved_balance(40), CandidateDeposit::get()); assert_eq!(MEMBERS.with(|m| m.borrow().clone()), vec![20, 40]); }); } @@ -61,7 +61,7 @@ fn submit_candidacy_works() { assert_eq!(fetch_from_pool(15), Some((who, None))); // then - assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get()); + assert_eq!(Balances::reserved_balance(who), CandidateDeposit::get()); }); } @@ -117,7 +117,7 @@ fn kicking_works() { new_test_ext().execute_with(|| { // given let who = 40; - assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get()); + assert_eq!(Balances::reserved_balance(who), CandidateDeposit::get()); assert_eq!(find_in_pool(who), Some(0)); // when @@ -128,7 +128,7 @@ fn kicking_works() { assert_eq!(find_in_pool(who), None); assert_eq!(ScoredPool::members(), vec![20, 31]); assert_eq!(MEMBERS.with(|m| m.borrow().clone()), ScoredPool::members()); - assert_eq!(Balances::reserved_balance(&who), 0); // deposit must have been returned + assert_eq!(Balances::reserved_balance(who), 0); // deposit must have been returned }); } @@ -246,7 +246,7 @@ fn withdraw_scored_candidacy_must_work() { new_test_ext().execute_with(|| { // given let who = 40; - assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get()); + assert_eq!(Balances::reserved_balance(who), CandidateDeposit::get()); // when let index = find_in_pool(who).expect("entity must be in pool") as u32; @@ -255,7 +255,7 @@ fn withdraw_scored_candidacy_must_work() { // then assert_eq!(fetch_from_pool(who), None); assert_eq!(ScoredPool::members(), vec![20, 31]); - assert_eq!(Balances::reserved_balance(&who), 0); + assert_eq!(Balances::reserved_balance(who), 0); }); } diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index aa9aaa1f4479a96a423abde9b827a4a5421df2fb..06981240b1576cdc85a2de3aa09fb62ec2bff992 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -2,7 +2,8 @@ name = "pallet-session" version = "2.0.0" authors = ["Parity Technologies "] -edition = "2018" +edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/session/src/historical.rs b/frame/session/src/historical.rs index e67a2ae1b0eab0dc30c11a78eb4da3aa88cfb745..76175cd6be0e55850dfcb0a8b29b2328fbe78d4a 100644 --- a/frame/session/src/historical.rs +++ b/frame/session/src/historical.rs @@ -55,11 +55,14 @@ pub trait Trait: super::Trait { decl_storage! { trait Store for Module as Session { /// Mapping from historical session indices to session-data root hash and validator count. - HistoricalSessions get(fn historical_root): map SessionIndex => Option<(T::Hash, ValidatorCount)>; + HistoricalSessions get(fn historical_root): + map hasher(blake2_256) SessionIndex => Option<(T::Hash, ValidatorCount)>; /// The range of historical sessions we store. [first, last) StoredRange: Option<(SessionIndex, SessionIndex)>; /// Deprecated. - CachedObsolete: map SessionIndex => Option>; + CachedObsolete: + map hasher(blake2_256) SessionIndex + => Option>; } } diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 098b5330779effdfd09c792b4dce329c4d9f4406..a18d55d26cbc1b483df5bcda1546c5a1a49de91a 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -30,92 +30,72 @@ //! //! - **Session:** A session is a period of time that has a constant set of validators. Validators can only join //! or exit the validator set at a session change. It is measured in block numbers. The block where a session is -//! ended is determined by the `ShouldSessionEnd` trait. When the session is ending, a new validator set +//! ended is determined by the `ShouldEndSession` trait. When the session is ending, a new validator set //! can be chosen by `OnSessionEnding` implementations. //! - **Session key:** A session key is actually several keys kept together that provide the various signing //! functions required by network authorities/validators in pursuit of their duties. //! - **Validator ID:** Every account has an associated validator ID. For some simple staking systems, this //! may just be the same as the account ID. For staking systems using a stash/controller model, //! the validator ID would be the stash account ID of the controller. -//! - **Session key configuration process:** A session key is set using `set_key` for use in the -//! next session. It is stored in `NextKeyFor`, a mapping between the caller's `ValidatorId` and the session -//! keys provided. `set_key` allows users to set their session key prior to being selected as validator. +//! - **Session key configuration process:** Session keys are set using `set_keys` for use not in +//! the next session, but the session after next. They are stored in `NextKeys`, a mapping between +//! the caller's `ValidatorId` and the session keys provided. `set_keys` allows users to set their +//! session key prior to being selected as validator. //! It is a public call since it uses `ensure_signed`, which checks that the origin is a signed account. -//! As such, the account ID of the origin stored in in `NextKeyFor` may not necessarily be associated with +//! As such, the account ID of the origin stored in `NextKeys` may not necessarily be associated with //! a block author or a validator. The session keys of accounts are removed once their account balance is zero. -//! - **Validator set session key configuration process:** Each session we iterate through the current -//! set of validator account IDs to check if a session key was created for it in the previous session -//! using `set_key`. If it was then we call `set_authority` from the [Consensus module](../frame_consensus/index.html) -//! and pass it a set of session keys (each associated with an account ID) as the session keys for the new -//! validator set. Lastly, if the session key of the current authority does not match any session keys stored under -//! its validator index in the `AuthorityStorageVec` mapping, then we update the mapping with its session -//! key and update the saved list of original authorities if necessary -//! (see https://github.com/paritytech/substrate/issues/1290). Note: Authorities are stored in the Consensus module. -//! They are represented by a validator account ID index from the Session module and allocated with a session -//! key for the length of the session. -//! - **Session length change process:** At the start of the next session we allocate a session index and record the -//! timestamp when the session started. If a `NextSessionLength` was recorded in the previous session, we record -//! it as the new session length. Additionally, if the new session length differs from the length of the -//! next session then we record a `LastLengthChange`. +//! - **Session length:** This pallet does not assume anything about the length of each session. +//! Rather, it relies on an implementation of `ShouldEndSession` to dictate a new session's start. +//! This pallet provides the `PeriodicSessions` struct for simple periodic sessions. //! - **Session rotation configuration:** Configure as either a 'normal' (rewardable session where rewards are //! applied) or 'exceptional' (slashable) session rotation. -//! - **Session rotation process:** The session is changed at the end of the final block of the current session -//! using the `on_finalize` method. It may be called by either an origin or internally from another runtime -//! module at the end of each block. +//! - **Session rotation process:** At the beginning of each block, the `on_initialize` function +//! queries the provided implementation of `ShouldEndSession`. If the session is to end the newly +//! activated validator IDs and session keys are taken from storage and passed to the +//! `SessionHandler`. The validator set supplied by `SessionManager::new_session` and the corresponding session +//! keys, which may have been registered via `set_keys` during the previous session, are written +//! to storage where they will wait one session before being passed to the `SessionHandler` +//! themselves. //! //! ### Goals //! -//! The Session module in Substrate is designed to make the following possible: +//! The Session pallet is designed to make the following possible: //! -//! - Set session keys of the validator set for the next session. -//! - Set the length of a session. +//! - Set session keys of the validator set for upcoming sessions. +//! - Control the length of sessions. //! - Configure and switch between either normal or exceptional session rotations. //! //! ## Interface //! //! ### Dispatchable Functions //! -//! - `set_key` - Set a validator's session key for the next session. -//! - `set_length` - Set a new session length to be applied upon the next session change. -//! - `force_new_session` - Force a new session that should be considered either a normal (rewardable) -//! or exceptional rotation. -//! - `on_finalize` - Called when a block is finalized. Will rotate session if it is the last -//! block of the current session. +//! - `set_keys` - Set a validator's session keys for upcoming sessions. //! //! ### Public Functions //! -//! - `validator_count` - Get the current number of validators. -//! - `last_length_change` - Get the block number when the session length last changed. -//! - `apply_force_new_session` - Force a new session. Can be called by other runtime modules. -//! - `set_validators` - Set the current set of validators. Can only be called by the Staking module. -//! - `check_rotate_session` - Rotate the session and apply rewards if necessary. Called after the Staking -//! module updates the authorities to the new validator set. -//! - `rotate_session` - Change to the next session. Register the new authority set. Update session keys. -//! Enact session length change if applicable. -//! - `ideal_session_duration` - Get the time of an ideal session. -//! - `blocks_remaining` - Get the number of blocks remaining in the current session, -//! excluding the current block. +//! - `rotate_session` - Change to the next session. Register the new authority set. Queue changes +//! for next session rotation. +//! - `disable_index` - Disable a validator by index. +//! - `disable` - Disable a validator by Validator ID //! //! ## Usage //! //! ### Example from the SRML //! -//! The [Staking module](../pallet_staking/index.html) uses the Session module to get the validator set. +//! The [Staking pallet](../pallet_staking/index.html) uses the Session pallet to get the validator set. //! //! ``` //! use pallet_session as session; -//! # fn not_executed() { //! -//! let validators = >::validators(); -//! # } +//! fn validators() -> Vec<::ValidatorId> { +//! >::validators() +//! } //! # fn main(){} //! ``` //! //! ## Related Modules //! -//! - [Consensus](../frame_consensus/index.html) //! - [Staking](../pallet_staking/index.html) -//! - [Timestamp](../pallet_timestamp/index.html) #![cfg_attr(not(feature = "std"), no_std)] @@ -126,7 +106,7 @@ use frame_support::weights::SimpleDispatchInfo; use sp_runtime::traits::{Convert, Zero, Member, OpaqueKeys}; use sp_staking::SessionIndex; use frame_support::{dispatch, ConsensusEngineId, decl_module, decl_event, decl_storage, decl_error}; -use frame_support::{ensure, traits::{OnFreeBalanceZero, Get, FindAuthor, ValidatorRegistration}, Parameter}; +use frame_support::{ensure, traits::{OnReapAccount, Get, FindAuthor, ValidatorRegistration}, Parameter}; use frame_system::{self as system, ensure_signed}; #[cfg(test)] @@ -478,7 +458,7 @@ decl_module! { fn deposit_event() = default; - /// Sets the session key(s) of the function caller to `key`. + /// Sets the session key(s) of the function caller to `keys`. /// Allows an account to set its session key prior to becoming a validator. /// This doesn't take effect until the next session. /// @@ -696,8 +676,8 @@ impl Module { } } -impl OnFreeBalanceZero for Module { - fn on_free_balance_zero(who: &T::ValidatorId) { +impl OnReapAccount for Module { + fn on_reap_account(who: &T::ValidatorId) { Self::prune_dead_keys(who); } } @@ -774,7 +754,7 @@ mod tests { let id = DUMMY; assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), Some(1)); - Session::on_free_balance_zero(&1); + Session::on_reap_account(&1); assert_eq!(Session::load_keys(&1), None); assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), None); }) diff --git a/frame/session/src/mock.rs b/frame/session/src/mock.rs index ff84743a61596d02704dfdba7d006908835ff450..0c922670697aa1856b65529571e9e4c3009ea5b4 100644 --- a/frame/session/src/mock.rs +++ b/frame/session/src/mock.rs @@ -21,8 +21,9 @@ use std::cell::RefCell; use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; use sp_core::{crypto::key_types::DUMMY, H256}; use sp_runtime::{ - Perbill, impl_opaque_keys, traits::{BlakeTwo256, IdentityLookup, ConvertInto}, - testing::{Header, UintAuthorityId} + Perbill, impl_opaque_keys, + traits::{BlakeTwo256, IdentityLookup, ConvertInto}, + testing::{Header, UintAuthorityId}, }; use sp_staking::SessionIndex; @@ -175,6 +176,9 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = Session; } impl pallet_timestamp::Trait for Test { diff --git a/frame/society/Cargo.toml b/frame/society/Cargo.toml index 94000e898d6028802e61c1a5c53735b144ef3415..ac140c971420b281c330a8072cd6f4c92abb3e21 100644 --- a/frame/society/Cargo.toml +++ b/frame/society/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-society" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index f4e5904ea42d7546a690723b31792a3e1d2231b5..e803e54d4f7c1cc3a853db5b8ecfe391ceb27a49 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -262,8 +262,8 @@ use sp_runtime::{Percent, ModuleId, RuntimeDebug, use frame_support::{decl_error, decl_module, decl_storage, decl_event, ensure, dispatch::DispatchResult}; use frame_support::weights::SimpleDispatchInfo; use frame_support::traits::{ - Currency, ReservableCurrency, Randomness, Get, ChangeMembers, - ExistenceRequirement::{KeepAlive, AllowDeath}, + Currency, ReservableCurrency, Randomness, Get, ChangeMembers, BalanceStatus, + ExistenceRequirement::AllowDeath, }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -414,7 +414,8 @@ decl_storage! { /// The set of suspended candidates. pub SuspendedCandidates get(suspended_candidate): - map T::AccountId => Option<(BalanceOf, BidKind>)>; + map hasher(blake2_256) T::AccountId + => Option<(BalanceOf, BidKind>)>; /// Amount of our account balance that is specifically for the next round's bid(s). pub Pot get(fn pot) config(): BalanceOf; @@ -431,19 +432,19 @@ decl_storage! { }): Vec; /// The set of suspended members. - pub SuspendedMembers get(fn suspended_member): map T::AccountId => bool; + pub SuspendedMembers get(fn suspended_member): map hasher(blake2_256) T::AccountId => bool; /// The current bids, stored ordered by the value of the bid. Bids: Vec>>; /// Members currently vouching or banned from vouching again - Vouching get(fn vouching): map T::AccountId => Option; + Vouching get(fn vouching): map hasher(blake2_256) T::AccountId => Option; /// Pending payouts; ordered by block number, with the amount that should be paid out. - Payouts: map T::AccountId => Vec<(T::BlockNumber, BalanceOf)>; + Payouts: map hasher(blake2_256) T::AccountId => Vec<(T::BlockNumber, BalanceOf)>; /// The ongoing number of losing votes cast by the member. - Strikes: map T::AccountId => StrikeCount; + Strikes: map hasher(blake2_256) T::AccountId => StrikeCount; /// Double map from Candidate -> Voter -> (Maybe) Vote. Votes: double_map @@ -529,8 +530,8 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(50_000)] pub fn bid(origin, value: BalanceOf) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(!>::exists(&who), Error::::Suspended); - ensure!(!>::exists(&who), Error::::Suspended); + ensure!(!>::contains_key(&who), Error::::Suspended); + ensure!(!>::contains_key(&who), Error::::Suspended); let bids = >::get(); ensure!(!Self::is_bid(&bids, &who), Error::::AlreadyBid); let candidates = >::get(); @@ -639,8 +640,8 @@ decl_module! { pub fn vouch(origin, who: T::AccountId, value: BalanceOf, tip: BalanceOf) -> DispatchResult { let voucher = ensure_signed(origin)?; // Check user is not suspended. - ensure!(!>::exists(&who), Error::::Suspended); - ensure!(!>::exists(&who), Error::::Suspended); + ensure!(!>::contains_key(&who), Error::::Suspended); + ensure!(!>::contains_key(&who), Error::::Suspended); // Check user is not a bid or candidate. let bids = >::get(); ensure!(!Self::is_bid(&bids, &who), Error::::AlreadyBid); @@ -651,7 +652,7 @@ decl_module! { ensure!(!Self::is_member(&members, &who), Error::::AlreadyMember); // Check sender can vouch. ensure!(Self::is_member(&members, &voucher), Error::::NotMember); - ensure!(!>::exists(&voucher), Error::::AlreadyVouching); + ensure!(!>::contains_key(&voucher), Error::::AlreadyVouching); >::insert(&voucher, VouchingStatus::Vouching); Self::put_bid(bids, &who, value.clone(), BidKind::Vouch(voucher.clone(), tip)); @@ -787,7 +788,7 @@ decl_module! { let mut payouts = >::get(&who); if let Some((when, amount)) = payouts.first() { if when <= &>::block_number() { - T::Currency::transfer(&Self::payouts(), &who, *amount, KeepAlive)?; + T::Currency::transfer(&Self::payouts(), &who, *amount, AllowDeath)?; payouts.remove(0); if payouts.is_empty() { >::remove(&who); @@ -891,7 +892,7 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(30_000)] fn judge_suspended_member(origin, who: T::AccountId, forgive: bool) { T::SuspensionJudgementOrigin::ensure_origin(origin)?; - ensure!(>::exists(&who), Error::::NotSuspended); + ensure!(>::contains_key(&who), Error::::NotSuspended); if forgive { // Try to add member back to society. Can fail with `MaxMembers` limit. @@ -983,7 +984,7 @@ decl_module! { match kind { BidKind::Deposit(deposit) => { // Slash deposit and move it to the society account - let _ = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit); + let _ = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit, BalanceStatus::Free); } BidKind::Vouch(voucher, _) => { // Ban the voucher from vouching again @@ -1535,18 +1536,24 @@ impl, I: Instance> Module { >::remove_all(); } - // Start a new defender rotation - let phrase = b"society_challenge"; - // we'll need a random seed here. - let seed = T::Randomness::random(phrase); - // seed needs to be guaranteed to be 32 bytes. - let seed = <[u8; 32]>::decode(&mut TrailingZeroInput::new(seed.as_ref())) - .expect("input is padded with zeroes; qed"); - let mut rng = ChaChaRng::from_seed(seed); - let chosen = pick_item(&mut rng, &members).expect("exited if members empty; qed"); - - >::put(&chosen); - Self::deposit_event(RawEvent::Challenged(chosen.clone())); + // Avoid challenging if there's only two members since we never challenge the Head or + // the Founder. + if members.len() > 2 { + // Start a new defender rotation + let phrase = b"society_challenge"; + // we'll need a random seed here. + let seed = T::Randomness::random(phrase); + // seed needs to be guaranteed to be 32 bytes. + let seed = <[u8; 32]>::decode(&mut TrailingZeroInput::new(seed.as_ref())) + .expect("input is padded with zeroes; qed"); + let mut rng = ChaChaRng::from_seed(seed); + let chosen = pick_item(&mut rng, &members[1..members.len() - 1]) + .expect("exited if members empty; qed"); + >::put(&chosen); + Self::deposit_event(RawEvent::Challenged(chosen.clone())); + } else { + >::kill(); + } } } diff --git a/frame/society/src/mock.rs b/frame/society/src/mock.rs index b8f249f35ce3925575fbf5c894b2954a550a8525..081d68ada4ce7076f8606d6926a045d6d81e78b6 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -23,7 +23,9 @@ use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried. use sp_runtime::{ - Perbill, traits::{BlakeTwo256, IdentityLookup, OnInitialize, OnFinalize}, testing::Header, + Perbill, + testing::Header, + traits::{BlakeTwo256, IdentityLookup, OnInitialize, OnFinalize}, }; use frame_system::EnsureSignedBy; @@ -50,9 +52,7 @@ parameter_types! { pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } ord_parameter_types! { @@ -77,19 +77,17 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type OnNewAccount = (); + type OnReapAccount = Balances; + type AccountData = pallet_balances::AccountData; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; - type OnReapAccount = System; + type AccountStore = System; } impl Trait for Test { @@ -146,7 +144,6 @@ impl EnvBuilder { self.balances.push((Society::account_id(), self.balance.max(self.pot))); pallet_balances::GenesisConfig:: { balances: self.balances, - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); GenesisConfig::{ members: self.members, diff --git a/frame/society/src/tests.rs b/frame/society/src/tests.rs index 3e5afc47f500afed7248a1f6baa082cb2b67baa4..47d13d8361c2b302b2821443577d6c6e4bcf591a 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -577,14 +577,14 @@ fn challenges_work() { assert_eq!(Society::defender(), None); // 20 will be challenged during the challenge rotation run_to_block(8); - assert_eq!(Society::defender(), Some(20)); + assert_eq!(Society::defender(), Some(30)); // They can always free vote for themselves - assert_ok!(Society::defender_vote(Origin::signed(20), true)); + assert_ok!(Society::defender_vote(Origin::signed(30), true)); // If no one else votes, nothing happens run_to_block(16); assert_eq!(Society::members(), vec![10, 20, 30, 40]); // New challenge period - assert_eq!(Society::defender(), Some(20)); + assert_eq!(Society::defender(), Some(30)); // Non-member cannot challenge assert_noop!(Society::defender_vote(Origin::signed(1), true), Error::::NotMember); // 3 people say accept, 1 reject @@ -601,7 +601,7 @@ fn challenges_work() { assert_eq!(>::get(30), None); assert_eq!(>::get(40), None); // One more time - assert_eq!(Society::defender(), Some(20)); + assert_eq!(Society::defender(), Some(30)); // 2 people say accept, 2 reject assert_ok!(Society::defender_vote(Origin::signed(10), true)); assert_ok!(Society::defender_vote(Origin::signed(20), true)); @@ -609,10 +609,10 @@ fn challenges_work() { assert_ok!(Society::defender_vote(Origin::signed(40), false)); run_to_block(32); // 20 is suspended - assert_eq!(Society::members(), vec![10, 30, 40]); - assert_eq!(Society::suspended_member(20), true); + assert_eq!(Society::members(), vec![10, 20, 40]); + assert_eq!(Society::suspended_member(30), true); // New defender is chosen - assert_eq!(Society::defender(), Some(40)); + assert_eq!(Society::defender(), Some(20)); // Votes are reset assert_eq!(>::get(10), None); assert_eq!(>::get(20), None); diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 52584cf47f47d969b929ce3d1ed373d012f502e1..a38cc0416b7273353856181b39a6b55ecf004d2e 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-staking" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/staking/reward-curve/Cargo.toml b/frame/staking/reward-curve/Cargo.toml index 0353476a955d1358a77ab4898287c49399661724..0753400596e1340c3d4d272694fa728cda0340f0 100644 --- a/frame/staking/reward-curve/Cargo.toml +++ b/frame/staking/reward-curve/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-staking-reward-curve" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true diff --git a/frame/staking/reward-curve/src/lib.rs b/frame/staking/reward-curve/src/lib.rs index 89a1980d19fd92a72afd588f9a38f25e9d3dbc8d..3c31cecc2145871b60f33caf67d36a3e55968bbf 100644 --- a/frame/staking/reward-curve/src/lib.rs +++ b/frame/staking/reward-curve/src/lib.rs @@ -386,7 +386,7 @@ fn generate_test_module(input: &INposInput) -> TokenStream2 { #[test] fn reward_curve_precision() { - for &base in [MILLION, u32::max_value()].into_iter() { + for &base in [MILLION, u32::max_value()].iter() { let number_of_check = 100_000.min(base); for check_index in 0..=number_of_check { let i = (check_index as u64 * base as u64 / number_of_check as u64) as u32; diff --git a/frame/staking/src/inflation.rs b/frame/staking/src/inflation.rs index 9f11fa984598766ce3ab902554237af271811367..d5135fcc1ffb3db8ee96a6c5aa16a519cf46742b 100644 --- a/frame/staking/src/inflation.rs +++ b/frame/staking/src/inflation.rs @@ -19,7 +19,7 @@ //! The staking rate in NPoS is the total amount of tokens staked by nominators and validators, //! divided by the total token supply. -use sp_runtime::{Perbill, traits::SimpleArithmetic, curve::PiecewiseLinear}; +use sp_runtime::{Perbill, PerThing, traits::AtLeast32Bit, curve::PiecewiseLinear}; /// The total payout to all validators (and their nominators) per era. /// @@ -32,7 +32,7 @@ pub fn compute_total_payout( npos_token_staked: N, total_tokens: N, era_duration: u64 -) -> (N, N) where N: SimpleArithmetic + Clone { +) -> (N, N) where N: AtLeast32Bit + Clone { // Milliseconds per year for the Julian year (365.25 days). const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100; diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 7f1ad37b32cf6f7900fa110c5c2d6c97df4a5e5e..447f655048a6b8c810ad0f044537e7654d7b95b6 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -250,7 +250,6 @@ mod mock; #[cfg(test)] mod tests; -mod migration; mod slashing; pub mod inflation; @@ -261,18 +260,17 @@ use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, weights::SimpleDispatchInfo, traits::{ - Currency, OnFreeBalanceZero, LockIdentifier, LockableCurrency, + Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, Time } }; use pallet_session::historical::SessionManager; use sp_runtime::{ - Perbill, - RuntimeDebug, + Perbill, PerThing, RuntimeDebug, curve::PiecewiseLinear, traits::{ Convert, Zero, One, StaticLookup, CheckedSub, Saturating, Bounded, SaturatedConversion, - SimpleArithmetic, EnsureOrigin, + AtLeast32Bit, EnsureOrigin, } }; use sp_staking::{ @@ -284,6 +282,7 @@ use sp_runtime::{Serialize, Deserialize}; use frame_system::{self as system, ensure_signed, ensure_root}; use sp_phragmen::ExtendedBalance; +use frame_support::traits::OnReapAccount; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const MAX_NOMINATIONS: usize = 16; @@ -395,7 +394,7 @@ pub struct StakingLedger { impl< AccountId, - Balance: HasCompact + Copy + Saturating + SimpleArithmetic, + Balance: HasCompact + Copy + Saturating + AtLeast32Bit, > StakingLedger { /// Remove entries from `unlocking` that are sufficiently old and reduce the /// total by the sum of their balances. @@ -439,7 +438,7 @@ impl< } impl StakingLedger where - Balance: SimpleArithmetic + Saturating + Copy, + Balance: AtLeast32Bit + Saturating + Copy, { /// Slash the validator for a given amount of balance. This can grow the value /// of the slash in the case that the validator has less than `minimum_balance` @@ -671,28 +670,32 @@ decl_storage! { pub Invulnerables get(fn invulnerables) config(): Vec; /// Map from all locked "stash" accounts to the controller account. - pub Bonded get(fn bonded): map T::AccountId => Option; + pub Bonded get(fn bonded): map hasher(blake2_256) T::AccountId => Option; /// Map from all (unlocked) "controller" accounts to the info regarding the staking. pub Ledger get(fn ledger): - map T::AccountId => Option>>; + map hasher(blake2_256) T::AccountId + => Option>>; /// Where the reward payment should be made. Keyed by stash. - pub Payee get(fn payee): map T::AccountId => RewardDestination; + pub Payee get(fn payee): map hasher(blake2_256) T::AccountId => RewardDestination; /// The map from (wannabe) validator stash key to the preferences of that validator. - pub Validators get(fn validators): linked_map T::AccountId => ValidatorPrefs; + pub Validators get(fn validators): + linked_map hasher(blake2_256) T::AccountId => ValidatorPrefs; /// The map from nominator stash key to the set of stash keys of all validators to nominate. /// /// NOTE: is private so that we can ensure upgraded before all typical accesses. /// Direct storage APIs can still bypass this protection. - Nominators get(fn nominators): linked_map T::AccountId => Option>; + Nominators get(fn nominators): + linked_map hasher(blake2_256) T::AccountId => Option>; /// Nominators for a particular account that is in action right now. You can't iterate /// through validators here, but you can find them in the Session module. /// /// This is keyed by the stash account. - pub Stakers get(fn stakers): map T::AccountId => Exposure>; + pub Stakers get(fn stakers): + map hasher(blake2_256) T::AccountId => Exposure>; /// The currently elected validator set keyed by stash account ID. pub CurrentElected get(fn current_elected): Vec; @@ -729,7 +732,8 @@ decl_storage! { pub CanceledSlashPayout get(fn canceled_payout) config(): BalanceOf; /// All unapplied slashes that are queued for later. - pub UnappliedSlashes: map EraIndex => Vec>>; + pub UnappliedSlashes: + map hasher(blake2_256) EraIndex => Vec>>; /// A mapping from still-bonded eras to the first session index of that era. BondedEras: Vec<(EraIndex, SessionIndex)>; @@ -737,25 +741,25 @@ decl_storage! { /// All slashing events on validators, mapped by era to the highest slash proportion /// and slash value of the era. ValidatorSlashInEra: - double_map EraIndex, hasher(twox_128) T::AccountId => Option<(Perbill, BalanceOf)>; + double_map hasher(blake2_256) EraIndex, hasher(twox_128) T::AccountId + => Option<(Perbill, BalanceOf)>; /// All slashing events on nominators, mapped by era to the highest slash value of the era. NominatorSlashInEra: - double_map EraIndex, hasher(twox_128) T::AccountId => Option>; + double_map hasher(blake2_256) EraIndex, hasher(twox_128) T::AccountId + => Option>; /// Slashing spans for stash accounts. - SlashingSpans: map T::AccountId => Option; + SlashingSpans: map hasher(blake2_256) T::AccountId => Option; /// Records information about the maximum slash of a stash within a slashing span, /// as well as how much reward has been paid out. SpanSlash: - map (T::AccountId, slashing::SpanIndex) => slashing::SpanRecord>; + map hasher(blake2_256) (T::AccountId, slashing::SpanIndex) + => slashing::SpanRecord>; /// The earliest era for which we have a pending, unapplied slash. EarliestUnappliedSlash: Option; - - /// The version of storage for upgrade. - StorageVersion: u32; } add_extra_genesis { config(stakers): @@ -787,8 +791,6 @@ decl_storage! { }, _ => Ok(()) }; } - - StorageVersion::put(migration::CURRENT_VERSION); }); } } @@ -878,13 +880,13 @@ decl_module! { ) { let stash = ensure_signed(origin)?; - if >::exists(&stash) { + if >::contains_key(&stash) { Err(Error::::AlreadyBonded)? } let controller = T::Lookup::lookup(controller)?; - if >::exists(&controller) { + if >::contains_key(&controller) { Err(Error::::AlreadyPaired)? } @@ -1130,7 +1132,7 @@ decl_module! { let stash = ensure_signed(origin)?; let old_controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; let controller = T::Lookup::lookup(controller)?; - if >::exists(&controller) { + if >::contains_key(&controller) { Err(Error::::AlreadyPaired)? } if controller != old_controller { @@ -1142,7 +1144,7 @@ decl_module! { } /// The ideal number of validators. - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(5_000)] fn set_validator_count(origin, #[compact] new: u32) { ensure_root(origin)?; ValidatorCount::put(new); @@ -1155,7 +1157,7 @@ decl_module! { /// # /// - No arguments. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(5_000)] fn force_no_eras(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNone); @@ -1167,21 +1169,21 @@ decl_module! { /// # /// - No arguments. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(5_000)] fn force_new_era(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNew); } /// Set the validators who cannot be slashed (if any). - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(5_000)] fn set_invulnerables(origin, validators: Vec) { ensure_root(origin)?; >::put(validators); } /// Force a current staker to become completely unstaked, immediately. - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] fn force_unstake(origin, stash: T::AccountId) { ensure_root(origin)?; @@ -1196,7 +1198,7 @@ decl_module! { /// # /// - One storage write /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(5_000)] fn force_new_era_always(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceAlways); @@ -1209,7 +1211,7 @@ decl_module! { /// # /// - One storage write. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(1_000_000)] fn cancel_deferred_slash(origin, era: EraIndex, slash_indices: Vec) { T::SlashCancelOrigin::try_origin(origin) .map(|_| ()) @@ -1278,7 +1280,6 @@ impl Module { STAKING_ID, &ledger.stash, ledger.total, - T::BlockNumber::max_value(), WithdrawReasons::all(), ); >::insert(controller, ledger); @@ -1291,9 +1292,10 @@ impl Module { } /// Ensures storage is upgraded to most recent necessary state. - fn ensure_storage_upgraded() { - migration::perform_migrations::(); - } + /// + /// Right now it's a no-op as all networks that are supported by Substrate Frame Core are + /// running with the latest staking storage scheme. + fn ensure_storage_upgraded() {} /// Actually make a payment to a staker. This uses the currency's reward function /// to pay the right payee for the given staker account. @@ -1495,7 +1497,7 @@ impl Module { }); all_nominators.extend(nominator_votes); - let maybe_phragmen_result = sp_phragmen::elect::<_, _, _, T::CurrencyToVote>( + let maybe_phragmen_result = sp_phragmen::elect::<_, _, _, T::CurrencyToVote, Perbill>( Self::validator_count() as usize, Self::minimum_validator_count().max(1) as usize, all_validators, @@ -1512,7 +1514,7 @@ impl Module { let to_balance = |e: ExtendedBalance| >>::convert(e); - let supports = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote>( + let supports = sp_phragmen::build_support_map::<_, _, _, T::CurrencyToVote, Perbill>( &elected_stashes, &assignments, Self::slashable_balance_of, @@ -1674,8 +1676,8 @@ impl SessionManager> } } -impl OnFreeBalanceZero for Module { - fn on_free_balance_zero(stash: &T::AccountId) { +impl OnReapAccount for Module { + fn on_reap_account(stash: &T::AccountId) { Self::ensure_storage_upgraded(); Self::kill_stash(stash); } diff --git a/frame/staking/src/migration.rs b/frame/staking/src/migration.rs deleted file mode 100644 index 6cb472375a48d0753134e3262155d333c2a66de0..0000000000000000000000000000000000000000 --- a/frame/staking/src/migration.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Storage migrations for srml-staking. - -/// Indicator of a version of a storage layout. -pub type VersionNumber = u32; - -// the current expected version of the storage -pub const CURRENT_VERSION: VersionNumber = 2; - -/// The inner logic of migrations. -#[cfg(any(test, feature = "migrate"))] -pub mod inner { - use crate::{Store, Module, Trait}; - use frame_support::{StorageLinkedMap, StoragePrefixedMap, StorageValue}; - use codec::{Encode, Decode}; - use sp_std::vec::Vec; - use super::{CURRENT_VERSION, VersionNumber}; - - // the minimum supported version of the migration logic. - const MIN_SUPPORTED_VERSION: VersionNumber = 0; - - // migrate storage from v0 to v1. - // - // this upgrades the `Nominators` linked_map value type from `Vec` to - // `Option>` - pub fn to_v1(version: &mut VersionNumber) { - if *version != 0 { return } - *version += 1; - - let now = >::current_era(); - let res = as Store>::Nominators::translate::, _, _>( - |key| key, - |targets| crate::Nominations { - targets, - submitted_in: now, - suppressed: false, - }, - ); - - if let Err(e) = res { - frame_support::print("Encountered error in migration of Staking::Nominators map."); - if e.is_none() { - frame_support::print("Staking::Nominators map reinitialized"); - } - } - - frame_support::print("Finished migrating Staking storage to v1."); - } - - // migrate storage from v1 to v2: adds another field to the `SlashingSpans` - // struct. - pub fn to_v2(version: &mut VersionNumber) { - use crate::{EraIndex, slashing::SpanIndex}; - #[derive(Decode)] - struct V1SlashingSpans { - span_index: SpanIndex, - last_start: EraIndex, - prior: Vec, - } - - #[derive(Encode)] - struct V2SlashingSpans { - span_index: SpanIndex, - last_start: EraIndex, - last_nonzero_slash: EraIndex, - prior: Vec, - } - - if *version != 1 { return } - *version += 1; - - let prefix = as Store>::SlashingSpans::final_prefix(); - let mut current_key = prefix.to_vec(); - loop { - let maybe_next_key = sp_io::storage::next_key(¤t_key[..]) - .filter(|v| v.starts_with(&prefix[..])); - - match maybe_next_key { - Some(next_key) => { - let maybe_spans = sp_io::storage::get(&next_key[..]) - .and_then(|v| V1SlashingSpans::decode(&mut &v[..]).ok()); - if let Some(spans) = maybe_spans { - let new_val = V2SlashingSpans { - span_index: spans.span_index, - last_start: spans.last_start, - last_nonzero_slash: spans.last_start, - prior: spans.prior, - }.encode(); - - sp_io::storage::set(&next_key[..], &new_val[..]); - } - current_key = next_key; - } - None => break, - } - } - } - - pub(super) fn perform_migrations() { - as Store>::StorageVersion::mutate(|version| { - if *version < MIN_SUPPORTED_VERSION { - frame_support::print("Cannot migrate staking storage because version is less than\ - minimum."); - frame_support::print(*version); - return - } - - if *version == CURRENT_VERSION { return } - - to_v1::(version); - to_v2::(version); - }); - } -} - -#[cfg(not(any(test, feature = "migrate")))] -mod inner { - pub(super) fn perform_migrations() { } -} - -/// Perform all necessary storage migrations to get storage into the expected stsate for current -/// logic. No-op if fully upgraded. -pub(crate) fn perform_migrations() { - inner::perform_migrations::(); -} diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 8aa20c19c6e298cc03872e431a0a63e648cf03b3..c5f3ef250803a3d18c8f6649507b41335c3feede 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -138,22 +138,16 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); -} -parameter_types! { - pub const TransferFee: Balance = 0; - pub const CreationFee: Balance = 0; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Staking, Session); } impl pallet_balances::Trait for Test { type Balance = Balance; - type OnFreeBalanceZero = Staking; - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const Period: BlockNumber = 1; @@ -236,7 +230,7 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { - existential_deposit: 0, + existential_deposit: 1, validator_pool: false, nominate: true, validator_count: 2, @@ -293,7 +287,7 @@ impl ExtBuilder { pub fn build(self) -> sp_io::TestExternalities { self.set_associated_consts(); let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let balance_factor = if self.existential_deposit > 0 { + let balance_factor = if self.existential_deposit > 1 { 256 } else { 1 @@ -323,7 +317,6 @@ impl ExtBuilder { // This allow us to have a total_payout different from 0. (999, 1_000_000_000_000), ], - vesting: vec![], }.assimilate_storage(&mut storage); let stake_21 = if self.fair { 1000 } else { 2000 }; diff --git a/frame/staking/src/slashing.rs b/frame/staking/src/slashing.rs index df36b1c763c4b96d48c18c2d8bee955f5e6e195a..3c8f39501a25e8c8b1fe2306c4d8ceb596b5e457 100644 --- a/frame/staking/src/slashing.rs +++ b/frame/staking/src/slashing.rs @@ -52,7 +52,7 @@ use super::{ EraIndex, Trait, Module, Store, BalanceOf, Exposure, Perbill, SessionInterface, NegativeImbalanceOf, UnappliedSlash, }; -use sp_runtime::traits::{Zero, Saturating}; +use sp_runtime::{traits::{Zero, Saturating}, PerThing}; use frame_support::{ StorageMap, StorageDoubleMap, traits::{Currency, OnUnbalanced, Imbalance}, diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 43a2d0079164f304f0b1cfdb4ebb64ae0eba5da7..73f1afd6345adf88b01e5d1167218ecb6724c517 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -18,7 +18,6 @@ use super::*; use mock::*; -use codec::Encode; use sp_runtime::{assert_eq_error_rate, traits::{OnInitialize, BadOrigin}}; use sp_staking::offence::OffenceDetails; use frame_support::{ @@ -110,8 +109,8 @@ fn basic_setup_works() { assert_eq!(Staking::current_era(), 0); // Account 10 has `balance_factor` free balance - assert_eq!(Balances::free_balance(&10), 1); - assert_eq!(Balances::free_balance(&10), 1); + assert_eq!(Balances::free_balance(10), 1); + assert_eq!(Balances::free_balance(10), 1); // New era is not being forced assert_eq!(Staking::force_era(), Forcing::NotForcing); @@ -759,7 +758,7 @@ fn cannot_transfer_staked_balance() { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(10)); // Confirm account 11 has some free balance - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); // Confirm account 11 (via controller 10) is totally staked assert_eq!(Staking::stakers(&11).total, 1000); // Confirm account 11 cannot transfer as a result @@ -788,7 +787,7 @@ fn cannot_transfer_staked_balance_2() { // Confirm account 21 is stashed assert_eq!(Staking::bonded(&21), Some(20)); // Confirm account 21 has some free balance - assert_eq!(Balances::free_balance(&21), 2000); + assert_eq!(Balances::free_balance(21), 2000); // Confirm account 21 (via controller 20) is totally staked assert_eq!(Staking::stakers(&21).total, 1000); // Confirm account 21 can transfer at most 1000 @@ -811,7 +810,7 @@ fn cannot_reserve_staked_balance() { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(10)); // Confirm account 11 has some free balance - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); // Confirm account 11 (via controller 10) is totally staked assert_eq!(Staking::stakers(&11).own, 1000); // Confirm account 11 cannot transfer as a result @@ -838,9 +837,9 @@ fn reward_destination_works() { // Check that account 11 is a validator assert!(Staking::current_elected().contains(&11)); // Check the balance of the validator account - assert_eq!(Balances::free_balance(&10), 1); + assert_eq!(Balances::free_balance(10), 1); // Check the balance of the stash account - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); // Check how much is at stake assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, @@ -859,7 +858,7 @@ fn reward_destination_works() { // Check that RewardDestination is Staked (default) assert_eq!(Staking::payee(&11), RewardDestination::Staked); // Check that reward went to the stash account of validator - assert_eq!(Balances::free_balance(&11), 1000 + total_payout_0); + assert_eq!(Balances::free_balance(11), 1000 + total_payout_0); // Check that amount at stake increased accordingly assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, @@ -881,7 +880,7 @@ fn reward_destination_works() { // Check that RewardDestination is Stash assert_eq!(Staking::payee(&11), RewardDestination::Stash); // Check that reward went to the stash account - assert_eq!(Balances::free_balance(&11), 1000 + total_payout_0 + total_payout_1); + assert_eq!(Balances::free_balance(11), 1000 + total_payout_0 + total_payout_1); // Record this value let recorded_stash_balance = 1000 + total_payout_0 + total_payout_1; // Check that amount at stake is NOT increased @@ -896,7 +895,7 @@ fn reward_destination_works() { >::insert(&11, RewardDestination::Controller); // Check controller balance - assert_eq!(Balances::free_balance(&10), 1); + assert_eq!(Balances::free_balance(10), 1); // Compute total payout now for whole duration as other parameter won't change let total_payout_2 = current_total_payout_for_duration(3000); @@ -908,7 +907,7 @@ fn reward_destination_works() { // Check that RewardDestination is Controller assert_eq!(Staking::payee(&11), RewardDestination::Controller); // Check that reward went to the controller account - assert_eq!(Balances::free_balance(&10), 1 + total_payout_2); + assert_eq!(Balances::free_balance(10), 1 + total_payout_2); // Check that amount at stake is NOT increased assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, @@ -917,7 +916,7 @@ fn reward_destination_works() { unlocking: vec![], })); // Check that amount in staked account is NOT increased. - assert_eq!(Balances::free_balance(&11), recorded_stash_balance); + assert_eq!(Balances::free_balance(11), recorded_stash_balance); }); } @@ -977,7 +976,7 @@ fn bond_extra_works() { // See `bond_extra_and_withdraw_unbonded_works` for more details and updates on `Exposure`. ExtBuilder::default().build().execute_with(|| { // Check that account 10 is a validator - assert!(>::exists(11)); + assert!(>::contains_key(11)); // Check that account 10 is bonded to account 11 assert_eq!(Staking::bonded(&11), Some(10)); // Check how much is at stake @@ -1375,7 +1374,7 @@ fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment( // Confirm validator count is 2 assert_eq!(Staking::validator_count(), 2); // Confirm account 10 and 20 are validators - assert!(>::exists(&11) && >::exists(&21)); + assert!(>::contains_key(&11) && >::contains_key(&21)); assert_eq!(Staking::stakers(&11).total, 1000); assert_eq!(Staking::stakers(&21).total, 2000); @@ -1422,9 +1421,9 @@ fn on_free_balance_zero_stash_removes_validator() { // Tests that storage items are untouched when controller is empty ExtBuilder::default().existential_deposit(10).build().execute_with(|| { // Check the balance of the validator account - assert_eq!(Balances::free_balance(&10), 256); + assert_eq!(Balances::free_balance(10), 256); // Check the balance of the stash account - assert_eq!(Balances::free_balance(&11), 256000); + assert_eq!(Balances::free_balance(11), 256000); // Check these two accounts are bonded assert_eq!(Staking::bonded(&11), Some(10)); @@ -1433,24 +1432,24 @@ fn on_free_balance_zero_stash_removes_validator() { assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); // Check storage items that should be cleaned up - assert!(>::exists(&10)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); // Reduce free_balance of controller to 0 let _ = Balances::slash(&10, u64::max_value()); // Check the balance of the stash account has not been touched - assert_eq!(Balances::free_balance(&11), 256000); + assert_eq!(Balances::free_balance(11), 256000); // Check these two accounts are still bonded assert_eq!(Staking::bonded(&11), Some(10)); // Check storage items have not changed - assert!(>::exists(&10)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); // Reduce free_balance of stash to 0 let _ = Balances::slash(&11, u64::max_value()); @@ -1458,11 +1457,11 @@ fn on_free_balance_zero_stash_removes_validator() { assert_eq!(Balances::total_balance(&11), 0); // Check storage items do not exist - assert!(!>::exists(&10)); - assert!(!>::exists(&11)); - assert!(!>::exists(&11)); - assert!(!>::exists(&11)); - assert!(!>::exists(&11)); + assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); }); } @@ -1474,20 +1473,20 @@ fn on_free_balance_zero_stash_removes_nominator() { // Make 10 a nominator assert_ok!(Staking::nominate(Origin::signed(10), vec![20])); // Check that account 10 is a nominator - assert!(>::exists(11)); + assert!(>::contains_key(11)); // Check the balance of the nominator account - assert_eq!(Balances::free_balance(&10), 256); + assert_eq!(Balances::free_balance(10), 256); // Check the balance of the stash account - assert_eq!(Balances::free_balance(&11), 256000); + assert_eq!(Balances::free_balance(11), 256000); // Set payee information assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash)); // Check storage items that should be cleaned up - assert!(>::exists(&10)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); // Reduce free_balance of controller to 0 let _ = Balances::slash(&10, u64::max_value()); @@ -1495,15 +1494,15 @@ fn on_free_balance_zero_stash_removes_nominator() { assert_eq!(Balances::total_balance(&10), 0); // Check the balance of the stash account has not been touched - assert_eq!(Balances::free_balance(&11), 256000); + assert_eq!(Balances::free_balance(11), 256000); // Check these two accounts are still bonded assert_eq!(Staking::bonded(&11), Some(10)); // Check storage items have not changed - assert!(>::exists(&10)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); - assert!(>::exists(&11)); + assert!(>::contains_key(&10)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); + assert!(>::contains_key(&11)); // Reduce free_balance of stash to 0 let _ = Balances::slash(&11, u64::max_value()); @@ -1511,11 +1510,11 @@ fn on_free_balance_zero_stash_removes_nominator() { assert_eq!(Balances::total_balance(&11), 0); // Check storage items do not exist - assert!(!>::exists(&10)); - assert!(!>::exists(&11)); - assert!(!>::exists(&11)); - assert!(!>::exists(&11)); - assert!(!>::exists(&11)); + assert!(!>::contains_key(&10)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); + assert!(!>::contains_key(&11)); }); } @@ -1689,9 +1688,9 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() { assert_eq!(Staking::slot_stake(), 1); // Old ones are rewarded. - assert_eq!(Balances::free_balance(&10), init_balance_10 + total_payout_0 / 3); + assert_eq!(Balances::free_balance(10), init_balance_10 + total_payout_0 / 3); // no rewards paid to 2. This was initial election. - assert_eq!(Balances::free_balance(&2), init_balance_2); + assert_eq!(Balances::free_balance(2), init_balance_2); let total_payout_1 = current_total_payout_for_duration(3000); assert!(total_payout_1 > 100); // Test is meaningfull if reward something @@ -1701,7 +1700,7 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() { assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); assert_eq!(Staking::slot_stake(), 1); - assert_eq!(Balances::free_balance(&2), init_balance_2 + total_payout_1 / 3); + assert_eq!(Balances::free_balance(2), init_balance_2 + total_payout_1 / 3); assert_eq!( Balances::free_balance(&10), init_balance_10 + total_payout_0 / 3 + total_payout_1 / 3, @@ -1968,7 +1967,7 @@ fn offence_ensures_new_era_without_clobbering() { #[test] fn offence_deselects_validator_when_slash_is_zero() { ExtBuilder::default().build().execute_with(|| { - assert!(>::exists(11)); + assert!(>::contains_key(11)); on_offence_now( &[OffenceDetails { offender: ( @@ -1980,7 +1979,7 @@ fn offence_deselects_validator_when_slash_is_zero() { &[Perbill::from_percent(0)], ); assert_eq!(Staking::force_era(), Forcing::ForceNew); - assert!(!>::exists(11)); + assert!(!>::contains_key(11)); }); } @@ -2008,7 +2007,7 @@ fn slashing_performed_according_exposure() { ); // The stash account should be slashed for 250 (50% of 500). - assert_eq!(Balances::free_balance(&11), 1000 - 250); + assert_eq!(Balances::free_balance(11), 1000 - 250); }); } @@ -2017,7 +2016,7 @@ fn slash_in_old_span_does_not_deselect() { ExtBuilder::default().build().execute_with(|| { start_era(1); - assert!(>::exists(11)); + assert!(>::contains_key(11)); on_offence_now( &[OffenceDetails { offender: ( @@ -2029,13 +2028,13 @@ fn slash_in_old_span_does_not_deselect() { &[Perbill::from_percent(0)], ); assert_eq!(Staking::force_era(), Forcing::ForceNew); - assert!(!>::exists(11)); + assert!(!>::contains_key(11)); start_era(2); Staking::validate(Origin::signed(10), Default::default()).unwrap(); assert_eq!(Staking::force_era(), Forcing::NotForcing); - assert!(>::exists(11)); + assert!(>::contains_key(11)); start_era(3); @@ -2056,7 +2055,7 @@ fn slash_in_old_span_does_not_deselect() { // not for zero-slash. assert_eq!(Staking::force_era(), Forcing::NotForcing); - assert!(>::exists(11)); + assert!(>::contains_key(11)); on_offence_in_era( &[OffenceDetails { @@ -2066,13 +2065,14 @@ fn slash_in_old_span_does_not_deselect() { ), reporters: vec![], }], - &[Perbill::from_percent(100)], + // NOTE: A 100% slash here would clean up the account, causing de-registration. + &[Perbill::from_percent(95)], 1, ); // or non-zero. assert_eq!(Staking::force_era(), Forcing::NotForcing); - assert!(>::exists(11)); + assert!(>::contains_key(11)); assert_ledger_consistent(11); }); } @@ -2102,8 +2102,8 @@ fn reporters_receive_their_slice() { // 50% * (10% * initial_balance / 2) let reward = (initial_balance / 20) / 2; let reward_each = reward / 2; // split into two pieces. - assert_eq!(Balances::free_balance(&1), 10 + reward_each); - assert_eq!(Balances::free_balance(&2), 20 + reward_each); + assert_eq!(Balances::free_balance(1), 10 + reward_each); + assert_eq!(Balances::free_balance(2), 20 + reward_each); assert_ledger_consistent(11); }); } @@ -2132,7 +2132,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { // F1 * (reward_proportion * slash - 0) // 50% * (10% * initial_balance * 20%) let reward = (initial_balance / 5) / 20; - assert_eq!(Balances::free_balance(&1), 10 + reward); + assert_eq!(Balances::free_balance(1), 10 + reward); on_offence_now( &[OffenceDetails { @@ -2150,7 +2150,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { // F1 * (reward_proportion * slash - prior_payout) // 50% * (10% * (initial_balance / 2) - prior_payout) let reward = ((initial_balance / 20) - prior_payout) / 2; - assert_eq!(Balances::free_balance(&1), 10 + prior_payout + reward); + assert_eq!(Balances::free_balance(1), 10 + prior_payout + reward); assert_ledger_consistent(11); }); } @@ -2159,8 +2159,8 @@ fn subsequent_reports_in_same_span_pay_out_less() { fn invulnerables_are_not_slashed() { // For invulnerable validators no slashing is performed. ExtBuilder::default().invulnerables(vec![11]).build().execute_with(|| { - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&21), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(21), 2000); let exposure = Staking::stakers(&21); let initial_balance = Staking::slashable_balance_of(&21); @@ -2183,9 +2183,9 @@ fn invulnerables_are_not_slashed() { ); // The validator 11 hasn't been slashed, but 21 has been. - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); // 2000 - (0.2 * initial_balance) - assert_eq!(Balances::free_balance(&21), 2000 - (2 * initial_balance / 10)); + assert_eq!(Balances::free_balance(21), 2000 - (2 * initial_balance / 10)); // ensure that nominators were slashed as well. for (initial_balance, other) in nominator_balances.into_iter().zip(exposure.others) { @@ -2203,7 +2203,7 @@ fn invulnerables_are_not_slashed() { fn dont_slash_if_fraction_is_zero() { // Don't slash if the fraction is zero. ExtBuilder::default().build().execute_with(|| { - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); on_offence_now( &[OffenceDetails { @@ -2217,7 +2217,7 @@ fn dont_slash_if_fraction_is_zero() { ); // The validator hasn't been slashed. The new era is not forced. - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); assert_ledger_consistent(11); }); } @@ -2225,7 +2225,7 @@ fn dont_slash_if_fraction_is_zero() { #[test] fn only_slash_for_max_in_era() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); on_offence_now( &[ @@ -2238,7 +2238,7 @@ fn only_slash_for_max_in_era() { ); // The validator has been slashed and has been force-chilled. - assert_eq!(Balances::free_balance(&11), 500); + assert_eq!(Balances::free_balance(11), 500); assert_eq!(Staking::force_era(), Forcing::ForceNew); on_offence_now( @@ -2252,7 +2252,7 @@ fn only_slash_for_max_in_era() { ); // The validator has not been slashed additionally. - assert_eq!(Balances::free_balance(&11), 500); + assert_eq!(Balances::free_balance(11), 500); on_offence_now( &[ @@ -2265,15 +2265,15 @@ fn only_slash_for_max_in_era() { ); // The validator got slashed 10% more. - assert_eq!(Balances::free_balance(&11), 400); + assert_eq!(Balances::free_balance(11), 400); assert_ledger_consistent(11); }) } #[test] fn garbage_collection_after_slashing() { - ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - assert_eq!(Balances::free_balance(&11), 256_000); + ExtBuilder::default().existential_deposit(2).build().execute_with(|| { + assert_eq!(Balances::free_balance(11), 256_000); on_offence_now( &[ @@ -2285,7 +2285,7 @@ fn garbage_collection_after_slashing() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(&11), 256_000 - 25_600); + assert_eq!(Balances::free_balance(11), 256_000 - 25_600); assert!(::SlashingSpans::get(&11).is_some()); assert_eq!(::SpanSlash::get(&(11, 0)).amount_slashed(), &25_600); @@ -2302,7 +2302,7 @@ fn garbage_collection_after_slashing() { // validator and nominator slash in era are garbage-collected by era change, // so we don't test those here. - assert_eq!(Balances::free_balance(&11), 0); + assert_eq!(Balances::free_balance(11), 0); assert!(::SlashingSpans::get(&11).is_none()); assert_eq!(::SpanSlash::get(&(11, 0)).amount_slashed(), &0); }) @@ -2313,10 +2313,10 @@ fn garbage_collection_on_window_pruning() { ExtBuilder::default().build().execute_with(|| { start_era(1); - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); let exposure = Staking::stakers(&11); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( @@ -2331,8 +2331,8 @@ fn garbage_collection_on_window_pruning() { let now = Staking::current_era(); - assert_eq!(Balances::free_balance(&11), 900); - assert_eq!(Balances::free_balance(&101), 2000 - (nominated_value / 10)); + assert_eq!(Balances::free_balance(11), 900); + assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); assert!(::ValidatorSlashInEra::get(&now, &11).is_some()); assert!(::NominatorSlashInEra::get(&now, &101).is_some()); @@ -2357,15 +2357,15 @@ fn slashing_nominators_by_span_max() { start_era(2); start_era(3); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&21), 2000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(21), 2000); + assert_eq!(Balances::free_balance(101), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); let exposure_11 = Staking::stakers(&11); let exposure_21 = Staking::stakers(&21); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); let nominated_value_11 = exposure_11.others.iter().find(|o| o.who == 101).unwrap().value; let nominated_value_21 = exposure_21.others.iter().find(|o| o.who == 101).unwrap().value; @@ -2380,10 +2380,10 @@ fn slashing_nominators_by_span_max() { 2, ); - assert_eq!(Balances::free_balance(&11), 900); + assert_eq!(Balances::free_balance(11), 900); let slash_1_amount = Perbill::from_percent(10) * nominated_value_11; - assert_eq!(Balances::free_balance(&101), 2000 - slash_1_amount); + assert_eq!(Balances::free_balance(101), 2000 - slash_1_amount); let expected_spans = vec![ slashing::SlashingSpan { index: 1, start: 4, length: None }, @@ -2415,14 +2415,14 @@ fn slashing_nominators_by_span_max() { ); // 11 was not further slashed, but 21 and 101 were. - assert_eq!(Balances::free_balance(&11), 900); - assert_eq!(Balances::free_balance(&21), 1700); + assert_eq!(Balances::free_balance(11), 900); + assert_eq!(Balances::free_balance(21), 1700); let slash_2_amount = Perbill::from_percent(30) * nominated_value_21; assert!(slash_2_amount > slash_1_amount); // only the maximum slash in a single span is taken. - assert_eq!(Balances::free_balance(&101), 2000 - slash_2_amount); + assert_eq!(Balances::free_balance(101), 2000 - slash_2_amount); // third slash: in same era and on same validator as first, higher // in-era value, but lower slash value than slash 2. @@ -2438,15 +2438,15 @@ fn slashing_nominators_by_span_max() { ); // 11 was further slashed, but 21 and 101 were not. - assert_eq!(Balances::free_balance(&11), 800); - assert_eq!(Balances::free_balance(&21), 1700); + assert_eq!(Balances::free_balance(11), 800); + assert_eq!(Balances::free_balance(21), 1700); let slash_3_amount = Perbill::from_percent(20) * nominated_value_21; assert!(slash_3_amount < slash_2_amount); assert!(slash_3_amount > slash_1_amount); // only the maximum slash in a single span is taken. - assert_eq!(Balances::free_balance(&101), 2000 - slash_2_amount); + assert_eq!(Balances::free_balance(101), 2000 - slash_2_amount); }); } @@ -2457,7 +2457,7 @@ fn slashes_are_summed_across_spans() { start_era(2); start_era(3); - assert_eq!(Balances::free_balance(&21), 2000); + assert_eq!(Balances::free_balance(21), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); let get_span = |account| ::SlashingSpans::get(&account).unwrap(); @@ -2478,7 +2478,7 @@ fn slashes_are_summed_across_spans() { ]; assert_eq!(get_span(21).iter().collect::>(), expected_spans); - assert_eq!(Balances::free_balance(&21), 1900); + assert_eq!(Balances::free_balance(21), 1900); // 21 has been force-chilled. re-signal intent to validate. Staking::validate(Origin::signed(20), Default::default()).unwrap(); @@ -2504,7 +2504,7 @@ fn slashes_are_summed_across_spans() { ]; assert_eq!(get_span(21).iter().collect::>(), expected_spans); - assert_eq!(Balances::free_balance(&21), 1810); + assert_eq!(Balances::free_balance(21), 1810); }); } @@ -2513,10 +2513,10 @@ fn deferred_slashes_are_deferred() { ExtBuilder::default().slash_defer_duration(2).build().execute_with(|| { start_era(1); - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); let exposure = Staking::stakers(&11); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( @@ -2529,25 +2529,25 @@ fn deferred_slashes_are_deferred() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); start_era(2); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); start_era(3); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. start_era(4); - assert_eq!(Balances::free_balance(&11), 900); - assert_eq!(Balances::free_balance(&101), 2000 - (nominated_value / 10)); + assert_eq!(Balances::free_balance(11), 900); + assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); }) } @@ -2556,10 +2556,10 @@ fn remove_deferred() { ExtBuilder::default().slash_defer_duration(2).build().execute_with(|| { start_era(1); - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); let exposure = Staking::stakers(&11); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( @@ -2572,8 +2572,8 @@ fn remove_deferred() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); start_era(2); @@ -2590,21 +2590,21 @@ fn remove_deferred() { Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0]).unwrap(); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); start_era(3); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. start_era(4); // the first slash for 10% was cancelled, so no effect. - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); start_era(5); @@ -2616,8 +2616,8 @@ fn remove_deferred() { let actual_slash = total_slash - initial_slash; // 5% slash (15 - 10) processed now. - assert_eq!(Balances::free_balance(&11), 950); - assert_eq!(Balances::free_balance(&101), 2000 - actual_slash); + assert_eq!(Balances::free_balance(11), 950); + assert_eq!(Balances::free_balance(101), 2000 - actual_slash); }) } @@ -2626,10 +2626,10 @@ fn remove_multi_deferred() { ExtBuilder::default().slash_defer_duration(2).build().execute_with(|| { start_era(1); - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); let exposure = Staking::stakers(&11); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); on_offence_now( &[ @@ -2670,22 +2670,15 @@ fn remove_multi_deferred() { }) } -#[test] -fn version_initialized() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(::StorageVersion::get(), crate::migration::CURRENT_VERSION); - }); -} - #[test] fn slash_kicks_validators_not_nominators() { ExtBuilder::default().build().execute_with(|| { start_era(1); - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); let exposure = Staking::stakers(&11); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( @@ -2698,8 +2691,8 @@ fn slash_kicks_validators_not_nominators() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(&11), 900); - assert_eq!(Balances::free_balance(&101), 2000 - (nominated_value / 10)); + assert_eq!(Balances::free_balance(11), 900); + assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); // This is the best way to check that the validator was chilled; `get` will // return default value. @@ -2716,65 +2709,15 @@ fn slash_kicks_validators_not_nominators() { }); } -#[test] -fn migration_v2() { - ExtBuilder::default().build().execute_with(|| { - use crate::{EraIndex, slashing::SpanIndex}; - - #[derive(Encode)] - struct V1SlashingSpans { - span_index: SpanIndex, - last_start: EraIndex, - prior: Vec, - } - - // inject old-style values directly into storage. - let set = |stash, spans: V1SlashingSpans| { - let key = ::SlashingSpans::hashed_key_for(stash); - sp_io::storage::set(&key, &spans.encode()); - }; - - let spans_11 = V1SlashingSpans { - span_index: 10, - last_start: 1, - prior: vec![0], - }; - - let spans_21 = V1SlashingSpans { - span_index: 1, - last_start: 5, - prior: vec![], - }; - - set(11, spans_11); - set(21, spans_21); - - ::StorageVersion::put(1); - - // perform migration. - crate::migration::inner::to_v2::(&mut 1); - - assert_eq!( - ::SlashingSpans::get(&11).unwrap().last_nonzero_slash(), - 1, - ); - - assert_eq!( - ::SlashingSpans::get(&21).unwrap().last_nonzero_slash(), - 5, - ); - }); -} - #[test] fn zero_slash_keeps_nominators() { ExtBuilder::default().build().execute_with(|| { start_era(1); - assert_eq!(Balances::free_balance(&11), 1000); + assert_eq!(Balances::free_balance(11), 1000); let exposure = Staking::stakers(&11); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(101), 2000); on_offence_now( &[ @@ -2786,8 +2729,8 @@ fn zero_slash_keeps_nominators() { &[Perbill::from_percent(0)], ); - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&101), 2000); + assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(Balances::free_balance(101), 2000); // This is the best way to check that the validator was chilled; `get` will // return default value. diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index c97d04f9c7ce1f64685c4a74c0ea6604bf4a8e47..ac91129c574ad41c3e11ad772979a7c8b880e19d 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-sudo" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index e49596b4bdb9bda1d2f676c31be978c8f884ac1a..e69840e1586670d26e543468876b41524d38c288 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -77,7 +77,6 @@ //! //! ## Related Modules //! -//! * [Consensus](../frame_consensus/index.html) //! * [Democracy](../pallet_democracy/index.html) //! //! [`Call`]: ./enum.Call.html @@ -120,7 +119,7 @@ decl_module! { /// - One DB write (event). /// - Unknown weight of derivative `proposal` execution. /// # - #[weight = SimpleDispatchInfo::FreeOperational] + #[weight = SimpleDispatchInfo::FixedNormal(50_000)] fn sudo(origin, proposal: Box) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index d8460ef7e8aeaff44fe0f08c9684f38ab2b93409..f0e9b53bf2907bfbead3a28ab717ac808f60ba3f 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -3,12 +3,13 @@ name = "frame-support" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4" serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] } -frame-metadata = { version = "10.0.0", default-features = false, path = "../metadata" } +frame-metadata = { version = "11.0.0", default-features = false, path = "../metadata" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } sp-io ={ path = "../../primitives/io", default-features = false } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 321002e08bdd78ea5d7652f4bf996d772f94e29d..06a84750b58a2126414cef31b1f7eb527b43bc2d 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-support-procedural" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 7e66610641dfa2df3b93860c352e9eccbfa89751..6c14f0ecfd73d5b25fcd5bcf6ce1fe547469c95e 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -20,9 +20,9 @@ use frame_support_procedural_tools::syn_ext as ext; use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes}; use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection}; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{Ident, Result}; +use syn::{Ident, Result, TypePath}; /// The fixed name of the system module. const SYSTEM_MODULE_NAME: &str = "System"; @@ -58,7 +58,7 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result},`", )) } }; @@ -68,25 +68,23 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result( runtime: &'a Ident, module_declarations: impl Iterator, scrate: &'a TokenStream2, + extrinsic: &TypePath, ) -> TokenStream2 { let modules_tokens = module_declarations .filter_map(|module_declaration| { @@ -236,7 +235,7 @@ fn decl_runtime_metadata<'a>( }); quote!( #scrate::impl_runtime_metadata!{ - for #runtime with modules + for #runtime with modules where Extrinsic = #extrinsic #(#modules_tokens)* } ) @@ -263,32 +262,60 @@ fn decl_outer_dispatch<'a>( ) } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum DeclOuterKind { - Event, - Origin, +fn decl_outer_origin<'a>( + runtime_name: &'a Ident, + module_declarations: impl Iterator, + system_name: &'a Ident, + scrate: &'a TokenStream2, +) -> syn::Result { + let mut modules_tokens = TokenStream2::new(); + for module_declaration in module_declarations { + match module_declaration.find_part("Origin") { + Some(module_entry) => { + let module = &module_declaration.module; + let instance = module_declaration.instance.as_ref(); + let generics = &module_entry.generics; + if instance.is_some() && generics.params.len() == 0 { + let msg = format!( + "Instantiable module with no generic `Origin` cannot \ + be constructed: module `{}` must have generic `Origin`", + module_declaration.name + ); + return Err(syn::Error::new(module_declaration.name.span(), msg)); + } + let tokens = quote!(#module #instance #generics ,); + modules_tokens.extend(tokens); + } + None => {} + } + } + + Ok(quote!( + #scrate::impl_outer_origin! { + pub enum Origin for #runtime_name where system = #system_name { + #modules_tokens + } + } + )) } -fn decl_outer_event_or_origin<'a>( +fn decl_outer_event<'a>( runtime_name: &'a Ident, module_declarations: impl Iterator, - system_name: &'a Ident, scrate: &'a TokenStream2, - kind: DeclOuterKind, ) -> syn::Result { let mut modules_tokens = TokenStream2::new(); - let kind_str = format!("{:?}", kind); for module_declaration in module_declarations { - match module_declaration.find_part(&kind_str) { + match module_declaration.find_part("Event") { Some(module_entry) => { let module = &module_declaration.module; let instance = module_declaration.instance.as_ref(); let generics = &module_entry.generics; if instance.is_some() && generics.params.len() == 0 { let msg = format!( - "Instantiable module with no generic `{}` cannot \ - be constructed: module `{}` must have generic `{}`", - kind_str, module_declaration.name, kind_str + "Instantiable module with no generic `Event` cannot \ + be constructed: module `{}` must have generic `Event`", + module_declaration.name, ); return Err(syn::Error::new(module_declaration.name.span(), msg)); } @@ -298,14 +325,10 @@ fn decl_outer_event_or_origin<'a>( None => {} } } - let macro_call = match kind { - DeclOuterKind::Event => quote!(#scrate::impl_outer_event!), - DeclOuterKind::Origin => quote!(#scrate::impl_outer_origin!), - }; - let enum_name = Ident::new(kind_str.as_str(), Span::call_site()); + Ok(quote!( - #macro_call { - pub enum #enum_name for #runtime_name where system = #system_name { + #scrate::impl_outer_event! { + pub enum Event for #runtime_name { #modules_tokens } } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 7c27bc2a04dcb185b9491727edcab5c8402fe4ed..b4376915a2e0ec2e3e223a7819c372b53f441961 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -35,8 +35,8 @@ use proc_macro::TokenStream; /// decl_storage! { /// trait Store for Module as Example { /// Foo get(fn foo) config(): u32=12; -/// Bar: map u32 => u32; -/// pub Zed build(|config| vec![(0, 0)]): linked_map u32 => u32; +/// Bar: map hasher(blake2_256) u32 => u32; +/// pub Zed build(|config| vec![(0, 0)]): linked_map hasher(blake2_256) u32 => u32; /// } /// } /// ``` @@ -74,7 +74,7 @@ use proc_macro::TokenStream; /// `$hash` representing a choice of hashing algorithms available in the /// [`Hashable`](../frame_support/trait.Hashable.html) trait. /// -/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher +/// `blake2_256` and `blake2_128_concat` are strong hasher. One should use another hasher /// with care, see generator documentation. /// /// The generator is implemented with: @@ -95,7 +95,7 @@ use proc_macro::TokenStream; /// `$hash` representing a choice of hashing algorithms available in the /// [`Hashable`](../frame_support/trait.Hashable.html) trait. /// -/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher +/// `blake2_256` and `blake2_128_concat` are strong hasher. One should use another hasher /// with care, see generator documentation. /// /// All key formatting logic can be accessed in a type-agnostic format via the @@ -126,13 +126,12 @@ use proc_macro::TokenStream; /// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be choosen with care, see /// generator documentation. /// -/// `hasher($hash1)` and `hasher($hash2) are optional and default to `blake2_256`. -/// One should use another hasher with care, see generator documentation. -/// -/// If the first key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used. +/// If the first key is untrusted, a cryptographic `hasher` such as `blake2_256` or +/// `blake2_128_concat` must be used. /// Otherwise, other values of all storage items can be compromised. /// -/// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used. +/// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` or +/// `blake2_128_concat` must be used. /// Otherwise, other items in storage with the same first key can be compromised. /// /// The generator is implemented with: diff --git a/frame/support/procedural/src/storage/parse.rs b/frame/support/procedural/src/storage/parse.rs index 5d91fbcc0e9123944729e917bcf708b53cd411c3..b503c2b81caea22255287666a74aaeb931c76c30 100644 --- a/frame/support/procedural/src/storage/parse.rs +++ b/frame/support/procedural/src/storage/parse.rs @@ -38,10 +38,37 @@ mod keyword { syn::custom_keyword!(hasher); } +/// Specific `Opt` to implement structure with optional parsing +#[derive(Debug, Clone)] +pub struct Opt

{ + pub inner: Option

, +} +impl syn::export::ToTokens for Opt

{ + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + if let Some(ref p) = self.inner { + p.to_tokens(tokens); + } + } +} + +macro_rules! impl_parse_for_opt { + ($struct:ident => $token:path) => { + impl syn::parse::Parse for Opt<$struct> { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + if input.peek($token) { + input.parse().map(|p| Opt { inner: Some(p) }) + } else { + Ok(Opt { inner: None }) + } + } + } + }; +} + /// Parsing usage only #[derive(Parse, ToTokens, Debug)] struct StorageDefinition { - pub hidden_crate: ext::Opt, + pub hidden_crate: Opt, pub visibility: syn::Visibility, pub trait_token: Token![trait], pub ident: Ident, @@ -62,7 +89,7 @@ struct StorageDefinition { pub crate_ident: Ident, pub where_clause: Option, pub content: ext::Braces>, - pub extra_genesis: ext::Opt, + pub extra_genesis: Opt, } #[derive(Parse, ToTokens, Debug)] @@ -70,6 +97,7 @@ struct SpecificHiddenCrate { pub keyword: keyword::hiddencrate, pub ident: ext::Parens, } +impl_parse_for_opt!(SpecificHiddenCrate => keyword::hiddencrate); #[derive(Parse, ToTokens, Debug)] struct AddExtraGenesis { @@ -77,17 +105,36 @@ struct AddExtraGenesis { pub content: ext::Braces, } +impl_parse_for_opt!(AddExtraGenesis => keyword::add_extra_genesis); + #[derive(Parse, ToTokens, Debug)] struct AddExtraGenesisContent { pub lines: ext::Punctuated, } -#[derive(Parse, ToTokens, Debug)] +#[derive(ToTokens, Debug)] enum AddExtraGenesisLineEnum { AddExtraGenesisLine(AddExtraGenesisLine), AddExtraGenesisBuild(DeclStorageBuild), } +impl syn::parse::Parse for AddExtraGenesisLineEnum { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + let input_fork = input.fork(); + // OuterAttributes are forbidden for build variant, + // However to have better documentation we match against the keyword after those attributes. + let _: ext::OuterAttributes = input_fork.parse()?; + let lookahead = input_fork.lookahead1(); + if lookahead.peek(keyword::build) { + Ok(Self::AddExtraGenesisBuild(input.parse()?)) + } else if lookahead.peek(keyword::config) { + Ok(Self::AddExtraGenesisLine(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} + #[derive(Parse, ToTokens, Debug)] struct AddExtraGenesisLine { pub attrs: ext::OuterAttributes, @@ -95,7 +142,7 @@ struct AddExtraGenesisLine { pub extra_field: ext::Parens, pub coldot_token: Token![:], pub extra_type: syn::Type, - pub default_value: ext::Opt, + pub default_value: Opt, } #[derive(Parse, ToTokens, Debug)] @@ -106,12 +153,12 @@ struct DeclStorageLine { pub visibility: syn::Visibility, // name pub name: Ident, - pub getter: ext::Opt, - pub config: ext::Opt, - pub build: ext::Opt, + pub getter: Opt, + pub config: Opt, + pub build: Opt, pub coldot_token: Token![:], pub storage_type: DeclStorageType, - pub default_value: ext::Opt, + pub default_value: Opt, } #[derive(Parse, ToTokens, Debug)] @@ -126,19 +173,25 @@ struct DeclStorageGetter { pub getfn: ext::Parens, } +impl_parse_for_opt!(DeclStorageGetter => keyword::get); + #[derive(Parse, ToTokens, Debug)] struct DeclStorageConfig { pub config_keyword: keyword::config, pub expr: ext::Parens>, } +impl_parse_for_opt!(DeclStorageConfig => keyword::config); + #[derive(Parse, ToTokens, Debug)] struct DeclStorageBuild { pub build_keyword: keyword::build, pub expr: ext::Parens, } -#[derive(Parse, ToTokens, Debug)] +impl_parse_for_opt!(DeclStorageBuild => keyword::build); + +#[derive(ToTokens, Debug)] enum DeclStorageType { Map(DeclStorageMap), LinkedMap(DeclStorageLinkedMap), @@ -146,10 +199,24 @@ enum DeclStorageType { Simple(syn::Type), } +impl syn::parse::Parse for DeclStorageType { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + if input.peek(keyword::map) { + Ok(Self::Map(input.parse()?)) + } else if input.peek(keyword::linked_map) { + Ok(Self::LinkedMap(input.parse()?)) + } else if input.peek(keyword::double_map) { + Ok(Self::DoubleMap(input.parse()?)) + } else { + Ok(Self::Simple(input.parse()?)) + } + } +} + #[derive(Parse, ToTokens, Debug)] struct DeclStorageMap { pub map_keyword: keyword::map, - pub hasher: ext::Opt, + pub hasher: Opt, pub key: syn::Type, pub ass_keyword: Token![=>], pub value: syn::Type, @@ -158,7 +225,7 @@ struct DeclStorageMap { #[derive(Parse, ToTokens, Debug)] struct DeclStorageLinkedMap { pub map_keyword: keyword::linked_map, - pub hasher: ext::Opt, + pub hasher: Opt, pub key: syn::Type, pub ass_keyword: Token![=>], pub value: syn::Type, @@ -167,16 +234,16 @@ struct DeclStorageLinkedMap { #[derive(Parse, ToTokens, Debug)] struct DeclStorageDoubleMap { pub map_keyword: keyword::double_map, - pub hasher1: ext::Opt, + pub hasher1: Opt, pub key1: syn::Type, pub comma_keyword: Token![,], - pub hasher2: ext::Opt, + pub hasher2: Opt, pub key2: syn::Type, pub ass_keyword: Token![=>], pub value: syn::Type, } -#[derive(Parse, ToTokens, Debug)] +#[derive(ToTokens, Debug)] enum Hasher { Blake2_256(keyword::blake2_256), Blake2_128(keyword::blake2_128), @@ -186,18 +253,51 @@ enum Hasher { Twox64Concat(keyword::twox_64_concat), } +impl syn::parse::Parse for Hasher { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keyword::blake2_256) { + Ok(Self::Blake2_256(input.parse()?)) + } else if lookahead.peek(keyword::blake2_128) { + Ok(Self::Blake2_128(input.parse()?)) + } else if lookahead.peek(keyword::blake2_128_concat) { + Ok(Self::Blake2_128Concat(input.parse()?)) + } else if lookahead.peek(keyword::twox_256) { + Ok(Self::Twox256(input.parse()?)) + } else if lookahead.peek(keyword::twox_128) { + Ok(Self::Twox128(input.parse()?)) + } else if lookahead.peek(keyword::twox_64_concat) { + Ok(Self::Twox64Concat(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} + #[derive(Parse, ToTokens, Debug)] struct DeclStorageDefault { pub equal_token: Token![=], pub expr: syn::Expr, } +impl syn::parse::Parse for Opt { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + if input.peek(Token![=]) { + input.parse().map(|p| Opt { inner: Some(p) }) + } else { + Ok(Opt { inner: None }) + } + } +} + #[derive(Parse, ToTokens, Debug)] struct SetHasher { pub hasher_keyword: keyword::hasher, pub inner: ext::Parens, } +impl_parse_for_opt!(SetHasher => keyword::hasher); + impl From for super::HasherKind { fn from(set_hasher: SetHasher) -> Self { set_hasher.inner.content.into() @@ -361,29 +461,31 @@ fn parse_storage_line_defs( })?; } + let span = line.storage_type.span(); + let no_hasher_error = || syn::Error::new( + span, + "Default hasher has been removed, use explicit hasher(blake2_256) instead." + ); + let storage_type = match line.storage_type { DeclStorageType::Map(map) => super::StorageLineTypeDef::Map( super::MapDef { - hasher: map.hasher.inner.map(Into::into) - .unwrap_or(super::HasherKind::Blake2_256), + hasher: map.hasher.inner.ok_or_else(no_hasher_error)?.into(), key: map.key, value: map.value, } ), DeclStorageType::LinkedMap(map) => super::StorageLineTypeDef::LinkedMap( super::MapDef { - hasher: map.hasher.inner.map(Into::into) - .unwrap_or(super::HasherKind::Blake2_256), + hasher: map.hasher.inner.ok_or_else(no_hasher_error)?.into(), key: map.key, value: map.value, } ), DeclStorageType::DoubleMap(map) => super::StorageLineTypeDef::DoubleMap( super::DoubleMapDef { - hasher1: map.hasher1.inner.map(Into::into) - .unwrap_or(super::HasherKind::Blake2_256), - hasher2: map.hasher2.inner.map(Into::into) - .unwrap_or(super::HasherKind::Blake2_256), + hasher1: map.hasher1.inner.ok_or_else(no_hasher_error)?.into(), + hasher2: map.hasher2.inner.ok_or_else(no_hasher_error)?.into(), key1: map.key1, key2: map.key2, value: map.value, diff --git a/frame/support/procedural/src/storage/storage_struct.rs b/frame/support/procedural/src/storage/storage_struct.rs index e44863b1579d5ee25ae094b73dfe004545ae5de1..91bdf7bf87c808ffd3451e29447b2944841aeef0 100644 --- a/frame/support/procedural/src/storage/storage_struct.rs +++ b/frame/support/procedural/src/storage/storage_struct.rs @@ -167,18 +167,6 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre ); quote!( - impl<#impl_trait> #scrate::storage::StoragePrefixedMap<#value_type> - for #storage_struct #optional_storage_where_clause - { - fn module_prefix() -> &'static [u8] { - #instance_or_inherent::PREFIX.as_bytes() - } - - fn storage_prefix() -> &'static [u8] { - #storage_name_str.as_bytes() - } - } - impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct #optional_storage_where_clause { diff --git a/frame/support/procedural/tools/Cargo.toml b/frame/support/procedural/tools/Cargo.toml index a9b8f6d4bfafd27efc81c1822759b68080ac2af5..4a1301e71261b63de2b7f002f135a8678a7f14fe 100644 --- a/frame/support/procedural/tools/Cargo.toml +++ b/frame/support/procedural/tools/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-support-procedural-tools" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] frame-support-procedural-tools-derive = { version = "2.0.0", path = "./derive" } diff --git a/frame/support/procedural/tools/derive/Cargo.toml b/frame/support/procedural/tools/derive/Cargo.toml index 49aea10807f1b2e44e5d9a99d7bbc21ba0f390d1..e65a7e0e5ff7b97b06d5a47ececbe8f8bbc5049e 100644 --- a/frame/support/procedural/tools/derive/Cargo.toml +++ b/frame/support/procedural/tools/derive/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-support-procedural-tools-derive" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true diff --git a/frame/support/procedural/tools/derive/src/lib.rs b/frame/support/procedural/tools/derive/src/lib.rs index e130ffbfa0e0c40d1e9265a43a495e83bf0fe708..f020c2a2fad125703ebc4ebd0116cfddc7718c68 100644 --- a/frame/support/procedural/tools/derive/src/lib.rs +++ b/frame/support/procedural/tools/derive/src/lib.rs @@ -52,18 +52,13 @@ pub(crate) fn fields_access( }) } -/// self defined parsing struct or enum. -/// not meant for any struct/enum, just for fast +/// self defined parsing struct. +/// not meant for any struct, just for fast /// parse implementation. -/// For enums: -/// variant are tested in order of definition. -/// Empty variant is always true. -/// Please use carefully, this will fully parse successful variant twice. #[proc_macro_derive(Parse)] pub fn derive_parse(input: TokenStream) -> TokenStream { let item = parse_macro_input!(input as syn::Item); match item { - syn::Item::Enum(input) => derive_parse_enum(input), syn::Item::Struct(input) => derive_parse_struct(input), _ => TokenStream::new(), // ignore } @@ -100,71 +95,6 @@ fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream { tokens.into() } -fn derive_parse_enum(input: syn::ItemEnum) -> TokenStream { - let syn::ItemEnum { - ident, - generics, - variants, - .. - } = input; - let variants = variants.iter().map(|v| { - let variant_ident = v.ident.clone(); - let fields_build = if v.fields.iter().count() > 0 { - let fields_id = fields_idents(v.fields.iter().map(Clone::clone)); - quote!( (#(#fields_id), *) ) - } else { - quote!() - }; - - let fields_procs = fields_idents(v.fields.iter().map(Clone::clone)) - .map(|fident| { - quote!{ - let mut #fident = match fork.parse() { - Ok(r) => r, - Err(_e) => break, - }; - } - }); - let fields_procs_again = fields_idents(v.fields.iter().map(Clone::clone)) - .map(|fident| { - quote!{ - #fident = input.parse().expect("was parsed just before"); - } - }); - - // double parse to update input cursor position - // next syn crate version should be checked for a way - // to copy position/state from a fork - quote!{ - let mut fork = input.fork(); - loop { - #(#fields_procs)* - #(#fields_procs_again)* - return Ok(#ident::#variant_ident#fields_build); - } - } - }); - - let tokens = quote! { - impl #generics syn::parse::Parse for #ident #generics { - fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { - #( - #variants - )* - // no early return from any variants - Err( - syn::parse::Error::new( - proc_macro2::Span::call_site(), - "derived enum no matching variants" - ) - ) - } - } - - }; - tokens.into() -} - /// self defined parsing struct or enum. /// not meant for any struct/enum, just for fast /// parse implementation. diff --git a/frame/support/procedural/tools/src/syn_ext.rs b/frame/support/procedural/tools/src/syn_ext.rs index d0a5066b8017ad38edd221418a7c3504e2d37a11..7b0a2cb93fa6b5707d5b190e2f0c37db1aedb47a 100644 --- a/frame/support/procedural/tools/src/syn_ext.rs +++ b/frame/support/procedural/tools/src/syn_ext.rs @@ -164,39 +164,6 @@ impl ToTokens for OuterAttributes { } } -#[derive(Debug)] -pub struct Opt

{ - pub inner: Option

, -} - -impl Parse for Opt

{ - // Note that it cost a double parsing (same as enum derive) - fn parse(input: ParseStream) -> Result { - let inner = match input.fork().parse::

() { - Ok(_item) => Some(input.parse().expect("Same parsing ran before")), - Err(_e) => None, - }; - - Ok(Opt { inner }) - } -} - -impl ToTokens for Opt

{ - fn to_tokens(&self, tokens: &mut TokenStream) { - if let Some(ref p) = self.inner { - p.to_tokens(tokens); - } - } -} - -impl Clone for Opt

{ - fn clone(&self) -> Self { - Self { - inner: self.inner.clone() - } - } -} - pub fn extract_type_option(typ: &syn::Type) -> Option { if let syn::Type::Path(ref path) = typ { let v = path.path.segments.last()?; diff --git a/frame/support/src/debug.rs b/frame/support/src/debug.rs index 0316fb979725c0d90bfdf842a294f2662e5c1b7d..29847b70e5ca6ab579fed7117c64fb3deedff07b 100644 --- a/frame/support/src/debug.rs +++ b/frame/support/src/debug.rs @@ -129,10 +129,12 @@ pub mod native { #[macro_export] macro_rules! runtime_print { ($($arg:tt)+) => { - use core::fmt::Write; - let mut w = $crate::debug::Writer::default(); - let _ = core::write!(&mut w, $($arg)+); - w.print(); + { + use core::fmt::Write; + let mut w = $crate::debug::Writer::default(); + let _ = core::write!(&mut w, $($arg)+); + w.print(); + } } } diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index b2d7e6e1d409e232335672cdc3b1894cc8633443..c2f42fd56124d2f49f6620b458c90a39e82b940c 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -28,6 +28,7 @@ pub use crate::weights::{ TransactionPriority, Weight, WeighBlock, PaysFee, }; pub use sp_runtime::{traits::Dispatchable, DispatchError, DispatchResult}; +pub use crate::traits::{CallMetadata, GetCallMetadata, GetCallName}; /// A type that cannot be instantiated. pub enum Never {} @@ -1302,42 +1303,56 @@ macro_rules! decl_module { for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo { - $( - if let $call_type::$fn_name($( ref $param_name ),*) = self { - let weight = >::weigh_data( - &$weight, - ($( $param_name, )*) - ); - let class = >::classify_dispatch( - &$weight, - ($( $param_name, )*) - ); - let pays_fee = >::pays_fee( - &$weight, - ($( $param_name, )*) - ); - return $crate::dispatch::DispatchInfo { weight, class, pays_fee }; - } - if let $call_type::__PhantomItem(_, _) = self { unreachable!("__PhantomItem should never be used.") } - )* - // Defensive only: this function must have already returned at this point. - // all dispatchable function will have a weight which has the `::default` - // implementation of `SimpleDispatchInfo`. Nonetheless, we create one if it does - // not exist. - let weight = >::weigh_data( - &$crate::dispatch::SimpleDispatchInfo::default(), - () - ); - let class = >::classify_dispatch( - &$crate::dispatch::SimpleDispatchInfo::default(), - () - ); - let pays_fee = >::pays_fee( - &$crate::dispatch::SimpleDispatchInfo::default(), - () - ); - $crate::dispatch::DispatchInfo { weight, class, pays_fee } + match *self { + $( + $call_type::$fn_name( $( ref $param_name ),* ) => { + let weight = >::weigh_data( + &$weight, + ($( $param_name, )*) + ); + let class = >::classify_dispatch( + &$weight, + ($( $param_name, )*) + ); + let pays_fee = >::pays_fee( + &$weight, + ($( $param_name, )*) + ); + $crate::dispatch::DispatchInfo { + weight, + class, + pays_fee, + } + }, + )* + $call_type::__PhantomItem(_, _) => unreachable!("__PhantomItem should never be used."), + } + } + } + // Implement GetCallName for the Call. + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::GetCallName + for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn get_call_name(&self) -> &'static str { + match *self { + $( + $call_type::$fn_name( $( ref $param_name ),* ) => { + // Don't generate any warnings for unused variables + let _ = ( $( $param_name ),* ); + stringify!($fn_name) + }, + )* + $call_type::__PhantomItem(_, _) => unreachable!("__PhantomItem should never be used."), + } + } + + fn get_call_names() -> &'static [&'static str] { + &[ + $( + stringify!($fn_name), + )* + ] } } @@ -1500,6 +1515,36 @@ macro_rules! impl_outer_dispatch { } } } + impl $crate::dispatch::GetCallMetadata for $call_type { + fn get_call_metadata(&self) -> $crate::dispatch::CallMetadata { + use $crate::dispatch::GetCallName; + match self { + $( $call_type::$camelcase(call) => { + let function_name = call.get_call_name(); + let pallet_name = stringify!($camelcase); + $crate::dispatch::CallMetadata { function_name, pallet_name } + }, )* + } + } + + fn get_module_names() -> &'static [&'static str] { + &[$( + stringify!($camelcase), + )*] + } + + fn get_call_names(module: &str) -> &'static [&'static str] { + use $crate::dispatch::{Callable, GetCallName}; + match module { + $( + stringify!($camelcase) => + <<$camelcase as Callable<$runtime>>::Call + as GetCallName>::get_call_names(), + )* + _ => unreachable!(), + } + } + } impl $crate::dispatch::Dispatchable for $call_type { type Origin = $origin; type Trait = $call_type; @@ -1871,6 +1916,7 @@ mod tests { use super::*; use crate::sp_runtime::traits::{OnInitialize, OnFinalize}; use crate::weights::{DispatchInfo, DispatchClass}; + use crate::traits::{CallMetadata, GetCallMetadata, GetCallName}; pub trait Trait: system::Trait + Sized where Self::AccountId: From { type Origin; @@ -2084,4 +2130,30 @@ mod tests { assert_eq!(>::on_finalize(2), 10); assert_eq!(>::on_finalize(3), 0); } + + #[test] + fn call_name() { + let name = Call::::aux_3().get_call_name(); + assert_eq!("aux_3", name); + } + + #[test] + fn call_metadata() { + let call = OuterCall::Test(Call::::aux_3()); + let metadata = call.get_call_metadata(); + let expected = CallMetadata { function_name: "aux_3".into(), pallet_name: "Test".into() }; + assert_eq!(metadata, expected); + } + + #[test] + fn get_call_names() { + let call_names = Call::::get_call_names(); + assert_eq!(["aux_0", "aux_1", "aux_2", "aux_3", "aux_4", "aux_5", "operational"], call_names); + } + + #[test] + fn get_module_names() { + let module_names = OuterCall::get_module_names(); + assert_eq!(["Test"], module_names); + } } diff --git a/frame/support/src/event.rs b/frame/support/src/event.rs index 3ff6d4ab45c2daa36b8473e54aa20b380a2981ad..d30e6ddeea0c7f5a7c4d131e5e197b8be528fc15 100644 --- a/frame/support/src/event.rs +++ b/frame/support/src/event.rs @@ -337,30 +337,14 @@ macro_rules! impl_outer_event { ( $(#[$attr:meta])* pub enum $name:ident for $runtime:ident { - $( $rest_event_without_system:tt )* + $( $rest_events:tt )* } ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; - system; - Modules { $( $rest_event_without_system )* }; - ; - ); - }; - ( - $(#[$attr:meta])* - pub enum $name:ident for $runtime:ident where system = $system:ident { - $( $rest_event_with_system:tt )* - } - ) => { - $crate::impl_outer_event!( - $( #[$attr] )*; - $name; - $runtime; - $system; - Modules { $( $rest_event_with_system )* }; + Modules { $( $rest_events )* }; ; ); }; @@ -369,7 +353,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident $instance:ident, $( $rest_event_generic_instance:tt )* @@ -380,7 +363,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_generic_instance )* }; $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>{ $instance },; ); @@ -390,7 +372,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident $instance:ident, $( $rest_event_instance:tt )* @@ -401,7 +382,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_instance )* }; $( $module_name::Event $( <$generic_param> )* $( { $generic_instance } )?, )* $module::Event { $instance },; ); @@ -411,7 +391,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident, $( $rest_event_generic:tt )* @@ -422,7 +401,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_generic )* }; $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>,; ); @@ -432,7 +410,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident, $( $rest_event_no_generic_no_instance:tt )* @@ -443,7 +420,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_no_generic_no_instance )* }; $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event,; ); @@ -454,7 +430,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules {}; $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; ) => { @@ -468,18 +443,12 @@ macro_rules! impl_outer_event { $(#[$attr])* #[allow(non_camel_case_types)] pub enum $name { - system($system::Event), $( [< $module_name $(_ $generic_instance )? >]( $module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? > ), )* } - impl From<$system::Event> for $name { - fn from(x: $system::Event) -> Self { - $name::system(x) - } - } $( impl From<$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >> for $name { fn from(x: $module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >) -> Self { @@ -505,7 +474,6 @@ macro_rules! impl_outer_event { $crate::__impl_outer_event_json_metadata!( $runtime; $name; - $system; $( $module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? > @@ -521,7 +489,6 @@ macro_rules! __impl_outer_event_json_metadata { ( $runtime:ident; $event_name:ident; - $system:ident; $( $module_name:ident::Event < $( $generic_params:path ),* > $( $instance:ident )?, )*; ) => { impl $runtime { @@ -530,22 +497,20 @@ macro_rules! __impl_outer_event_json_metadata { $crate::event::OuterEventMetadata { name: $crate::event::DecodeDifferent::Encode(stringify!($event_name)), events: $crate::event::DecodeDifferent::Encode(&[ - ("system", $crate::event::FnEncode($system::Event::metadata)) $( - , ( + ( stringify!($module_name), $crate::event::FnEncode( $module_name::Event ::< $( $generic_params ),* > ::metadata ) ) - )* + ),* ]) } } $crate::__impl_outer_event_json_metadata! { @DECL_MODULE_EVENT_FNS - $system <> ; $( $module_name < $( $generic_params ),* > $( $instance )? ; )* } } @@ -717,6 +682,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for TestRuntime { + system, event_module, event_module2, event_module3, @@ -727,7 +693,8 @@ mod tests { pub struct TestRuntime2; impl_outer_event! { - pub enum TestEventSystemRenamed for TestRuntime2 where system = system_renamed { + pub enum TestEventSystemRenamed for TestRuntime2 { + system_renamed, event_module, event_module2, event_module3, diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index ebfc7bbe97ebe6d4318f54a5b261d1e560160443..531f2557140e118c4b781d00b277682deaa905e2 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -67,7 +67,8 @@ pub mod traits; pub mod weights; pub use self::hash::{ - Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat, Blake2_128Concat, Hashable + Twox256, Twox128, Blake2_256, Blake2_128, Twox64Concat, Blake2_128Concat, Hashable, + StorageHasher }; pub use self::storage::{ StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap, StoragePrefixedMap @@ -253,20 +254,23 @@ mod tests { trait Store for Module as Example { pub Data get(fn data) build(|_| vec![(15u32, 42u64)]): linked_map hasher(twox_64_concat) u32 => u64; - pub OptionLinkedMap: linked_map u32 => Option; + pub OptionLinkedMap: linked_map hasher(blake2_256) u32 => Option; pub GenericData get(fn generic_data): linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber; pub GenericData2 get(fn generic_data2): - linked_map T::BlockNumber => Option; + linked_map hasher(blake2_256) T::BlockNumber => Option; pub GetterNoFnKeyword get(no_fn): Option; pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]): double_map hasher(twox_64_concat) u32, hasher(blake2_256) u32 => u64; pub GenericDataDM: - double_map T::BlockNumber, hasher(twox_128) T::BlockNumber => T::BlockNumber; + double_map hasher(blake2_256) T::BlockNumber, hasher(twox_128) T::BlockNumber + => T::BlockNumber; pub GenericData2DM: - double_map T::BlockNumber, hasher(twox_256) T::BlockNumber => Option; - pub AppendableDM: double_map u32, T::BlockNumber => Vec; + double_map hasher(blake2_256) T::BlockNumber, hasher(twox_256) T::BlockNumber + => Option; + pub AppendableDM: + double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Vec; } } diff --git a/frame/support/src/metadata.rs b/frame/support/src/metadata.rs index 110df87c644b6ff233e0a508d4ed698d314c7cdf..46662e53548a51d8cda0def166d2126a7c0c2216 100644 --- a/frame/support/src/metadata.rs +++ b/frame/support/src/metadata.rs @@ -17,7 +17,8 @@ pub use frame_metadata::{ DecodeDifferent, FnEncode, RuntimeMetadata, ModuleMetadata, RuntimeMetadataLastVersion, DefaultByteGetter, RuntimeMetadataPrefixed, StorageEntryMetadata, StorageMetadata, - StorageEntryType, StorageEntryModifier, DefaultByte, StorageHasher, ModuleErrorMetadata + StorageEntryType, StorageEntryModifier, DefaultByte, StorageHasher, ModuleErrorMetadata, + ExtrinsicMetadata, }; /// Implements the metadata support for the given runtime and all its modules. @@ -43,10 +44,12 @@ pub use frame_metadata::{ ///# type Origin = u32; ///# type BlockNumber = u32; ///# } +///# +///# type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic<(), (), (), ()>; /// /// struct Runtime; /// frame_support::impl_runtime_metadata! { -/// for Runtime with modules +/// for Runtime with modules where Extrinsic = UncheckedExtrinsic /// module0::Module as Module0 with, /// module1::Module as Module1 with, /// module2::Module as Module2 with Storage, @@ -57,13 +60,24 @@ pub use frame_metadata::{ #[macro_export] macro_rules! impl_runtime_metadata { ( - for $runtime:ident with modules + for $runtime:ident with modules where Extrinsic = $ext:ident $( $rest:tt )* ) => { impl $runtime { pub fn metadata() -> $crate::metadata::RuntimeMetadataPrefixed { $crate::metadata::RuntimeMetadataLastVersion { modules: $crate::__runtime_modules_to_metadata!($runtime;; $( $rest )*), + extrinsic: $crate::metadata::ExtrinsicMetadata { + version: <$ext as $crate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + signed_extensions: < + < + $ext as $crate::sp_runtime::traits::ExtrinsicMetadata + >::SignedExtensions as $crate::sp_runtime::traits::SignedExtension + >::identifier() + .into_iter() + .map($crate::metadata::DecodeDifferent::Encode) + .collect(), + }, }.into() } } @@ -232,10 +246,46 @@ mod tests { use frame_metadata::{ EventMetadata, StorageEntryModifier, StorageEntryType, FunctionMetadata, StorageEntryMetadata, ModuleMetadata, RuntimeMetadataPrefixed, DefaultByte, ModuleConstantMetadata, DefaultByteGetter, - ErrorMetadata, + ErrorMetadata, ExtrinsicMetadata, }; use codec::{Encode, Decode}; use crate::traits::Get; + use sp_runtime::transaction_validity::TransactionValidityError; + + #[derive(Clone, Eq, Debug, PartialEq, Encode, Decode)] + struct TestExtension; + impl sp_runtime::traits::SignedExtension for TestExtension { + type AccountId = u32; + type Call = u32; + type AdditionalSigned = u32; + type DispatchInfo = (); + type Pre = (); + const IDENTIFIER: &'static str = "testextension"; + fn additional_signed(&self) -> Result { + Ok(1) + } + } + + #[derive(Clone, Eq, Debug, PartialEq, Encode, Decode)] + struct TestExtension2; + impl sp_runtime::traits::SignedExtension for TestExtension2 { + type AccountId = u32; + type Call = u32; + type AdditionalSigned = u32; + type DispatchInfo = (); + type Pre = (); + const IDENTIFIER: &'static str = "testextension2"; + fn additional_signed(&self) -> Result { + Ok(1) + } + } + + struct TestExtrinsic; + + impl sp_runtime::traits::ExtrinsicMetadata for TestExtrinsic { + const VERSION: u8 = 1; + type SignedExtensions = (TestExtension, TestExtension2); + } mod system { use super::*; @@ -354,6 +404,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for TestRuntime { + system, event_module, event_module2, } @@ -393,7 +444,7 @@ mod tests { } impl_runtime_metadata!( - for TestRuntime with modules + for TestRuntime with modules where Extrinsic = TestExtrinsic system::Module as System with Event, event_module::Module as Module with Event Call, event_module2::Module as Module2 with Event Storage Call, @@ -420,131 +471,138 @@ mod tests { } } - const EXPECTED_METADATA: RuntimeMetadataLastVersion = RuntimeMetadataLastVersion { - modules: DecodeDifferent::Encode(&[ - ModuleMetadata { - name: DecodeDifferent::Encode("System"), - storage: None, - calls: None, - event: Some(DecodeDifferent::Encode( - FnEncode(||&[ - EventMetadata { - name: DecodeDifferent::Encode("SystemEvent"), - arguments: DecodeDifferent::Encode(&[]), - documentation: DecodeDifferent::Encode(&[]) - } - ]) - )), - constants: DecodeDifferent::Encode( - FnEncode(|| &[ - ModuleConstantMetadata { - name: DecodeDifferent::Encode("BlockNumber"), - ty: DecodeDifferent::Encode("T::BlockNumber"), - value: DecodeDifferent::Encode( - DefaultByteGetter(&ConstantBlockNumberByteGetter) - ), - documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]), + #[test] + fn runtime_metadata() { + let expected_metadata: RuntimeMetadataLastVersion = RuntimeMetadataLastVersion { + modules: DecodeDifferent::Encode(&[ + ModuleMetadata { + name: DecodeDifferent::Encode("System"), + storage: None, + calls: None, + event: Some(DecodeDifferent::Encode( + FnEncode(||&[ + EventMetadata { + name: DecodeDifferent::Encode("SystemEvent"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]) + } + ]) + )), + constants: DecodeDifferent::Encode( + FnEncode(|| &[ + ModuleConstantMetadata { + name: DecodeDifferent::Encode("BlockNumber"), + ty: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode( + DefaultByteGetter(&ConstantBlockNumberByteGetter) + ), + documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]), + }, + ModuleConstantMetadata { + name: DecodeDifferent::Encode("GetType"), + ty: DecodeDifferent::Encode("T::AccountId"), + value: DecodeDifferent::Encode( + DefaultByteGetter(&ConstantGetTypeByteGetter) + ), + documentation: DecodeDifferent::Encode(&[]), + }, + ModuleConstantMetadata { + name: DecodeDifferent::Encode("ASSOCIATED_CONST"), + ty: DecodeDifferent::Encode("u64"), + value: DecodeDifferent::Encode( + DefaultByteGetter(&ConstantAssociatedConstByteGetter) + ), + documentation: DecodeDifferent::Encode(&[]), + } + ]) + ), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module"), + storage: None, + calls: Some( + DecodeDifferent::Encode(FnEncode(|| &[ + FunctionMetadata { + name: DecodeDifferent::Encode("aux_0"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + } + ]))), + event: Some(DecodeDifferent::Encode( + FnEncode(||&[ + EventMetadata { + name: DecodeDifferent::Encode("TestEvent"), + arguments: DecodeDifferent::Encode(&["Balance"]), + documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]) + } + ]) + )), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[ + ErrorMetadata { + name: DecodeDifferent::Encode("UserInputError"), + documentation: DecodeDifferent::Encode(&[" Some user input error"]), }, - ModuleConstantMetadata { - name: DecodeDifferent::Encode("GetType"), - ty: DecodeDifferent::Encode("T::AccountId"), - value: DecodeDifferent::Encode( - DefaultByteGetter(&ConstantGetTypeByteGetter) - ), - documentation: DecodeDifferent::Encode(&[]), + ErrorMetadata { + name: DecodeDifferent::Encode("BadThingHappened"), + documentation: DecodeDifferent::Encode(&[ + " Something bad happened", + " this could be due to many reasons", + ]), }, - ModuleConstantMetadata { - name: DecodeDifferent::Encode("ASSOCIATED_CONST"), - ty: DecodeDifferent::Encode("u64"), - value: DecodeDifferent::Encode( - DefaultByteGetter(&ConstantAssociatedConstByteGetter) - ), - documentation: DecodeDifferent::Encode(&[]), - } - ]) - ), - errors: DecodeDifferent::Encode(FnEncode(|| &[])), - }, - ModuleMetadata { - name: DecodeDifferent::Encode("Module"), - storage: None, - calls: Some( - DecodeDifferent::Encode(FnEncode(|| &[ - FunctionMetadata { - name: DecodeDifferent::Encode("aux_0"), - arguments: DecodeDifferent::Encode(&[]), - documentation: DecodeDifferent::Encode(&[]), - } - ]))), - event: Some(DecodeDifferent::Encode( - FnEncode(||&[ - EventMetadata { - name: DecodeDifferent::Encode("TestEvent"), - arguments: DecodeDifferent::Encode(&["Balance"]), - documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."]) - } - ]) - )), - constants: DecodeDifferent::Encode(FnEncode(|| &[])), - errors: DecodeDifferent::Encode(FnEncode(|| &[ - ErrorMetadata { - name: DecodeDifferent::Encode("UserInputError"), - documentation: DecodeDifferent::Encode(&[" Some user input error"]), - }, - ErrorMetadata { - name: DecodeDifferent::Encode("BadThingHappened"), - documentation: DecodeDifferent::Encode(&[ - " Something bad happened", - " this could be due to many reasons", - ]), - }, - ])), - }, - ModuleMetadata { - name: DecodeDifferent::Encode("Module2"), - storage: Some(DecodeDifferent::Encode( - FnEncode(|| StorageMetadata { - prefix: DecodeDifferent::Encode("TestStorage"), - entries: DecodeDifferent::Encode( - &[ - StorageEntryMetadata { - name: DecodeDifferent::Encode("StorageMethod"), - modifier: StorageEntryModifier::Optional, - ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), - default: DecodeDifferent::Encode( - DefaultByteGetter( - &event_module2::__GetByteStructStorageMethod( - std::marker::PhantomData:: + ])), + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module2"), + storage: Some(DecodeDifferent::Encode( + FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("TestStorage"), + entries: DecodeDifferent::Encode( + &[ + StorageEntryMetadata { + name: DecodeDifferent::Encode("StorageMethod"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter( + &event_module2::__GetByteStructStorageMethod( + std::marker::PhantomData:: + ) ) - ) - ), - documentation: DecodeDifferent::Encode(&[]), - } - ] - ) - }), - )), - calls: Some(DecodeDifferent::Encode(FnEncode(|| &[]))), - event: Some(DecodeDifferent::Encode( - FnEncode(||&[ - EventMetadata { - name: DecodeDifferent::Encode("TestEvent"), - arguments: DecodeDifferent::Encode(&["Balance"]), - documentation: DecodeDifferent::Encode(&[]) - } - ]) - )), - constants: DecodeDifferent::Encode(FnEncode(|| &[])), - errors: DecodeDifferent::Encode(FnEncode(|| &[])), - }, - ]) - }; + ), + documentation: DecodeDifferent::Encode(&[]), + } + ] + ) + }), + )), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[]))), + event: Some(DecodeDifferent::Encode( + FnEncode(||&[ + EventMetadata { + name: DecodeDifferent::Encode("TestEvent"), + arguments: DecodeDifferent::Encode(&["Balance"]), + documentation: DecodeDifferent::Encode(&[]) + } + ]) + )), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + }, + ]), + extrinsic: ExtrinsicMetadata { + version: 1, + signed_extensions: vec![ + DecodeDifferent::Encode("testextension"), + DecodeDifferent::Encode("testextension2"), + ], + } + }; - #[test] - fn runtime_metadata() { let metadata_encoded = TestRuntime::metadata().encode(); let metadata_decoded = RuntimeMetadataPrefixed::decode(&mut &metadata_encoded[..]); - let expected_metadata: RuntimeMetadataPrefixed = EXPECTED_METADATA.into(); + let expected_metadata: RuntimeMetadataPrefixed = expected_metadata.into(); pretty_assertions::assert_eq!(expected_metadata, metadata_decoded.unwrap()); } diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index 947235c4bd44fcb69b30738b251f2a19f614fb93..ff73fdbedf8f8a2ba59056029510c06258e8be66 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -110,7 +110,7 @@ where Self::storage_double_map_final_key(k1, k2) } - fn exists(k1: KArg1, k2: KArg2) -> bool + fn contains_key(k1: KArg1, k2: KArg2) -> bool where KArg1: EncodeLike, KArg2: EncodeLike, diff --git a/frame/support/src/storage/generator/linked_map.rs b/frame/support/src/storage/generator/linked_map.rs index 14bef77c9e40bcfe5a5acc6461d11520f313109f..2d0df8fcc810c99512b7b8c5b80bcecf07444d2b 100644 --- a/frame/support/src/storage/generator/linked_map.rs +++ b/frame/support/src/storage/generator/linked_map.rs @@ -326,7 +326,7 @@ where type Enumerator = Enumerator; - fn exists>(key: KeyArg) -> bool { + fn contains_key>(key: KeyArg) -> bool { unhashed::exists(Self::storage_linked_map_final_key(key).as_ref()) } @@ -433,6 +433,8 @@ where } } + /// The translation happens in-place, new keys are inserted at the same time as old keys are + /// removed, thus new keys must not collide with still remaining old keys. fn translate(translate_key: TK, translate_val: TV) -> Result<(), Option> where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V { diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index 78f261e7daf487d5b5ae4e849bcd1a75887fc682..497b3fd4777d771ac053253d7639c56045c077d9 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -95,7 +95,7 @@ impl> storage::StorageMap } } - fn exists>(key: KeyArg) -> bool { + fn contains_key>(key: KeyArg) -> bool { unhashed::exists(Self::storage_map_final_key(key).as_ref()) } @@ -104,7 +104,7 @@ impl> storage::StorageMap } fn insert, ValArg: EncodeLike>(key: KeyArg, val: ValArg) { - unhashed::put(Self::storage_map_final_key(key).as_ref(), &val.borrow()) + unhashed::put(Self::storage_map_final_key(key).as_ref(), &val) } fn remove>(key: KeyArg) { @@ -117,12 +117,58 @@ impl> storage::StorageMap let ret = f(&mut val); match G::from_query_to_optional_value(val) { - Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()), + Some(ref val) => unhashed::put(final_key.as_ref(), &val), None => unhashed::kill(final_key.as_ref()), } ret } + fn mutate_exists, R, F: FnOnce(&mut Option) -> R>(key: KeyArg, f: F) -> R { + let final_key = Self::storage_map_final_key(key); + let mut val = unhashed::get(final_key.as_ref()); + + let ret = f(&mut val); + match val { + Some(ref val) => unhashed::put(final_key.as_ref(), &val), + None => unhashed::kill(final_key.as_ref()), + } + ret + } + + fn try_mutate, R, E, F: FnOnce(&mut Self::Query) -> Result>( + key: KeyArg, + f: F + ) -> Result { + let final_key = Self::storage_map_final_key(key); + let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref())); + + let ret = f(&mut val); + if ret.is_ok() { + match G::from_query_to_optional_value(val) { + Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()), + None => unhashed::kill(final_key.as_ref()), + } + } + ret + } + + fn try_mutate_exists, R, E, F: FnOnce(&mut Option) -> Result>( + key: KeyArg, + f: F + ) -> Result { + let final_key = Self::storage_map_final_key(key); + let mut val = unhashed::get(final_key.as_ref()); + + let ret = f(&mut val); + if ret.is_ok() { + match val { + Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()), + None => unhashed::kill(final_key.as_ref()), + } + } + ret + } + fn take>(key: KeyArg) -> Self::Query { let key = Self::storage_map_final_key(key); let value = unhashed::take(key.as_ref()); diff --git a/frame/support/src/storage/generator/mod.rs b/frame/support/src/storage/generator/mod.rs index 476c6d447205407898907d0cae2819c7a421a121..2bee76babdc40ca152b398f6aa750d976e829cb5 100644 --- a/frame/support/src/storage/generator/mod.rs +++ b/frame/support/src/storage/generator/mod.rs @@ -65,7 +65,7 @@ mod tests { crate::decl_storage! { trait Store for Module as Runtime { Value get(fn value) config(): (u64, u64); - NumberMap: linked_map NumberNumber => u64; + NumberMap: linked_map hasher(blake2_256) NumberNumber => u64; } } diff --git a/frame/support/src/storage/generator/value.rs b/frame/support/src/storage/generator/value.rs index 44641b85f7a3e32a50b4768cb8cd5ac71f64e430..4083576e298135df123052ea80ffda39ba8a6af6 100644 --- a/frame/support/src/storage/generator/value.rs +++ b/frame/support/src/storage/generator/value.rs @@ -66,6 +66,10 @@ impl> storage::StorageValue for G { G::from_optional_value_to_query(value) } + fn try_get() -> Result { + unhashed::get(&Self::storage_value_final_key()).ok_or(()) + } + fn translate) -> Option>(f: F) -> Result, ()> { let key = Self::storage_value_final_key(); diff --git a/frame/support/src/storage/migration.rs b/frame/support/src/storage/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..e8d58b46d4c4f0e18a693ce50317bc0c89b65422 --- /dev/null +++ b/frame/support/src/storage/migration.rs @@ -0,0 +1,98 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Some utilities for helping access storage with arbitrary key types. + +use sp_std::prelude::*; +use codec::{Encode, Decode}; +use crate::{StorageHasher, Twox128}; + +/// Utility to iterate through raw items in storage. +pub struct StorageIterator { + prefix: [u8; 32], + previous_key: Vec, + drain: bool, + _phantom: ::sp_std::marker::PhantomData, +} + +impl StorageIterator { + /// Construct iterator to iterate over map items in `module` for the map called `item`. + pub fn new(module: &[u8], item: &[u8]) -> Self { + let mut prefix = [0u8; 32]; + prefix[0..16].copy_from_slice(&Twox128::hash(module)); + prefix[16..32].copy_from_slice(&Twox128::hash(item)); + Self { prefix, previous_key: prefix[..].to_vec(), drain: false, _phantom: Default::default() } + } + /// Mutate this iterator into a draining iterator; items iterated are removed from storage. + pub fn drain(mut self) -> Self { + self.drain = true; + self + } +} + +impl Iterator for StorageIterator { + type Item = (Vec, T); + + fn next(&mut self) -> Option<(Vec, T)> { + loop { + let maybe_next = sp_io::storage::next_key(&self.previous_key) + .filter(|n| n.starts_with(&self.prefix)); + break match maybe_next { + Some(next) => { + self.previous_key = next.clone(); + let maybe_value = frame_support::storage::unhashed::get::(&next); + match maybe_value { + Some(value) => { + if self.drain { + frame_support::storage::unhashed::kill(&next); + } + Some((self.previous_key[32..].to_vec(), value)) + } + None => continue, + } + } + None => None, + } + } + } +} + +/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`. +pub fn get_storage_value(module: &[u8], item: &[u8], hash: &[u8]) -> Option { + let mut key = vec![0u8; 32 + hash.len()]; + key[0..16].copy_from_slice(&Twox128::hash(module)); + key[16..32].copy_from_slice(&Twox128::hash(item)); + key[32..].copy_from_slice(hash); + frame_support::storage::unhashed::get::(&key) +} + +/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`. +pub fn take_storage_value(module: &[u8], item: &[u8], hash: &[u8]) -> Option { + let mut key = vec![0u8; 32 + hash.len()]; + key[0..16].copy_from_slice(&Twox128::hash(module)); + key[16..32].copy_from_slice(&Twox128::hash(item)); + key[32..].copy_from_slice(hash); + frame_support::storage::unhashed::take::(&key) +} + +/// Put a particular value into storage by the `module`, the map's `item` name and the key `hash`. +pub fn put_storage_value(module: &[u8], item: &[u8], hash: &[u8], value: T) { + let mut key = vec![0u8; 32 + hash.len()]; + key[0..16].copy_from_slice(&Twox128::hash(module)); + key[16..32].copy_from_slice(&Twox128::hash(item)); + key[32..].copy_from_slice(hash); + frame_support::storage::unhashed::put(&key, &value); +} diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index f629ac3eb91a18b3d4a965c81c29d07c179bd8ef..4bca5ea402398c349642bb103ae30f89aab27c74 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -25,6 +25,7 @@ pub mod hashed; pub mod child; #[doc(hidden)] pub mod generator; +pub mod migration; /// A trait for working with macro-generated storage values under the substrate storage API. /// @@ -43,6 +44,10 @@ pub trait StorageValue { /// Load the value from the provided storage instance. fn get() -> Self::Query; + /// Try to get the underlying value from the provided storage instance; `Ok` if it exists, + /// `Err` if not. + fn try_get() -> Result; + /// Translate a value from some previous type (`O`) to the current type. /// /// `f: F` is the translation function. @@ -126,7 +131,7 @@ pub trait StorageMap { fn hashed_key_for>(key: KeyArg) -> Vec; /// Does the value (explicitly) exist in storage? - fn exists>(key: KeyArg) -> bool; + fn contains_key>(key: KeyArg) -> bool; /// Load the value associated with the given key from the map. fn get>(key: KeyArg) -> Self::Query; @@ -143,14 +148,28 @@ pub trait StorageMap { /// Mutate the value under a key. fn mutate, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R; + /// Mutate the item, only if an `Ok` value is returned. + fn try_mutate, R, E, F: FnOnce(&mut Self::Query) -> Result>( + key: KeyArg, + f: F, + ) -> Result; + + /// Mutate the value under a key. Deletes the item if mutated to a `None`. + fn mutate_exists, R, F: FnOnce(&mut Option) -> R>(key: KeyArg, f: F) -> R; + + /// Mutate the item, only if an `Ok` value is returned. Deletes the item if mutated to a `None`. + fn try_mutate_exists, R, E, F: FnOnce(&mut Option) -> Result>( + key: KeyArg, + f: F, + ) -> Result; + /// Take the value under a key. fn take>(key: KeyArg) -> Self::Query; /// Append the given items to the value in the storage. /// /// `V` is required to implement `codec::EncodeAppend`. - fn append(key: KeyArg, items: Items) -> Result<(), &'static str> - where + fn append(key: KeyArg, items: Items) -> Result<(), &'static str> where KeyArg: EncodeLike, Item: Encode, EncodeLikeItem: EncodeLike, @@ -162,8 +181,7 @@ pub trait StorageMap { /// old (presumably corrupt) value is replaced with the given `items`. /// /// `V` is required to implement `codec::EncodeAppend`. - fn append_or_insert(key: KeyArg, items: Items) - where + fn append_or_insert(key: KeyArg, items: Items) where KeyArg: EncodeLike, Item: Encode, EncodeLikeItem: EncodeLike, @@ -176,7 +194,7 @@ pub trait StorageMap { /// `T` is required to implement `Codec::DecodeLength`. /// /// Note that `0` is returned as the default value if no encoded value exists at the given key. - /// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()` + /// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()` /// function for this purpose. fn decode_len>(key: KeyArg) -> Result where V: codec::DecodeLength + Len; @@ -196,7 +214,7 @@ pub trait StorageLinkedMap { type Enumerator: Iterator; /// Does the value (explicitly) exist in storage? - fn exists>(key: KeyArg) -> bool; + fn contains_key>(key: KeyArg) -> bool; /// Load the value associated with the given key from the map. fn get>(key: KeyArg) -> Self::Query; @@ -227,7 +245,7 @@ pub trait StorageLinkedMap { /// `T` is required to implement `Codec::DecodeLength`. /// /// Note that `0` is returned as the default value if no encoded value exists at the given key. - /// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()` + /// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()` /// function for this purpose. fn decode_len>(key: KeyArg) -> Result where V: codec::DecodeLength + Len; @@ -270,7 +288,7 @@ pub trait StorageDoubleMap { KArg1: EncodeLike, KArg2: EncodeLike; - fn exists(k1: KArg1, k2: KArg2) -> bool + fn contains_key(k1: KArg1, k2: KArg2) -> bool where KArg1: EncodeLike, KArg2: EncodeLike; @@ -348,7 +366,7 @@ pub trait StorageDoubleMap { /// `V` is required to implement `Codec::DecodeLength`. /// /// Note that `0` is returned as the default value if no encoded value exists at the given key. - /// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()` + /// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()` /// function for this purpose. fn decode_len(key1: KArg1, key2: KArg2) -> Result where diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index cb2228392454f13b2037454f367fb3fdccb0a347..e12defcc54fc0f2d953c48bf5d95a5d6eff5e9eb 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -22,11 +22,121 @@ use sp_std::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug}; use codec::{FullCodec, Codec, Encode, Decode}; use sp_core::u32_trait::Value as U32; use sp_runtime::{ + RuntimeDebug, ConsensusEngineId, DispatchResult, DispatchError, - traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating, TrailingZeroInput}, + traits::{MaybeSerializeDeserialize, AtLeast32Bit, Saturating, TrailingZeroInput}, }; use crate::dispatch::Parameter; +use crate::storage::StorageMap; + +/// An abstraction of a value stored within storage, but possibly as part of a larger composite +/// item. +pub trait StoredMap { + /// Get the item, or its default if it doesn't yet exist; we make no distinction between the + /// two. + fn get(k: &K) -> T; + /// Get whether the item takes up any storage. If this is `false`, then `get` will certainly + /// return the `T::default()`. If `true`, then there is no implication for `get` (i.e. it + /// may return any value, including the default). + /// + /// NOTE: This may still be `true`, even after `remove` is called. This is the case where + /// a single storage entry is shared between multiple `StoredMap` items single, without + /// additional logic to enforce it, deletion of any one them doesn't automatically imply + /// deletion of them all. + fn is_explicit(k: &K) -> bool; + /// Mutate the item. + fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> R; + /// Mutate the item, removing or resetting to default value if it has been mutated to `None`. + fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> R; + /// Maybe mutate the item only if an `Ok` value is returned from `f`. Do nothing if an `Err` is + /// returned. It is removed or reset to default value if it has been mutated to `None` + fn try_mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> Result) -> Result; + /// Set the item to something new. + fn insert(k: &K, t: T) { Self::mutate(k, |i| *i = t); } + /// Remove the item or otherwise replace it with its default value; we don't care which. + fn remove(k: &K); +} + +/// A simple, generic one-parameter event notifier/handler. +pub trait Happened { + /// The thing happened. + fn happened(t: &T); +} + +/// A shim for placing around a storage item in order to use it as a `StoredValue`. Ideally this +/// wouldn't be needed as `StorageValue`s should blanket implement `StoredValue`s, however this +/// would break the ability to have custom impls of `StoredValue`. The other workaround is to +/// implement it directly in the macro. +/// +/// This form has the advantage that two additional types are provides, `Created` and `Removed`, +/// which are both generic events that can be tied to handlers to do something in the case of being +/// about to create an account where one didn't previously exist (at all; not just where it used to +/// be the default value), or where the account is being removed or reset back to the default value +/// where previously it did exist (though may have been in a default state). This works well with +/// system module's `CallOnCreatedAccount` and `CallKillAccount`. +pub struct StorageMapShim< + S, + Created, + Removed, + K, + T +>(sp_std::marker::PhantomData<(S, Created, Removed, K, T)>); +impl< + S: StorageMap, + Created: Happened, + Removed: Happened, + K: FullCodec, + T: FullCodec +> StoredMap for StorageMapShim { + fn get(k: &K) -> T { S::get(k) } + fn is_explicit(k: &K) -> bool { S::contains_key(k) } + fn insert(k: &K, t: T) { + S::insert(k, t); + if !S::contains_key(&k) { + Created::happened(k); + } + } + fn remove(k: &K) { + if S::contains_key(&k) { + Removed::happened(&k); + } + S::remove(k); + } + fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> R { + let r = S::mutate(k, f); + if !S::contains_key(&k) { + Created::happened(k); + } + r + } + fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> R { + let (existed, exists, r) = S::mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let r = f(maybe_value); + (existed, maybe_value.is_some(), r) + }); + if !existed && exists { + Created::happened(k); + } else if existed && !exists { + Removed::happened(k); + } + r + } + fn try_mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> Result) -> Result { + S::try_mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + f(maybe_value).map(|v| (existed, maybe_value.is_some(), v)) + }).map(|(existed, exists, v)| { + if !existed && exists { + Created::happened(k); + } else if existed && !exists { + Removed::happened(k); + } + v + }) + } +} /// Anything that can have a `::len()` method. pub trait Len { @@ -64,11 +174,23 @@ pub trait Contains { fn count() -> usize { Self::sorted_members().len() } } -/// The account with the given id was killed. +/// Determiner to say whether a given account is unused. +pub trait IsDeadAccount { + /// Is the given account dead? + fn is_dead_account(who: &AccountId) -> bool; +} + +impl IsDeadAccount for () { + fn is_dead_account(_who: &AccountId) -> bool { + true + } +} + +/// Handler for when a new account has been created. #[impl_trait_for_tuples::impl_for_tuples(30)] -pub trait OnFreeBalanceZero { - /// The account with the given id was killed. - fn on_free_balance_zero(who: &AccountId); +pub trait OnNewAccount { + /// A new account `who` has been registered. + fn on_new_account(who: &AccountId); } /// The account with the given id was reaped. @@ -78,14 +200,6 @@ pub trait OnReapAccount { fn on_reap_account(who: &AccountId); } -/// Outcome of a balance update. -pub enum UpdateBalanceOutcome { - /// Account balance was simply updated. - Updated, - /// The update led to killing the account. - AccountKilled, -} - /// A trait for finding the author of a block header based on the `PreRuntime` digests contained /// within it. pub trait FindAuthor { @@ -267,7 +381,7 @@ pub enum SignedImbalance>{ impl< P: Imbalance, N: Imbalance, - B: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default, + B: AtLeast32Bit + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default, > SignedImbalance { pub fn zero() -> Self { SignedImbalance::Positive(P::zero()) @@ -330,7 +444,7 @@ impl< /// Abstraction over a fungible assets system. pub trait Currency { /// The balance of an account. - type Balance: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default; + type Balance: AtLeast32Bit + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default; /// The opaque token type for an imbalance. This is returned by unbalanced operations /// and must be dealt with. It may be dropped but cannot be cloned. @@ -376,9 +490,7 @@ pub trait Currency { /// This is the only balance that matters in terms of most operations on tokens. It alone /// is used to determine the balance when in the contract execution environment. When this /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is - /// deleted: specifically `FreeBalance`. Further, the `OnFreeBalanceZero` callback - /// is invoked, giving a chance to external modules to clean up data associated with - /// the deleted account. + /// deleted: specifically `FreeBalance`. /// /// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. @@ -496,10 +608,15 @@ pub trait Currency { fn make_free_balance_be( who: &AccountId, balance: Self::Balance, - ) -> ( - SignedImbalance, - UpdateBalanceOutcome, - ); + ) -> SignedImbalance; +} + +/// Status of funds. +pub enum BalanceStatus { + /// Funds are free, as corresponding to `free` item in Balances. + Free, + /// Funds are reserved, as corresponding to `reserved` item in Balances. + Reserved, } /// A currency where funds can be reserved from the user. @@ -530,7 +647,6 @@ pub trait ReservableCurrency: Currency { /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. fn reserved_balance(who: &AccountId) -> Self::Balance; - /// Moves `value` from balance to reserved balance. /// /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will @@ -549,16 +665,18 @@ pub trait ReservableCurrency: Currency { /// invoke `on_reserved_too_low` and could reap the account. fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance; - /// Moves up to `value` from reserved balance of account `slashed` to free balance of account + /// Moves up to `value` from reserved balance of account `slashed` to balance of account /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be - /// returned. + /// returned. Funds will be placed in either the `free` balance or the `reserved` balance, + /// depending on the `status`. /// /// As much funds up to `value` will be deducted as possible. If this is less than `value`, /// then `Ok(non_zero)` will be returned. fn repatriate_reserved( slashed: &AccountId, beneficiary: &AccountId, - value: Self::Balance + value: Self::Balance, + status: BalanceStatus, ) -> result::Result; } @@ -581,7 +699,6 @@ pub trait LockableCurrency: Currency { id: LockIdentifier, who: &AccountId, amount: Self::Balance, - until: Self::Moment, reasons: WithdrawReasons, ); @@ -592,13 +709,11 @@ pub trait LockableCurrency: Currency { /// applies the most severe constraints of the two, while `set_lock` replaces the lock /// with the new parameters. As in, `extend_lock` will set: /// - maximum `amount` - /// - farthest duration (`until`) /// - bitwise mask of all `reasons` fn extend_lock( id: LockIdentifier, who: &AccountId, amount: Self::Balance, - until: Self::Moment, reasons: WithdrawReasons, ); @@ -609,26 +724,36 @@ pub trait LockableCurrency: Currency { ); } -/// A currency whose accounts can have balances which vest over time. -pub trait VestingCurrency: Currency { +/// A vesting schedule over a currency. This allows a particular currency to have vesting limits +/// applied to it. +pub trait VestingSchedule { /// The quantity used to denote time; usually just a `BlockNumber`. type Moment; + /// The currency that this schedule applies to. + type Currency: Currency; + /// Get the amount that is currently being vested and cannot be transferred out of this account. - fn vesting_balance(who: &AccountId) -> Self::Balance; + fn vesting_balance(who: &AccountId) -> >::Balance; /// Adds a vesting schedule to a given account. /// /// If there already exists a vesting schedule for the given account, an `Err` is returned /// and nothing is updated. + /// + /// Is a no-op if the amount to be vested is zero. + /// + /// NOTE: This doesn't alter the free balance of the account. fn add_vesting_schedule( who: &AccountId, - locked: Self::Balance, - per_block: Self::Balance, + locked: >::Balance, + per_block: >::Balance, starting_block: Self::Moment, ) -> DispatchResult; /// Remove a vesting schedule for a given account. + /// + /// NOTE: This doesn't alter the free balance of the account. fn remove_vesting_schedule(who: &AccountId); } @@ -644,7 +769,7 @@ bitmask! { TransactionPayment = 0b00000001, /// In order to transfer ownership. Transfer = 0b00000010, - /// In order to reserve some funds for a later return or repatriation + /// In order to reserve some funds for a later return or repatriation. Reserve = 0b00000100, /// In order to pay some other (higher-level) fees. Fee = 0b00001000, @@ -654,7 +779,7 @@ bitmask! { } pub trait Time { - type Moment: SimpleArithmetic + Parameter + Default + Copy; + type Moment: AtLeast32Bit + Parameter + Default + Copy; fn now() -> Self::Moment; } @@ -762,7 +887,10 @@ pub trait Randomness { /// Get a "random" value /// /// Being a deterministic blockchain, real randomness is difficult to come by. This gives you - /// something that approximates it. `subject` is a context identifier and allows you to get a + /// something that approximates it. At best, this will be randomness which was + /// hard to predict a long time ago, but that has become easy to predict recently. + /// + /// `subject` is a context identifier and allows you to get a /// different result to other callers of this function; use it like /// `random(&b"my context"[..])`. fn random(subject: &[u8]) -> Output; @@ -802,3 +930,30 @@ pub trait ModuleToIndex { impl ModuleToIndex for () { fn module_to_index() -> Option { Some(0) } } + +/// The function and pallet name of the Call. +#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug)] +pub struct CallMetadata { + /// Name of the function. + pub function_name: &'static str, + /// Name of the pallet to which the function belongs. + pub pallet_name: &'static str, +} + +/// Gets the function name of the Call. +pub trait GetCallName { + /// Return all function names. + fn get_call_names() -> &'static [&'static str]; + /// Return the function name of the Call. + fn get_call_name(&self) -> &'static str; +} + +/// Gets the metadata for the Call - function name and pallet name. +pub trait GetCallMetadata { + /// Return all module names. + fn get_module_names() -> &'static [&'static str]; + /// Return all function names for the given `module`. + fn get_call_names(module: &str) -> &'static [&'static str]; + /// Return a [`CallMetadata`], containing function and pallet name of the Call. + fn get_call_metadata(&self) -> CallMetadata; +} diff --git a/frame/support/src/weights.rs b/frame/support/src/weights.rs index e44ab16458836bd50503d3c1ee1fbd7d0842b5d7..34166c368c8dfa04d458f8613d1ad11eec5eb6d6 100644 --- a/frame/support/src/weights.rs +++ b/frame/support/src/weights.rs @@ -127,11 +127,10 @@ impl From for DispatchClass { match tx { SimpleDispatchInfo::FixedOperational(_) => DispatchClass::Operational, SimpleDispatchInfo::MaxOperational => DispatchClass::Operational, - SimpleDispatchInfo::FreeOperational => DispatchClass::Operational, SimpleDispatchInfo::FixedNormal(_) => DispatchClass::Normal, SimpleDispatchInfo::MaxNormal => DispatchClass::Normal, - SimpleDispatchInfo::FreeNormal => DispatchClass::Normal, + SimpleDispatchInfo::InsecureFreeNormal => DispatchClass::Normal, } } } @@ -178,14 +177,12 @@ pub enum SimpleDispatchInfo { FixedNormal(Weight), /// A normal dispatch with the maximum weight. MaxNormal, - /// A normal dispatch with no weight. - FreeNormal, + /// A normal dispatch with no weight. Base and bytes fees still need to be paid. + InsecureFreeNormal, /// An operational dispatch with fixed weight. FixedOperational(Weight), /// An operational dispatch with the maximum weight. MaxOperational, - /// An operational dispatch with no weight. - FreeOperational, } impl WeighData for SimpleDispatchInfo { @@ -193,11 +190,10 @@ impl WeighData for SimpleDispatchInfo { match self { SimpleDispatchInfo::FixedNormal(w) => *w, SimpleDispatchInfo::MaxNormal => Bounded::max_value(), - SimpleDispatchInfo::FreeNormal => Bounded::min_value(), + SimpleDispatchInfo::InsecureFreeNormal => Bounded::min_value(), SimpleDispatchInfo::FixedOperational(w) => *w, SimpleDispatchInfo::MaxOperational => Bounded::max_value(), - SimpleDispatchInfo::FreeOperational => Bounded::min_value(), } } } @@ -213,11 +209,10 @@ impl PaysFee for SimpleDispatchInfo { match self { SimpleDispatchInfo::FixedNormal(_) => true, SimpleDispatchInfo::MaxNormal => true, - SimpleDispatchInfo::FreeNormal => true, + SimpleDispatchInfo::InsecureFreeNormal => true, SimpleDispatchInfo::FixedOperational(_) => true, SimpleDispatchInfo::MaxOperational => true, - SimpleDispatchInfo::FreeOperational => false, } } } diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 0c73dcd65e4b8f10a08b1d75fef44ada0b23088d..71d3893426f0646ba14cca957c9a6e9915ee57b0 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-support-test" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", default-features = false, features = ["derive"] } diff --git a/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr b/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr index 5964a8aa769a65971bc04082cb364b9167fc132d..442af9c01fa3469901f097055d01a120b0f630c7 100644 --- a/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr +++ b/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr @@ -1,4 +1,4 @@ -error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event},` +error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event},` --> $DIR/missing_system_module.rs:8:2 | 8 | { diff --git a/frame/support/test/tests/decl_error.rs b/frame/support/test/tests/decl_error.rs index b90c870c313f5e74e09dbd1eb25beae18699e7f9..39d7dbdb9647857ce1d054d53acac90fda42d085 100644 --- a/frame/support/test/tests/decl_error.rs +++ b/frame/support/test/tests/decl_error.rs @@ -99,7 +99,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Module1_1: module1::::{Module, Call, Storage}, Module2: module2::{Module, Call, Storage}, Module1_2: module1::::{Module, Call, Storage}, diff --git a/frame/support/test/tests/decl_storage.rs b/frame/support/test/tests/decl_storage.rs index 15a0807fa5701328458cf741d935b0a304e98c26..f5e3892d5f72405fb742a19ddd81b431a9cde386 100644 --- a/frame/support/test/tests/decl_storage.rs +++ b/frame/support/test/tests/decl_storage.rs @@ -59,23 +59,27 @@ mod tests { GetOptU32WithBuilderNone get(fn opt_u32_with_builder_none) build(|_| None): Option; // map non-getters: pub / $default - MAPU32 : map u32 => Option; - pub PUBMAPU32 : map u32 => Option; - MAPU32MYDEF : map u32 => Option; - pub PUBMAPU32MYDEF : map u32 => Option; + MAPU32 : map hasher(blake2_256) u32 => Option; + pub PUBMAPU32 : map hasher(blake2_256) u32 => Option; + MAPU32MYDEF : map hasher(blake2_256) u32 => Option; + pub PUBMAPU32MYDEF : map hasher(blake2_256) u32 => Option; // map getters: pub / $default - GETMAPU32 get(fn map_u32_getter): map u32 => String; - pub PUBGETMAPU32 get(fn pub_map_u32_getter): map u32 => String; + GETMAPU32 get(fn map_u32_getter): map hasher(blake2_256) u32 => String; + pub PUBGETMAPU32 get(fn pub_map_u32_getter): map hasher(blake2_256) u32 => String; - GETMAPU32MYDEF get(fn map_u32_getter_mydef): map u32 => String = "map".into(); - pub PUBGETMAPU32MYDEF get(fn pub_map_u32_getter_mydef): map u32 => String = "pubmap".into(); + GETMAPU32MYDEF get(fn map_u32_getter_mydef): + map hasher(blake2_256) u32 => String = "map".into(); + pub PUBGETMAPU32MYDEF get(fn pub_map_u32_getter_mydef): + map hasher(blake2_256) u32 => String = "pubmap".into(); // linked map - LINKEDMAPU32 : linked_map u32 => Option; - pub PUBLINKEDMAPU32MYDEF : linked_map u32 => Option; - GETLINKEDMAPU32 get(fn linked_map_u32_getter): linked_map u32 => String; - pub PUBGETLINKEDMAPU32MYDEF get(fn pub_linked_map_u32_getter_mydef): linked_map u32 => String = "pubmap".into(); + LINKEDMAPU32 : linked_map hasher(blake2_256) u32 => Option; + pub PUBLINKEDMAPU32MYDEF : linked_map hasher(blake2_256) u32 => Option; + GETLINKEDMAPU32 get(fn linked_map_u32_getter): + linked_map hasher(blake2_256) u32 => String; + pub PUBGETLINKEDMAPU32MYDEF get(fn pub_linked_map_u32_getter_mydef): + linked_map hasher(blake2_256) u32 => String = "pubmap".into(); COMPLEXTYPE1: ::std::vec::Vec<::Origin>; COMPLEXTYPE2: (Vec)>>, u32); @@ -558,17 +562,17 @@ mod test_append_and_len { JustVecWithDefault: Vec = vec![6, 9]; OptionVec: Option>; - MapVec: map u32 => Vec; - MapVecWithDefault: map u32 => Vec = vec![6, 9]; - OptionMapVec: map u32 => Option>; + MapVec: map hasher(blake2_256) u32 => Vec; + MapVecWithDefault: map hasher(blake2_256) u32 => Vec = vec![6, 9]; + OptionMapVec: map hasher(blake2_256) u32 => Option>; - DoubleMapVec: double_map u32, u32 => Vec; - DoubleMapVecWithDefault: double_map u32, u32 => Vec = vec![6, 9]; - OptionDoubleMapVec: double_map u32, u32 => Option>; + DoubleMapVec: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Vec; + DoubleMapVecWithDefault: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Vec = vec![6, 9]; + OptionDoubleMapVec: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => Option>; - LinkedMapVec: linked_map u32 => Vec; - LinkedMapVecWithDefault: linked_map u32 => Vec = vec![6, 9]; - OptionLinkedMapVec: linked_map u32 => Option>; + LinkedMapVec: linked_map hasher(blake2_256) u32 => Vec; + LinkedMapVecWithDefault: linked_map hasher(blake2_256) u32 => Vec = vec![6, 9]; + OptionLinkedMapVec: linked_map hasher(blake2_256) u32 => Option>; } } diff --git a/frame/support/test/tests/final_keys.rs b/frame/support/test/tests/final_keys.rs index 9f1b379e2544d0e11286b103dffdd090d6b2f6b2..e604038f4ea30031129d4f118dcffe4b0c5e7686 100644 --- a/frame/support/test/tests/final_keys.rs +++ b/frame/support/test/tests/final_keys.rs @@ -35,18 +35,18 @@ mod no_instance { trait Store for Module as FinalKeysNone { pub Value config(value): u32; - pub Map: map u32 => u32; + pub Map: map hasher(blake2_256) u32 => u32; pub Map2: map hasher(twox_128) u32 => u32; - pub LinkedMap: linked_map u32 => u32; + pub LinkedMap: linked_map hasher(blake2_256) u32 => u32; pub LinkedMap2: linked_map hasher(twox_128) u32 => u32; - pub DoubleMap: double_map u32, u32 => u32; + pub DoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32; pub DoubleMap2: double_map hasher(twox_128) u32, hasher(blake2_128) u32 => u32; pub TestGenericValue get(fn test_generic_value) config(): Option; pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map): - double_map u32, T::BlockNumber => Option; + double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Option; } } } @@ -65,18 +65,18 @@ mod instance { { pub Value config(value): u32; - pub Map: map u32 => u32; + pub Map: map hasher(blake2_256) u32 => u32; pub Map2: map hasher(twox_128) u32 => u32; - pub LinkedMap: linked_map u32 => u32; + pub LinkedMap: linked_map hasher(blake2_256) u32 => u32; pub LinkedMap2: linked_map hasher(twox_128) u32 => u32; - pub DoubleMap: double_map u32, u32 => u32; + pub DoubleMap: double_map hasher(blake2_256) u32, hasher(blake2_256) u32 => u32; pub DoubleMap2: double_map hasher(twox_128) u32, hasher(blake2_128) u32 => u32; pub TestGenericValue get(fn test_generic_value) config(): Option; pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map): - double_map u32, T::BlockNumber => Option; + double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Option; } add_extra_genesis { // See `decl_storage` limitation. @@ -112,13 +112,11 @@ fn final_keys_no_instance() { k.extend(1u32.using_encoded(blake2_256).to_vec()); assert_eq!(unhashed::get::(&k), Some(2u32)); assert_eq!(unhashed::get::(&head), Some(1u32)); - assert_eq!(&k[..32], &::final_prefix()); no_instance::LinkedMap2::insert(1, 2); let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"LinkedMap2")].concat(); k.extend(1u32.using_encoded(twox_128).to_vec()); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &::final_prefix()); no_instance::DoubleMap::insert(&1, &2, &3); let mut k = [twox_128(b"FinalKeysNone"), twox_128(b"DoubleMap")].concat(); @@ -163,13 +161,11 @@ fn final_keys_default_instance() { k.extend(1u32.using_encoded(blake2_256).to_vec()); assert_eq!(unhashed::get::(&k), Some(2u32)); assert_eq!(unhashed::get::(&head), Some(1u32)); - assert_eq!(&k[..32], &>::final_prefix()); >::insert(1, 2); let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"LinkedMap2")].concat(); k.extend(1u32.using_encoded(twox_128).to_vec()); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &>::final_prefix()); >::insert(&1, &2, &3); let mut k = [twox_128(b"FinalKeysSome"), twox_128(b"DoubleMap")].concat(); @@ -214,13 +210,11 @@ fn final_keys_instance_2() { k.extend(1u32.using_encoded(blake2_256).to_vec()); assert_eq!(unhashed::get::(&k), Some(2u32)); assert_eq!(unhashed::get::(&head), Some(1u32)); - assert_eq!(&k[..32], &>::final_prefix()); >::insert(1, 2); let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"LinkedMap2")].concat(); k.extend(1u32.using_encoded(twox_128).to_vec()); assert_eq!(unhashed::get::(&k), Some(2u32)); - assert_eq!(&k[..32], &>::final_prefix()); >::insert(&1, &2, &3); let mut k = [twox_128(b"Instance2FinalKeysSome"), twox_128(b"DoubleMap")].concat(); diff --git a/frame/support/test/tests/genesisconfig.rs b/frame/support/test/tests/genesisconfig.rs index dee7aefe0aaa38d842d6969de939414788581cb7..12af18aac90fada32f8126e4c3c8d59afa09ecaf 100644 --- a/frame/support/test/tests/genesisconfig.rs +++ b/frame/support/test/tests/genesisconfig.rs @@ -25,7 +25,7 @@ frame_support::decl_module! { frame_support::decl_storage! { trait Store for Module as Example { - pub AppendableDM config(t): double_map u32, T::BlockNumber => Vec; + pub AppendableDM config(t): double_map hasher(blake2_256) u32, hasher(blake2_256) T::BlockNumber => Vec; } } diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 879ef4fa684d22c4fb882bf97cc67414c46aa894..48854baad9f2ffb2338555da5c31df501e6c006c 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -67,8 +67,8 @@ mod module1 { T::BlockNumber: From + std::fmt::Display { pub Value config(value): T::GenericType; - pub Map: map u32 => u64; - pub LinkedMap: linked_map u32 => u64; + pub Map: map hasher(blake2_256) u32 => u64; + pub LinkedMap: linked_map hasher(blake2_256) u32 => u64; } add_extra_genesis { @@ -136,9 +136,9 @@ mod module2 { frame_support::decl_storage! { trait Store for Module, I: Instance=DefaultInstance> as Module2 { pub Value config(value): T::Amount; - pub Map config(map): map u64 => u64; - pub LinkedMap config(linked_map): linked_map u64 => Vec; - pub DoubleMap config(double_map): double_map u64, u64 => u64; + pub Map config(map): map hasher(blake2_256) u64 => u64; + pub LinkedMap config(linked_map): linked_map hasher(blake2_256) u64 => Vec; + pub DoubleMap config(double_map): double_map hasher(blake2_256) u64, hasher(blake2_256) u64 => u64; } } @@ -247,7 +247,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Module1_1: module1::::{ Module, Call, Storage, Event, Config, Origin, Inherent }, @@ -348,8 +348,8 @@ fn storage_with_instance_basic_operation() { assert_eq!(Value::get(), 0); let key = 1; - assert_eq!(Map::exists(0), true); - assert_eq!(Map::exists(key), false); + assert_eq!(Map::contains_key(0), true); + assert_eq!(Map::contains_key(key), false); Map::insert(key, 1); assert_eq!(Map::get(key), 1); assert_eq!(Map::take(key), 1); @@ -357,11 +357,11 @@ fn storage_with_instance_basic_operation() { Map::mutate(key, |a| *a=2); assert_eq!(Map::get(key), 2); Map::remove(key); - assert_eq!(Map::exists(key), false); + assert_eq!(Map::contains_key(key), false); assert_eq!(Map::get(key), 0); - assert_eq!(LinkedMap::exists(0), true); - assert_eq!(LinkedMap::exists(key), false); + assert_eq!(LinkedMap::contains_key(0), true); + assert_eq!(LinkedMap::contains_key(key), false); LinkedMap::insert(key, vec![1]); assert_eq!(LinkedMap::enumerate().count(), 2); assert_eq!(LinkedMap::get(key), vec![1]); @@ -373,17 +373,17 @@ fn storage_with_instance_basic_operation() { assert_eq!(LinkedMap::get(key), vec![2]); LinkedMap::remove(key); assert_eq!(LinkedMap::enumerate().count(), 1); - assert_eq!(LinkedMap::exists(key), false); + assert_eq!(LinkedMap::contains_key(key), false); assert_eq!(LinkedMap::get(key), vec![]); - assert_eq!(LinkedMap::exists(key), false); + assert_eq!(LinkedMap::contains_key(key), false); assert_eq!(LinkedMap::enumerate().count(), 1); LinkedMap::insert(key, &vec![1]); assert_eq!(LinkedMap::enumerate().count(), 2); let key1 = 1; let key2 = 1; - assert_eq!(DoubleMap::exists(&0, &0), true); - assert_eq!(DoubleMap::exists(&key1, &key2), false); + assert_eq!(DoubleMap::contains_key(&0, &0), true); + assert_eq!(DoubleMap::contains_key(&key1, &key2), false); DoubleMap::insert(&key1, &key2, &1); assert_eq!(DoubleMap::get(&key1, &key2), 1); assert_eq!(DoubleMap::take(&key1, &key2), 1); diff --git a/frame/support/test/tests/issue2219.rs b/frame/support/test/tests/issue2219.rs index f3ce4dac80e25666e5ed30f63750ddbad764addb..45377669811160a0e50cf7af951945f266649c5f 100644 --- a/frame/support/test/tests/issue2219.rs +++ b/frame/support/test/tests/issue2219.rs @@ -109,7 +109,7 @@ mod module { } else { vec![] } - }): map Role => Option>; + }): map hasher(blake2_256) Role => Option>; /// the roles members can enter into pub AvailableRoles get(fn available_roles) build(|config: &GenesisConfig| { @@ -124,10 +124,12 @@ mod module { pub ActorAccountIds get(fn actor_account_ids) : Vec; /// actor accounts associated with a role - pub AccountIdsByRole get(fn account_ids_by_role) : map Role => Vec; + pub AccountIdsByRole get(fn account_ids_by_role): + map hasher(blake2_256) Role => Vec; /// tokens locked until given block number - pub Bondage get(fn bondage) : map T::AccountId => T::BlockNumber; + pub Bondage get(fn bondage): + map hasher(blake2_256) T::AccountId => T::BlockNumber; /// First step before enter a role is registering intent with a new account/key. /// This is done by sending a role_entry_request() from the new account. @@ -171,7 +173,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Module: module::{Module, Call, Storage, Config}, } ); diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs index c5b591642b820c10aed5379ab58aa209d03bf9ed..c7f60117bc526ec4e96745c9f74379e913b43316 100644 --- a/frame/support/test/tests/system.rs +++ b/frame/support/test/tests/system.rs @@ -23,7 +23,7 @@ pub trait Trait: 'static + Eq + Clone { type BlockNumber: Decode + Encode + EncodeLike + Clone + Default; type Hash; type AccountId: Encode + EncodeLike + Decode; - type Event: From; + type Event: From>; type ModuleToIndex: frame_support::traits::ModuleToIndex; } @@ -36,9 +36,10 @@ impl Module { } frame_support::decl_event!( - pub enum Event { + pub enum Event where BlockNumber = ::BlockNumber { ExtrinsicSuccess, ExtrinsicFailed, + Ignore(BlockNumber), } ); diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index 098c320f454e59655dd238a80cca3b98b615e6f9..631d8eecb4715da13851e1323839f415abf7cf17 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-system" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index 42a35c49a117641b74f4a3fa3f36babf6306e6f9..1e24cb2c0597425a5540646d782f4767cf783b5f 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -18,7 +18,7 @@ use criterion::{Criterion, criterion_group, criterion_main, black_box}; use frame_system as system; use frame_support::{decl_module, decl_event, impl_outer_origin, impl_outer_event, weights::Weight}; use sp_core::H256; -use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; +use sp_runtime::{Perbill, PerThing, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; mod module { use super::*; @@ -46,6 +46,7 @@ impl_outer_origin!{ impl_outer_event! { pub enum Event for Runtime { + system, module, } } @@ -75,6 +76,9 @@ impl system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl module::Trait for Runtime { diff --git a/frame/system/rpc/runtime-api/Cargo.toml b/frame/system/rpc/runtime-api/Cargo.toml index f9beb848aad36a8ed826aabb844151d029d4115e..62440323768886eea4252e3a081807543dbdc48f 100644 --- a/frame/system/rpc/runtime-api/Cargo.toml +++ b/frame/system/rpc/runtime-api/Cargo.toml @@ -3,6 +3,7 @@ name = "frame-system-rpc-runtime-api" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-api = { version = "2.0.0", default-features = false, path = "../../../../primitives/api" } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 478fd780b98fa9c4b96540f0501ef98ec174e3f6..c351020a41057c1bcf6cac861a69ee4b348ca5c9 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -97,51 +97,35 @@ use sp_std::marker::PhantomData; use sp_std::fmt::Debug; use sp_version::RuntimeVersion; use sp_runtime::{ - RuntimeDebug, - generic::{self, Era}, Perbill, DispatchOutcome, DispatchError, + RuntimeDebug, Perbill, DispatchOutcome, DispatchError, + generic::{self, Era}, transaction_validity::{ ValidTransaction, TransactionPriority, TransactionLongevity, TransactionValidityError, InvalidTransaction, TransactionValidity, }, traits::{ - self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Lookup, LookupError, + self, CheckEqual, AtLeast32Bit, Zero, SignedExtension, Lookup, LookupError, SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, BadOrigin, SaturatedConversion, - MaybeSerialize, MaybeSerializeDeserialize, StaticLookup, One, Bounded, + MaybeSerialize, MaybeSerializeDeserialize, MaybeMallocSizeOf, StaticLookup, One, Bounded, }, }; use sp_core::{ChangesTrieConfiguration, storage::well_known_keys}; use frame_support::{ decl_module, decl_event, decl_storage, decl_error, storage, Parameter, - traits::{Contains, Get, ModuleToIndex, OnReapAccount}, + traits::{ + Contains, Get, ModuleToIndex, OnNewAccount, OnReapAccount, IsDeadAccount, Happened, + StoredMap + }, weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo}, }; -use codec::{Encode, Decode}; +use codec::{Encode, Decode, FullCodec, EncodeLike}; #[cfg(any(feature = "std", test))] use sp_io::TestExternalities; pub mod offchain; -/// Handler for when a new account has been created. -#[impl_trait_for_tuples::impl_for_tuples(30)] -pub trait OnNewAccount { - /// A new account `who` has been registered. - fn on_new_account(who: &AccountId); -} - -/// Determiner to say whether a given account is unused. -pub trait IsDeadAccount { - /// Is the given account dead? - fn is_dead_account(who: &AccountId) -> bool; -} - -impl IsDeadAccount for () { - fn is_dead_account(_who: &AccountId) -> bool { - true - } -} - /// Compute the trie root of a list of extrinsics. pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) @@ -165,18 +149,18 @@ pub trait Trait: 'static + Eq + Clone { /// Account index (aka nonce) type. This stores the number of previous transactions associated /// with a sender account. type Index: - Parameter + Member + MaybeSerialize + Debug + Default + MaybeDisplay + SimpleArithmetic + Parameter + Member + MaybeSerialize + Debug + Default + MaybeDisplay + AtLeast32Bit + Copy; /// The block number type used by the runtime. type BlockNumber: - Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + SimpleArithmetic - + Default + Bounded + Copy + sp_std::hash::Hash + sp_std::str::FromStr; + Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + AtLeast32Bit + + Default + Bounded + Copy + sp_std::hash::Hash + sp_std::str::FromStr + MaybeMallocSizeOf; /// The output of the `Hashing` function. type Hash: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + SimpleBitOps + Ord - + Default + Copy + CheckEqual + sp_std::hash::Hash + AsRef<[u8]> + AsMut<[u8]>; + + Default + Copy + CheckEqual + sp_std::hash::Hash + AsRef<[u8]> + AsMut<[u8]> + MaybeMallocSizeOf; /// The hashing system (algorithm) being used in the runtime (e.g. Blake2). type Hashing: Hash; @@ -200,7 +184,7 @@ pub trait Trait: 'static + Eq + Clone { >; /// The aggregated event type of the runtime. - type Event: Parameter + Member + From + Debug; + type Event: Parameter + Member + From> + Debug; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount: Get; @@ -224,6 +208,18 @@ pub trait Trait: 'static + Eq + Clone { /// Expects the `ModuleToIndex` type that is being generated by `construct_runtime!` in the /// runtime. For tests it is okay to use `()` as type (returns `0` for each input). type ModuleToIndex: ModuleToIndex; + + /// Data to be associated with an account (other than nonce/transaction counter, which this + /// module does regardless). + type AccountData: Member + FullCodec + Clone + Default; + + /// Handler for when a new account has just been created. + type OnNewAccount: OnNewAccount; + + /// A function that is invoked when an account has been determined to be dead. + /// + /// All resources should be cleaned up associated with the given account. + type OnReapAccount: OnReapAccount; } pub type DigestOf = generic::Digest<::Hash>; @@ -232,109 +228,6 @@ pub type DigestItemOf = generic::DigestItem<::Hash>; pub type Key = Vec; pub type KeyValue = (Vec, Vec); -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - type Error = Error; - - /// A big dispatch that will disallow any other transaction to be included. - // TODO: this must be preferable available for testing really (not possible at the moment). - #[weight = SimpleDispatchInfo::MaxOperational] - fn fill_block(origin) { - ensure_root(origin)?; - } - - /// Make some on-chain remark. - #[weight = SimpleDispatchInfo::FixedNormal(10_000)] - fn remark(origin, _remark: Vec) { - ensure_signed(origin)?; - } - - /// Set the number of pages in the WebAssembly environment's heap. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn set_heap_pages(origin, pages: u64) { - ensure_root(origin)?; - storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); - } - - /// Set the new runtime code. - #[weight = SimpleDispatchInfo::FixedOperational(200_000)] - pub fn set_code(origin, code: Vec) { - ensure_root(origin)?; - - let current_version = T::Version::get(); - let new_version = sp_io::misc::runtime_version(&code) - .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) - .ok_or_else(|| Error::::FailedToExtractRuntimeVersion)?; - - if new_version.spec_name != current_version.spec_name { - Err(Error::::InvalidSpecName)? - } - - if new_version.spec_version < current_version.spec_version { - Err(Error::::SpecVersionNotAllowedToDecrease)? - } else if new_version.spec_version == current_version.spec_version { - if new_version.impl_version < current_version.impl_version { - Err(Error::::ImplVersionNotAllowedToDecrease)? - } else if new_version.impl_version == current_version.impl_version { - Err(Error::::SpecOrImplVersionNeedToIncrease)? - } - } - - storage::unhashed::put_raw(well_known_keys::CODE, &code); - } - - /// Set the new runtime code without doing any checks of the given `code`. - #[weight = SimpleDispatchInfo::FixedOperational(200_000)] - pub fn set_code_without_checks(origin, code: Vec) { - ensure_root(origin)?; - storage::unhashed::put_raw(well_known_keys::CODE, &code); - } - - /// Set the new changes trie configuration. - #[weight = SimpleDispatchInfo::FixedOperational(20_000)] - pub fn set_changes_trie_config(origin, changes_trie_config: Option) { - ensure_root(origin)?; - match changes_trie_config.clone() { - Some(changes_trie_config) => storage::unhashed::put_raw( - well_known_keys::CHANGES_TRIE_CONFIG, - &changes_trie_config.encode(), - ), - None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG), - } - - let log = generic::DigestItem::ChangesTrieSignal( - generic::ChangesTrieSignal::NewConfiguration(changes_trie_config), - ); - Self::deposit_log(log.into()); - } - - /// Set some items of storage. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn set_storage(origin, items: Vec) { - ensure_root(origin)?; - for i in &items { - storage::unhashed::put_raw(&i.0, &i.1); - } - } - - /// Kill some items from storage. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn kill_storage(origin, keys: Vec) { - ensure_root(origin)?; - for key in &keys { - storage::unhashed::kill(&key); - } - } - - /// Kill all storage items with a key that starts with the given prefix. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn kill_prefix(origin, prefix: Key) { - ensure_root(origin)?; - storage::unhashed::kill_prefix(&prefix); - } - } -} - /// A phase of a block's execution. #[derive(Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone))] @@ -357,38 +250,6 @@ pub struct EventRecord { pub topics: Vec, } -decl_event!( - /// Event for the System module. - pub enum Event { - /// An extrinsic completed successfully. - ExtrinsicSuccess(DispatchInfo), - /// An extrinsic failed. - ExtrinsicFailed(DispatchError, DispatchInfo), - } -); - -decl_error! { - /// Error for the System module - pub enum Error for Module { - /// The name of specification does not match between the current runtime - /// and the new runtime. - InvalidSpecName, - /// The specification version is not allowed to decrease between the current runtime - /// and the new runtime. - SpecVersionNotAllowedToDecrease, - /// The implementation version is not allowed to decrease between the current runtime - /// and the new runtime. - ImplVersionNotAllowedToDecrease, - /// The specification or the implementation version need to increase between the - /// current runtime and the new runtime. - SpecOrImplVersionNeedToIncrease, - /// Failed to extract the runtime version from the new runtime. - /// - /// Either calling `Core_version` or decoding `RuntimeVersion` failed. - FailedToExtractRuntimeVersion, - } -} - /// Origin for the System module. #[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub enum RawOrigin { @@ -431,28 +292,45 @@ type EventIndex = u32; decl_storage! { trait Store for Module as System { - /// Extrinsics nonce for accounts. - pub AccountNonce get(fn account_nonce): map T::AccountId => T::Index; + /// The full account information for a particular account ID. + // TODO: should be hasher(twox64_concat) - will need staged migration + // TODO: should not including T::Index (the nonce) + // https://github.com/paritytech/substrate/issues/4917 + pub Account get(fn account): map hasher(blake2_256) T::AccountId => (T::Index, T::AccountData); + /// Total extrinsics count for the current block. ExtrinsicCount: Option; + /// Total weight for all extrinsics put together, for the current block. AllExtrinsicsWeight: Option; + /// Total length (in bytes) for all extrinsics put together, for the current block. AllExtrinsicsLen: Option; + /// Map of block numbers to block hashes. - pub BlockHash get(fn block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map T::BlockNumber => T::Hash; + // TODO: should be hasher(twox64_concat) - will need one-off migration + // https://github.com/paritytech/substrate/issues/4917 + pub BlockHash get(fn block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): + map hasher(blake2_256) T::BlockNumber => T::Hash; + /// Extrinsics data for the current block (maps an extrinsic's index to its data). - ExtrinsicData get(fn extrinsic_data): map u32 => Vec; + ExtrinsicData get(fn extrinsic_data): map hasher(twox_64_concat) u32 => Vec; + /// The current block number being processed. Set by `execute_block`. Number get(fn block_number) build(|_| 1.into()): T::BlockNumber; + /// Hash of the previous block. ParentHash get(fn parent_hash) build(|_| hash69()): T::Hash; + /// Extrinsics root of the current block, also part of the block header. ExtrinsicsRoot get(fn extrinsics_root): T::Hash; + /// Digest of the current block, also part of the block header. Digest get(fn digest): DigestOf; + /// Events deposited for the current block. Events get(fn events): Vec>; + /// The number of events in the `Events` list. EventCount get(fn event_count): EventIndex; @@ -471,7 +349,7 @@ decl_storage! { /// The value has the type `(T::BlockNumber, EventIndex)` because if we used only just /// the `EventIndex` then in case if the topic has the same contents on the next block /// no notification will be triggered thus the event might be lost. - EventTopics get(fn event_topics): map T::Hash => Vec<(T::BlockNumber, EventIndex)>; + EventTopics get(fn event_topics): map hasher(blake2_256) T::Hash => Vec<(T::BlockNumber, EventIndex)>; } add_extra_genesis { config(changes_trie_config): Option; @@ -494,6 +372,150 @@ decl_storage! { } } +decl_event!( + /// Event for the System module. + pub enum Event where AccountId = ::AccountId { + /// An extrinsic completed successfully. + ExtrinsicSuccess(DispatchInfo), + /// An extrinsic failed. + ExtrinsicFailed(DispatchError, DispatchInfo), + /// `:code` was updated. + CodeUpdated, + /// A new account was created. + NewAccount(AccountId), + /// An account was reaped. + ReapedAccount(AccountId), + } +); + +decl_error! { + /// Error for the System module + pub enum Error for Module { + /// The name of specification does not match between the current runtime + /// and the new runtime. + InvalidSpecName, + /// The specification version is not allowed to decrease between the current runtime + /// and the new runtime. + SpecVersionNotAllowedToDecrease, + /// The implementation version is not allowed to decrease between the current runtime + /// and the new runtime. + ImplVersionNotAllowedToDecrease, + /// The specification or the implementation version need to increase between the + /// current runtime and the new runtime. + SpecOrImplVersionNeedToIncrease, + /// Failed to extract the runtime version from the new runtime. + /// + /// Either calling `Core_version` or decoding `RuntimeVersion` failed. + FailedToExtractRuntimeVersion, + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + + /// A big dispatch that will disallow any other transaction to be included. + // TODO: This should only be available for testing, rather than in general usage, but + // that's not possible at present (since it's within the decl_module macro). + #[weight = SimpleDispatchInfo::MaxOperational] + fn fill_block(origin) { + ensure_root(origin)?; + } + + /// Make some on-chain remark. + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] + fn remark(origin, _remark: Vec) { + ensure_signed(origin)?; + } + + /// Set the number of pages in the WebAssembly environment's heap. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn set_heap_pages(origin, pages: u64) { + ensure_root(origin)?; + storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); + } + + /// Set the new runtime code. + #[weight = SimpleDispatchInfo::FixedOperational(200_000)] + pub fn set_code(origin, code: Vec) { + ensure_root(origin)?; + + let current_version = T::Version::get(); + let new_version = sp_io::misc::runtime_version(&code) + .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) + .ok_or_else(|| Error::::FailedToExtractRuntimeVersion)?; + + if new_version.spec_name != current_version.spec_name { + Err(Error::::InvalidSpecName)? + } + + if new_version.spec_version < current_version.spec_version { + Err(Error::::SpecVersionNotAllowedToDecrease)? + } else if new_version.spec_version == current_version.spec_version { + if new_version.impl_version < current_version.impl_version { + Err(Error::::ImplVersionNotAllowedToDecrease)? + } else if new_version.impl_version == current_version.impl_version { + Err(Error::::SpecOrImplVersionNeedToIncrease)? + } + } + + storage::unhashed::put_raw(well_known_keys::CODE, &code); + Self::deposit_event(RawEvent::CodeUpdated); + } + + /// Set the new runtime code without doing any checks of the given `code`. + #[weight = SimpleDispatchInfo::FixedOperational(200_000)] + pub fn set_code_without_checks(origin, code: Vec) { + ensure_root(origin)?; + storage::unhashed::put_raw(well_known_keys::CODE, &code); + Self::deposit_event(RawEvent::CodeUpdated); + } + + /// Set the new changes trie configuration. + #[weight = SimpleDispatchInfo::FixedOperational(20_000)] + pub fn set_changes_trie_config(origin, changes_trie_config: Option) { + ensure_root(origin)?; + match changes_trie_config.clone() { + Some(changes_trie_config) => storage::unhashed::put_raw( + well_known_keys::CHANGES_TRIE_CONFIG, + &changes_trie_config.encode(), + ), + None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG), + } + + let log = generic::DigestItem::ChangesTrieSignal( + generic::ChangesTrieSignal::NewConfiguration(changes_trie_config), + ); + Self::deposit_log(log.into()); + } + + /// Set some items of storage. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn set_storage(origin, items: Vec) { + ensure_root(origin)?; + for i in &items { + storage::unhashed::put_raw(&i.0, &i.1); + } + } + + /// Kill some items from storage. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn kill_storage(origin, keys: Vec) { + ensure_root(origin)?; + for key in &keys { + storage::unhashed::kill(&key); + } + } + + /// Kill all storage items with a key that starts with the given prefix. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn kill_prefix(origin, prefix: Key) { + ensure_root(origin)?; + storage::unhashed::kill_prefix(&prefix); + } + } +} + pub struct EnsureRoot(sp_std::marker::PhantomData); impl< O: Into, O>> + From>, @@ -617,7 +639,7 @@ impl Module { Self::deposit_event_indexed(&[], event.into()); } - /// Deposits an event into this block's event record adding this event + /// Deposits an event into this block's event record adding this event /// to the corresponding topic indexes. /// /// This will update storage entries that correspond to the specified topics. @@ -827,9 +849,14 @@ impl Module { /// Return the chain's current runtime version. pub fn runtime_version() -> RuntimeVersion { T::Version::get() } + /// Retrieve the account transaction counter from storage. + pub fn account_nonce(who: impl EncodeLike) -> T::Index { + Account::::get(who).0 + } + /// Increment a particular account's nonce by 1. - pub fn inc_account_nonce(who: &T::AccountId) { - >::insert(who, Self::account_nonce(who) + T::Index::one()); + pub fn inc_account_nonce(who: impl EncodeLike) { + Account::::mutate(who, |a| a.0 += T::Index::one()); } /// Note what the extrinsic data of the current extrinsic index is. If this @@ -846,8 +873,11 @@ impl Module { pub fn note_applied_extrinsic(r: &DispatchOutcome, _encoded_len: u32, info: DispatchInfo) { Self::deposit_event( match r { - Ok(()) => Event::ExtrinsicSuccess(info), - Err(err) => Event::ExtrinsicFailed(err.clone(), info), + Ok(()) => RawEvent::ExtrinsicSuccess(info), + Err(err) => { + sp_runtime::print(err); + RawEvent::ExtrinsicFailed(err.clone(), info) + }, } ); @@ -871,12 +901,118 @@ impl Module { let xts_root = extrinsics_data_root::(extrinsics); >::put(xts_root); } + + /// An account is being created. + pub fn on_created_account(who: T::AccountId) { + T::OnNewAccount::on_new_account(&who); + Self::deposit_event(RawEvent::NewAccount(who)); + } + + /// Kill the account and reap any related information. + pub fn kill_account(who: T::AccountId) { + if Account::::contains_key(&who) { + Account::::remove(&who); + Self::on_killed_account(who); + } + } + + /// Do anything that needs to be done after an account has been killed. + fn on_killed_account(who: T::AccountId) { + T::OnReapAccount::on_reap_account(&who); + Self::deposit_event(RawEvent::ReapedAccount(who)); + } +} + +/// Event handler which calls on_created_account when it happens. +pub struct CallOnCreatedAccount(PhantomData); +impl Happened for CallOnCreatedAccount { + fn happened(who: &T::AccountId) { + Module::::on_created_account(who.clone()); + } +} + +/// Event handler which calls kill_account when it happens. +pub struct CallKillAccount(PhantomData); +impl Happened for CallKillAccount { + fn happened(who: &T::AccountId) { + Module::::kill_account(who.clone()); + } +} + +// Implement StoredMap for a simple single-item, kill-account-on-remove system. This works fine for +// storing a single item which is required to not be empty/default for the account to exist. +// Anything more complex will need more sophisticated logic. +impl StoredMap for Module { + fn get(k: &T::AccountId) -> T::AccountData { + Account::::get(k).1 + } + fn is_explicit(k: &T::AccountId) -> bool { + Account::::contains_key(k) + } + fn insert(k: &T::AccountId, t: T::AccountData) { + let existed = Account::::contains_key(k); + Account::::insert(k, (T::Index::default(), t)); + if !existed { + Self::on_created_account(k.clone()); + } + } + fn remove(k: &T::AccountId) { + if Account::::contains_key(&k) { + Self::kill_account(k.clone()); + } + } + fn mutate(k: &T::AccountId, f: impl FnOnce(&mut T::AccountData) -> R) -> R { + let existed = Account::::contains_key(k); + let r = Account::::mutate(k, |a| f(&mut a.1)); + if !existed { + Self::on_created_account(k.clone()); + } + r + } + fn mutate_exists(k: &T::AccountId, f: impl FnOnce(&mut Option) -> R) -> R { + let (existed, exists, r) = Account::::mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let (maybe_nonce, mut maybe_extra) = split_inner(maybe_value.take(), |v| v); + let r = f(&mut maybe_extra); + *maybe_value = maybe_extra.map(|extra| (maybe_nonce.unwrap_or_default(), extra)); + (existed, maybe_value.is_some(), r) + }); + if !existed && exists { + Self::on_created_account(k.clone()); + } else if existed && !exists { + Self::on_killed_account(k.clone()); + } + r + } + fn try_mutate_exists(k: &T::AccountId, f: impl FnOnce(&mut Option) -> Result) -> Result { + Account::::try_mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let (maybe_nonce, mut maybe_extra) = split_inner(maybe_value.take(), |v| v); + f(&mut maybe_extra).map(|v| { + *maybe_value = maybe_extra.map(|extra| (maybe_nonce.unwrap_or_default(), extra)); + (existed, maybe_value.is_some(), v) + }) + }).map(|(existed, exists, v)| { + if !existed && exists { + Self::on_created_account(k.clone()); + } else if existed && !exists { + Self::on_killed_account(k.clone()); + } + v + }) + } } -impl OnReapAccount for Module { - /// Remove the nonce for the account. Account is considered fully removed from the system. - fn on_reap_account(who: &T::AccountId) { - >::remove(who); +/// Split an `option` into two constituent options, as defined by a `splitter` function. +pub fn split_inner(option: Option, splitter: impl FnOnce(T) -> (R, S)) + -> (Option, Option) +{ + match option { + Some(inner) => { + let (r, s) = splitter(inner); + (Some(r), Some(s)) + } + None => (None, None), } } @@ -890,7 +1026,7 @@ impl CheckWeight { /// a portion. fn get_dispatch_limit_ratio(class: DispatchClass) -> Perbill { match class { - DispatchClass::Operational => Perbill::one(), + DispatchClass::Operational => ::one(), DispatchClass::Normal => T::AvailableBlockRatio::get(), } } @@ -952,6 +1088,7 @@ impl SignedExtension for CheckWeight { type AdditionalSigned = (); type DispatchInfo = DispatchInfo; type Pre = (); + const IDENTIFIER: &'static str = "CheckWeight"; fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } @@ -1032,6 +1169,7 @@ impl SignedExtension for CheckNonce { type AdditionalSigned = (); type DispatchInfo = DispatchInfo; type Pre = (); + const IDENTIFIER: &'static str = "CheckNonce"; fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } @@ -1042,7 +1180,7 @@ impl SignedExtension for CheckNonce { _info: Self::DispatchInfo, _len: usize, ) -> Result<(), TransactionValidityError> { - let expected = >::get(who); + let (expected, extra) = Account::::get(who); if self.0 != expected { return Err( if self.0 < expected { @@ -1052,8 +1190,7 @@ impl SignedExtension for CheckNonce { }.into() ) } - - >::insert(who, expected + T::Index::one()); + Account::::insert(who, (expected + T::Index::one(), extra)); Ok(()) } @@ -1065,7 +1202,7 @@ impl SignedExtension for CheckNonce { _len: usize, ) -> TransactionValidity { // check index - let expected = >::get(who); + let (expected, _extra) = Account::::get(who); if self.0 < expected { return InvalidTransaction::Stale.into() } @@ -1087,6 +1224,12 @@ impl SignedExtension for CheckNonce { } } +impl IsDeadAccount for Module { + fn is_dead_account(who: &T::AccountId) -> bool { + !Account::::contains_key(who) + } +} + /// Check for transaction mortality. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckEra((Era, sp_std::marker::PhantomData)); @@ -1116,6 +1259,7 @@ impl SignedExtension for CheckEra { type AdditionalSigned = T::Hash; type DispatchInfo = DispatchInfo; type Pre = (); + const IDENTIFIER: &'static str = "CheckEra"; fn validate( &self, @@ -1135,7 +1279,7 @@ impl SignedExtension for CheckEra { fn additional_signed(&self) -> Result { let current_u64 = >::block_number().saturated_into::(); let n = (self.0).0.birth(current_u64).saturated_into::(); - if !>::exists(n) { + if !>::contains_key(n) { Err(InvalidTransaction::AncientBirthBlock.into()) } else { Ok(>::block_hash(n)) @@ -1172,6 +1316,7 @@ impl SignedExtension for CheckGenesis { type AdditionalSigned = T::Hash; type DispatchInfo = DispatchInfo; type Pre = (); + const IDENTIFIER: &'static str = "CheckGenesis"; fn additional_signed(&self) -> Result { Ok(>::block_hash(T::BlockNumber::zero())) @@ -1207,6 +1352,7 @@ impl SignedExtension for CheckVersion { type AdditionalSigned = u32; type DispatchInfo = DispatchInfo; type Pre = (); + const IDENTIFIER: &'static str = "CheckVersion"; fn additional_signed(&self) -> Result { Ok(>::runtime_version().spec_version) @@ -1275,13 +1421,18 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = Version; type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } - impl From for u16 { - fn from(e: Event) -> u16 { + impl From> for u16 { + fn from(e: Event) -> u16 { match e { - Event::ExtrinsicSuccess(..) => 100, - Event::ExtrinsicFailed(..) => 101, + Event::::ExtrinsicSuccess(..) => 100, + Event::::ExtrinsicFailed(..) => 101, + Event::::CodeUpdated => 102, + _ => 103, } } } @@ -1461,7 +1612,7 @@ mod tests { #[test] fn signed_ext_check_nonce_works() { new_test_ext().execute_with(|| { - >::insert(1, 1); + Account::::insert(1, (1, ())); let info = DispatchInfo::default(); let len = 0_usize; // stale @@ -1688,6 +1839,11 @@ mod tests { RawOrigin::Root.into(), substrate_test_runtime_client::runtime::WASM_BINARY.to_vec(), ).unwrap(); + + assert_eq!( + System::events(), + vec![EventRecord { phase: Phase::ApplyExtrinsic(0), event: 102u16, topics: vec![] }], + ); }); } } diff --git a/frame/system/src/offchain.rs b/frame/system/src/offchain.rs index f5fda34585d4ded2ff09936e50eceac0d7f20de4..b26f4be4b45e027dfd83573c2da516e911a6ff4c 100644 --- a/frame/system/src/offchain.rs +++ b/frame/system/src/offchain.rs @@ -20,8 +20,8 @@ use codec::Encode; use sp_std::convert::TryInto; use sp_std::prelude::Vec; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; -use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; -use frame_support::debug; +use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; +use frame_support::{debug, storage::StorageMap}; /// Creates runtime-specific signed transaction. /// @@ -128,19 +128,19 @@ pub trait SignAndSubmitTransaction { fn sign_and_submit(call: impl Into, public: PublicOf) -> Result<(), ()> { let call = call.into(); let id = public.clone().into_account(); - let expected = >::account_nonce(&id); + let (expected_nonce, extra) = super::Account::::get(&id); debug::native::debug!( target: "offchain", "Creating signed transaction from account: {:?} (nonce: {:?})", id, - expected, + expected_nonce, ); let (call, signature_data) = Self::CreateTransaction - ::create_transaction::(call, public, id.clone(), expected) + ::create_transaction::(call, public, id.clone(), expected_nonce) .ok_or(())?; // increment the nonce. This is fine, since the code should always // be running in off-chain context, so we NEVER persists data. - >::inc_account_nonce(&id); + super::Account::::insert(&id, (expected_nonce + One::one(), extra)); let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) diff --git a/frame/timestamp/Cargo.toml b/frame/timestamp/Cargo.toml index 354a4740b712cdaaac7a641e4c268de159cd6efe..e68392f9e9a9f9824e38ee26a317559d347981f7 100644 --- a/frame/timestamp/Cargo.toml +++ b/frame/timestamp/Cargo.toml @@ -3,11 +3,13 @@ name = "pallet-timestamp" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } diff --git a/frame/timestamp/src/benchmarking.rs b/frame/timestamp/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..55d6d7e0467409d513dccce4169a45347245a351 --- /dev/null +++ b/frame/timestamp/src/benchmarking.rs @@ -0,0 +1,105 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Timestamp pallet benchmarking. + +use super::*; + +use sp_std::prelude::*; + +use frame_system::RawOrigin; +use sp_runtime::{BenchmarkResults, BenchmarkParameter, selected_benchmark}; +use sp_runtime::traits::{Benchmarking, BenchmarkingSetup, Dispatchable}; + +/// Benchmark `set` extrinsic. +struct Set; +impl BenchmarkingSetup, RawOrigin> for Set { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + vec![ + // Current time ("Now") + (BenchmarkParameter::N, 1, 100), + ] + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(Call, RawOrigin), &'static str> + { + let user_origin = RawOrigin::None; + let now = components.iter().find(|&c| c.0 == BenchmarkParameter::N).unwrap().1; + + // Return the `set` call + Ok((Call::::set(now.into()), user_origin)) + } +} + +selected_benchmark!(Set); + +impl Benchmarking for Module { + fn run_benchmark(extrinsic: Vec, steps: u32, repeat: u32) -> Result, &'static str> { + // Map the input to the selected benchmark. + let selected_benchmark = match extrinsic.as_slice() { + b"set" => SelectedBenchmark::Set, + _ => return Err("Could not find extrinsic."), + }; + + // Warm up the DB + sp_io::benchmarking::commit_db(); + sp_io::benchmarking::wipe_db(); + + let components = , RawOrigin>>::components(&selected_benchmark); + let mut results: Vec = Vec::new(); + + // Select the component we will be benchmarking. Each component will be benchmarked. + for (name, low, high) in components.iter() { + // Create up to `STEPS` steps for that component between high and low. + let step_size = ((high - low) / steps).max(1); + let num_of_steps = (high - low) / step_size; + for s in 0..num_of_steps { + // This is the value we will be testing for component `name` + let component_value = low + step_size * s; + + // Select the mid value for all the other components. + let c: Vec<(BenchmarkParameter, u32)> = components.iter() + .map(|(n, l, h)| + (*n, if n == name { component_value } else { (h - l) / 2 + l }) + ).collect(); + + // Run the benchmark `repeat` times. + for _ in 0..repeat { + // Set up the externalities environment for the setup we want to benchmark. + let (call, caller) = , + RawOrigin, + >>::instance(&selected_benchmark, &c)?; + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + sp_io::benchmarking::commit_db(); + // Run the benchmark. + let start = sp_io::benchmarking::current_time(); + call.dispatch(caller.into())?; + let finish = sp_io::benchmarking::current_time(); + let elapsed = finish - start; + results.push((c.clone(), elapsed)); + // Wipe the DB back to the genesis state. + sp_io::benchmarking::wipe_db(); + } + } + } + + return Ok(results); + } +} diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index 1c16ed6380a92a702151a38982a6313d79fb82b0..95e1fc3a5e8088edb0b864cc09893c6e075bf9cd 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -90,6 +90,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod benchmarking; + use sp_std::{result, cmp}; use sp_inherents::{ProvideInherent, InherentData, InherentIdentifier}; use frame_support::{Parameter, decl_storage, decl_module}; @@ -97,7 +99,7 @@ use frame_support::traits::{Time, Get}; use sp_runtime::{ RuntimeString, traits::{ - SimpleArithmetic, Zero, SaturatedConversion, Scale + AtLeast32Bit, Zero, SaturatedConversion, Scale } }; use frame_support::weights::SimpleDispatchInfo; @@ -110,7 +112,7 @@ use sp_timestamp::{ /// The module configuration trait pub trait Trait: frame_system::Trait { /// Type used for expressing timestamp. - type Moment: Parameter + Default + SimpleArithmetic + type Moment: Parameter + Default + AtLeast32Bit + Scale + Copy; /// Something which can be notified when the timestamp is set. Set this to `()` if not needed. @@ -274,6 +276,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { pub const MinimumPeriod: u64 = 5; diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml index ceb1ba9ebb586f0824d8b30a8dea8dcf77f6da4a..ac3dbb2c2b16a25f6d13489952e526a2ce237ac6 100644 --- a/frame/transaction-payment/Cargo.toml +++ b/frame/transaction-payment/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-transaction-payment" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/frame/transaction-payment/rpc/Cargo.toml b/frame/transaction-payment/rpc/Cargo.toml index b8d1c35492242853492f709f6b647d66d684501c..5fb5f5e2745d6f553810ee3525210d05b23e2192 100644 --- a/frame/transaction-payment/rpc/Cargo.toml +++ b/frame/transaction-payment/rpc/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-transaction-payment-rpc" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } diff --git a/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/frame/transaction-payment/rpc/runtime-api/Cargo.toml index 3cf28bf662a249535befe93b4a3dcfbe4935e18f..8ee16a90dc3fcf08a90b824639ecac0d3cfb857d 100644 --- a/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-transaction-payment-rpc-runtime-api" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } diff --git a/frame/transaction-payment/rpc/runtime-api/src/lib.rs b/frame/transaction-payment/rpc/runtime-api/src/lib.rs index 6e7425f8b8e35d77381b864358c84041c1b6cc4d..77b6e3b45464e5bc7837ce619a43f7d283cc4bb7 100644 --- a/frame/transaction-payment/rpc/runtime-api/src/lib.rs +++ b/frame/transaction-payment/rpc/runtime-api/src/lib.rs @@ -22,12 +22,13 @@ use sp_std::prelude::*; use frame_support::weights::{Weight, DispatchClass}; use codec::{Encode, Codec, Decode}; #[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; -use sp_runtime::traits::{UniqueSaturatedInto, SaturatedConversion}; +use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use sp_runtime::traits::{MaybeDisplay, MaybeFromStr}; /// Some information related to a dispatchable that can be queried from the runtime. #[derive(Eq, PartialEq, Encode, Decode, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct RuntimeDispatchInfo { /// Weight of this dispatch. pub weight: Weight, @@ -35,47 +36,27 @@ pub struct RuntimeDispatchInfo { pub class: DispatchClass, /// The partial inclusion fee of this dispatch. This does not include tip or anything else which /// is dependent on the signature (aka. depends on a `SignedExtension`). + #[cfg_attr(feature = "std", serde(bound(serialize = "Balance: std::fmt::Display")))] + #[cfg_attr(feature = "std", serde(serialize_with = "serialize_as_string"))] + #[cfg_attr(feature = "std", serde(bound(deserialize = "Balance: std::str::FromStr")))] + #[cfg_attr(feature = "std", serde(deserialize_with = "deserialize_from_string"))] pub partial_fee: Balance, } -/// A capped version of `RuntimeDispatchInfo`. -/// -/// The `Balance` is capped (or expanded) to `u64` to avoid serde issues with `u128`. -#[derive(Eq, PartialEq, Encode, Decode, Default)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] -pub struct CappedDispatchInfo { - /// Weight of this dispatch. - pub weight: Weight, - /// Class of this dispatch. - pub class: DispatchClass, - /// The partial inclusion fee of this dispatch. This does not include tip or anything else which - /// is dependent on the signature (aka. depends on a `SignedExtension`). - pub partial_fee: u64, +#[cfg(feature = "std")] +fn serialize_as_string(t: &T, serializer: S) -> Result { + serializer.serialize_str(&t.to_string()) } -impl CappedDispatchInfo { - /// Create a new `CappedDispatchInfo` from `RuntimeDispatchInfo`. - pub fn new>( - dispatch: RuntimeDispatchInfo, - ) -> Self { - let RuntimeDispatchInfo { - weight, - class, - partial_fee, - } = dispatch; - - Self { - weight, - class, - partial_fee: partial_fee.saturated_into(), - } - } +#[cfg(feature = "std")] +fn deserialize_from_string<'de, D: Deserializer<'de>, T: std::str::FromStr>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + s.parse::().map_err(|_| serde::de::Error::custom("Parse from string failed")) } sp_api::decl_runtime_apis! { pub trait TransactionPaymentApi where - Balance: Codec, + Balance: Codec + MaybeDisplay + MaybeFromStr, Extrinsic: Codec, { fn query_info(uxt: Extrinsic, len: u32) -> RuntimeDispatchInfo; @@ -87,18 +68,34 @@ mod tests { use super::*; #[test] - fn should_serialize_properly_with_u64() { + fn should_serialize_and_deserialize_properly_with_string() { let info = RuntimeDispatchInfo { weight: 5, class: DispatchClass::Normal, partial_fee: 1_000_000_u64, }; - let info = CappedDispatchInfo::new(info); - assert_eq!( - serde_json::to_string(&info).unwrap(), - r#"{"weight":5,"class":"normal","partialFee":1000000}"#, - ); + let json_str = r#"{"weight":5,"class":"normal","partialFee":"1000000"}"#; + + assert_eq!(serde_json::to_string(&info).unwrap(), json_str); + assert_eq!(serde_json::from_str::>(json_str).unwrap(), info); + + // should not panic + serde_json::to_value(&info).unwrap(); + } + + #[test] + fn should_serialize_and_deserialize_properly_large_value() { + let info = RuntimeDispatchInfo { + weight: 5, + class: DispatchClass::Normal, + partial_fee: u128::max_value(), + }; + + let json_str = r#"{"weight":5,"class":"normal","partialFee":"340282366920938463463374607431768211455"}"#; + + assert_eq!(serde_json::to_string(&info).unwrap(), json_str); + assert_eq!(serde_json::from_str::>(json_str).unwrap(), info); // should not panic serde_json::to_value(&info).unwrap(); diff --git a/frame/transaction-payment/rpc/src/lib.rs b/frame/transaction-payment/rpc/src/lib.rs index aadc33759ede4388e6fac044f104017a7c1b65fe..945b0119d7236dd4108d84978fb017b736f318cf 100644 --- a/frame/transaction-payment/rpc/src/lib.rs +++ b/frame/transaction-payment/rpc/src/lib.rs @@ -21,21 +21,21 @@ use codec::{Codec, Decode}; use sp_blockchain::HeaderBackend; use jsonrpc_core::{Error as RpcError, ErrorCode, Result}; use jsonrpc_derive::rpc; -use sp_runtime::{generic::BlockId, traits::{Block as BlockT, UniqueSaturatedInto}}; +use sp_runtime::{generic::BlockId, traits::{Block as BlockT, MaybeDisplay, MaybeFromStr}}; use sp_api::ProvideRuntimeApi; use sp_core::Bytes; -use pallet_transaction_payment_rpc_runtime_api::CappedDispatchInfo; +use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi; pub use self::gen_client::Client as TransactionPaymentClient; #[rpc] -pub trait TransactionPaymentApi { +pub trait TransactionPaymentApi { #[rpc(name = "payment_queryInfo")] fn query_info( &self, encoded_xt: Bytes, at: Option - ) -> Result; + ) -> Result; } /// A struct that implements the [`TransactionPaymentApi`]. @@ -68,20 +68,20 @@ impl From for i64 { } } -impl TransactionPaymentApi<::Hash, Balance> +impl TransactionPaymentApi<::Hash, RuntimeDispatchInfo> for TransactionPayment where Block: BlockT, C: Send + Sync + 'static + ProvideRuntimeApi + HeaderBackend, C::Api: TransactionPaymentRuntimeApi, - Balance: Codec + UniqueSaturatedInto, + Balance: Codec + MaybeDisplay + MaybeFromStr, Extrinsic: Codec + Send + Sync + 'static, { fn query_info( &self, encoded_xt: Bytes, at: Option<::Hash> - ) -> Result { + ) -> Result> { let api = self.client.runtime_api(); let at = BlockId::hash(at.unwrap_or_else(|| // If the block hash is not supplied assume the best block. @@ -99,6 +99,6 @@ where code: ErrorCode::ServerError(Error::RuntimeError.into()), message: "Unable to query dispatch info.".into(), data: Some(format!("{:?}", e).into()), - }).map(CappedDispatchInfo::new) + }) } } diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 00dbf6bc66b73bfafb3272459239cc5da04a6c1f..0d9cfc0b77683ff3016654a63ac9a51c1ad22eab 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -76,7 +76,7 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as Balances { - NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_parts(0); + pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_parts(0); } } @@ -178,9 +178,7 @@ impl ChargeTransactionPayment { let adjusted_fee = targeted_fee_adjustment.saturated_multiply_accumulate(adjustable_fee); let base_fee = T::TransactionBaseFee::get(); - let final_fee = base_fee.saturating_add(adjusted_fee).saturating_add(tip); - - final_fee + base_fee.saturating_add(adjusted_fee).saturating_add(tip) } else { tip } @@ -201,6 +199,7 @@ impl sp_std::fmt::Debug for ChargeTransactionPayment impl SignedExtension for ChargeTransactionPayment where BalanceOf: Send + Sync { + const IDENTIFIER: &'static str = "ChargeTransactionPayment"; type AccountId = T::AccountId; type Call = T::Call; type AdditionalSigned = (); @@ -303,28 +302,23 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; - pub const ExistentialDeposit: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Runtime { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } - - thread_local! { +thread_local! { static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); static WEIGHT_TO_FEE: RefCell = RefCell::new(1); @@ -398,15 +392,18 @@ mod tests { self.set_constants(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 10 * self.balance_factor), - (2, 20 * self.balance_factor), - (3, 30 * self.balance_factor), - (4, 40 * self.balance_factor), - (5, 50 * self.balance_factor), - (6, 60 * self.balance_factor) - ], - vesting: vec![], + balances: if self.balance_factor > 0 { + vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 30 * self.balance_factor), + (4, 40 * self.balance_factor), + (5, 50 * self.balance_factor), + (6, 60 * self.balance_factor) + ] + } else { + vec![] + }, }.assimilate_storage(&mut t).unwrap(); t.into() } @@ -431,14 +428,14 @@ mod tests { .pre_dispatch(&1, CALL, info_from_weight(5), len) .is_ok() ); - assert_eq!(Balances::free_balance(&1), 100 - 5 - 5 - 10); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); assert!( ChargeTransactionPayment::::from(5 /* tipped */) .pre_dispatch(&2, CALL, info_from_weight(3), len) .is_ok() ); - assert_eq!(Balances::free_balance(&2), 200 - 5 - 10 - 3 - 5); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 3 - 5); }); } @@ -473,11 +470,11 @@ mod tests { .execute_with(|| { // 1 ain't have a penny. - assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::free_balance(1), 0); let len = 100; - // like a FreeOperational + // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. let operational_transaction = DispatchInfo { weight: 0, class: DispatchClass::Operational, @@ -489,7 +486,7 @@ mod tests { .is_ok() ); - // like a FreeNormal + // like a InsecureFreeNormal let free_transaction = DispatchInfo { weight: 0, class: DispatchClass::Normal, @@ -520,7 +517,7 @@ mod tests { .pre_dispatch(&1, CALL, info_from_weight(3), len) .is_ok() ); - assert_eq!(Balances::free_balance(&1), 100 - 10 - 5 - (10 + 3) * 3 / 2); + assert_eq!(Balances::free_balance(1), 100 - 10 - 5 - (10 + 3) * 3 / 2); }) } diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 9fd76ddd9cefaa0e126c41a679c125e4cd73dfe3..10226f3572bb1079e9100cc9a57a26d032f80717 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-treasury" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 1800d0ad04392c957f2a424441d17822004f0839..36f92c401e329d5a7bbea68c776a9e27201e4249 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -200,7 +200,7 @@ decl_storage! { ProposalCount get(fn proposal_count): ProposalIndex; /// Proposals that have been made. - Proposals get(fn proposals): map ProposalIndex => Option>>; + Proposals get(fn proposals): map hasher(blake2_256) ProposalIndex => Option>>; /// Proposal indices that have been approved but not yet awarded. Approvals get(fn approvals): Vec; @@ -371,7 +371,7 @@ decl_module! { fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::ApproveOrigin::ensure_origin(origin)?; - ensure!(>::exists(proposal_id), Error::::InvalidProposalIndex); + ensure!(>::contains_key(proposal_id), Error::::InvalidProposalIndex); Approvals::mutate(|v| v.push(proposal_id)); } @@ -403,9 +403,9 @@ decl_module! { ensure!(reason.len() <= MAX_SENSIBLE_REASON_LENGTH, Error::::ReasonTooBig); let reason_hash = T::Hashing::hash(&reason[..]); - ensure!(!Reasons::::exists(&reason_hash), Error::::AlreadyKnown); + ensure!(!Reasons::::contains_key(&reason_hash), Error::::AlreadyKnown); let hash = T::Hashing::hash_of(&(&reason_hash, &who)); - ensure!(!Tips::::exists(&hash), Error::::AlreadyKnown); + ensure!(!Tips::::contains_key(&hash), Error::::AlreadyKnown); let deposit = T::TipReportDepositBase::get() + T::TipReportDepositPerByte::get() * (reason.len() as u32).into(); @@ -474,7 +474,7 @@ decl_module! { let tipper = ensure_signed(origin)?; ensure!(T::Tippers::contains(&tipper), BadOrigin); let reason_hash = T::Hashing::hash(&reason[..]); - ensure!(!Reasons::::exists(&reason_hash), Error::::AlreadyKnown); + ensure!(!Reasons::::contains_key(&reason_hash), Error::::AlreadyKnown); let hash = T::Hashing::hash_of(&(&reason_hash, &who)); Reasons::::insert(&reason_hash, &reason); @@ -590,7 +590,7 @@ impl Module { } } - /// Remove any non-members of `Tippers` from a `tips` vectr. `O(T)`. + /// Remove any non-members of `Tippers` from a `tips` vector. `O(T)`. fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf)>) { let members = T::Tippers::sorted_members(); let mut members_iter = members.iter(); @@ -724,7 +724,9 @@ mod tests { use frame_support::traits::Contains; use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, OnFinalize, IdentityLookup, BadOrigin}, testing::Header, Perbill + Perbill, + testing::Header, + traits::{BlakeTwo256, OnFinalize, IdentityLookup, BadOrigin}, }; impl_outer_origin! { @@ -756,23 +758,19 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; - } +} impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); - type OnFreeBalanceZero = (); - type OnReapAccount = System; type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } pub struct TenToFourteen; impl Contains for TenToFourteen { @@ -818,7 +816,6 @@ mod tests { pallet_balances::GenesisConfig::{ // Total issuance will be 200 with treasury account initialized at ED. balances: vec![(0, 100), (1, 98), (2, 1)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); GenesisConfig::default().assimilate_storage::(&mut t).unwrap(); t.into() @@ -853,8 +850,8 @@ mod tests { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 3)); - assert_eq!(Balances::reserved_balance(&0), 12); - assert_eq!(Balances::free_balance(&0), 88); + assert_eq!(Balances::reserved_balance(0), 12); + assert_eq!(Balances::free_balance(0), 88); // other reports don't count. assert_noop!( @@ -869,9 +866,9 @@ mod tests { assert_noop!(Treasury::tip(Origin::signed(9), h.clone(), 10), BadOrigin); System::set_block_number(2); assert_ok!(Treasury::close_tip(Origin::signed(100), h.into())); - assert_eq!(Balances::reserved_balance(&0), 0); - assert_eq!(Balances::free_balance(&0), 102); - assert_eq!(Balances::free_balance(&3), 8); + assert_eq!(Balances::reserved_balance(0), 0); + assert_eq!(Balances::free_balance(0), 102); + assert_eq!(Balances::free_balance(3), 8); }); } @@ -880,16 +877,16 @@ mod tests { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 0)); - assert_eq!(Balances::reserved_balance(&0), 12); - assert_eq!(Balances::free_balance(&0), 88); + assert_eq!(Balances::reserved_balance(0), 12); + assert_eq!(Balances::free_balance(0), 88); let h = BlakeTwo256::hash_of(&(BlakeTwo256::hash(b"awesome.dot"), 0u64)); assert_ok!(Treasury::tip(Origin::signed(10), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(11), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(12), h.clone(), 10)); System::set_block_number(2); assert_ok!(Treasury::close_tip(Origin::signed(100), h.into())); - assert_eq!(Balances::reserved_balance(&0), 0); - assert_eq!(Balances::free_balance(&0), 110); + assert_eq!(Balances::reserved_balance(0), 0); + assert_eq!(Balances::free_balance(0), 110); }); } @@ -910,7 +907,7 @@ mod tests { System::set_block_number(2); assert_noop!(Treasury::close_tip(Origin::NONE, h.into()), BadOrigin); assert_ok!(Treasury::close_tip(Origin::signed(0), h.into())); - assert_eq!(Balances::free_balance(&3), 10); + assert_eq!(Balances::free_balance(3), 10); assert_noop!(Treasury::close_tip(Origin::signed(100), h.into()), Error::::UnknownTip); }); @@ -942,7 +939,7 @@ mod tests { assert_ok!(Treasury::tip(Origin::signed(12), h.clone(), 1000000)); System::set_block_number(2); assert_ok!(Treasury::close_tip(Origin::signed(0), h.into())); - assert_eq!(Balances::free_balance(&3), 10); + assert_eq!(Balances::free_balance(3), 10); }); } @@ -961,7 +958,7 @@ mod tests { assert_ok!(Treasury::tip(Origin::signed(10), h.clone(), 10)); System::set_block_number(2); assert_ok!(Treasury::close_tip(Origin::signed(0), h.into())); - assert_eq!(Balances::free_balance(&3), 10); + assert_eq!(Balances::free_balance(3), 10); }); } @@ -978,8 +975,8 @@ mod tests { fn spend_proposal_takes_min_deposit() { new_test_ext().execute_with(|| { assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); - assert_eq!(Balances::free_balance(&0), 99); - assert_eq!(Balances::reserved_balance(&0), 1); + assert_eq!(Balances::free_balance(0), 99); + assert_eq!(Balances::reserved_balance(0), 1); }); } @@ -987,8 +984,8 @@ mod tests { fn spend_proposal_takes_proportional_deposit() { new_test_ext().execute_with(|| { assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_eq!(Balances::free_balance(&0), 95); - assert_eq!(Balances::reserved_balance(&0), 5); + assert_eq!(Balances::free_balance(0), 95); + assert_eq!(Balances::reserved_balance(0), 5); }); } @@ -1011,7 +1008,7 @@ mod tests { assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(1); - assert_eq!(Balances::free_balance(&3), 0); + assert_eq!(Balances::free_balance(3), 0); assert_eq!(Treasury::pot(), 100); }); } @@ -1038,7 +1035,7 @@ mod tests { assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); >::on_finalize(2); - assert_eq!(Balances::free_balance(&3), 0); + assert_eq!(Balances::free_balance(3), 0); assert_eq!(Treasury::pot(), 50); }); } @@ -1089,7 +1086,7 @@ mod tests { assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); - assert_eq!(Balances::free_balance(&3), 100); + assert_eq!(Balances::free_balance(3), 100); assert_eq!(Treasury::pot(), 0); }); } @@ -1108,7 +1105,7 @@ mod tests { let _ = Balances::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); >::on_finalize(4); - assert_eq!(Balances::free_balance(&3), 150); // Fund has been spent + assert_eq!(Balances::free_balance(3), 150); // Fund has been spent assert_eq!(Treasury::pot(), 25); // Pot has finally changed }); } @@ -1133,7 +1130,7 @@ mod tests { >::on_finalize(4); assert_eq!(Treasury::pot(), 0); // Pot is emptied - assert_eq!(Balances::free_balance(&Treasury::account_id()), 1); // but the account is still there + assert_eq!(Balances::free_balance(Treasury::account_id()), 1); // but the account is still there }); } @@ -1144,13 +1141,12 @@ mod tests { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig::{ balances: vec![(0, 100), (1, 99), (2, 1)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); // Treasury genesis config is not build thus treasury account does not exist let mut t: sp_io::TestExternalities = t.into(); t.execute_with(|| { - assert_eq!(Balances::free_balance(&Treasury::account_id()), 0); // Account does not exist + assert_eq!(Balances::free_balance(Treasury::account_id()), 0); // Account does not exist assert_eq!(Treasury::pot(), 0); // Pot is empty assert_ok!(Treasury::propose_spend(Origin::signed(0), 99, 3)); @@ -1159,16 +1155,16 @@ mod tests { assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); >::on_finalize(2); assert_eq!(Treasury::pot(), 0); // Pot hasn't changed - assert_eq!(Balances::free_balance(&3), 0); // Balance of `3` hasn't changed + assert_eq!(Balances::free_balance(3), 0); // Balance of `3` hasn't changed Balances::make_free_balance_be(&Treasury::account_id(), 100); assert_eq!(Treasury::pot(), 99); // Pot now contains funds - assert_eq!(Balances::free_balance(&Treasury::account_id()), 100); // Account does exist + assert_eq!(Balances::free_balance(Treasury::account_id()), 100); // Account does exist >::on_finalize(4); assert_eq!(Treasury::pot(), 0); // Pot has changed - assert_eq!(Balances::free_balance(&3), 99); // Balance of `3` has changed + assert_eq!(Balances::free_balance(3), 99); // Balance of `3` has changed }); } } diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index afd7ae3d374c7d3385dc05bb789aeb97a0ee63c3..6d1187b0eeafbce02a510b7edc03942a3b1149ea 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -3,6 +3,7 @@ name = "pallet-utility" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true } diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index 9ab2975e2955016c8450f1ef677afc489ae9414b..414651659c022401159459f089acc097ce027b56 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -318,6 +318,8 @@ impl TypeId for IndexedUtilityModuleId { decl_module! { pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; @@ -659,6 +661,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for Test { + system, pallet_balances, utility, } @@ -698,23 +701,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnFreeBalanceZero = (); - type OnReapAccount = System; - type OnNewAccount = (); type Event = TestEvent; - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const MultisigDepositBase: u64 = 1; @@ -740,7 +739,6 @@ mod tests { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 10)], - vesting: vec![], }.assimilate_storage(&mut t).unwrap(); t.into() } diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..6923297af5616be2dffcb1bad90b775480056397 --- /dev/null +++ b/frame/vesting/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "pallet-vesting" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +serde = { version = "1.0.101", optional = true } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +enumflags2 = { version = "0.6.2" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } + +[dev-dependencies] +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } +hex-literal = "0.2.1" + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sp-std/std", + "sp-io/std", + "sp-runtime/std", + "frame-support/std", + "frame-system/std", +] diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..3777475f3fafe350f3e02631170ba1bd895adf86 --- /dev/null +++ b/frame/vesting/src/lib.rs @@ -0,0 +1,616 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Vesting Module +//! +//! - [`vesting::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! +//! ## Overview +//! +//! A simple module providing a means of placing a linear curve on an account's locked balance. This +//! module ensures that there is a lock in place preventing the balance to drop below the *unvested* +//! amount for any reason other than transaction fee payment. +//! +//! As the amount vested increases over time, the amount unvested reduces. However, locks remain in +//! place and explicit action is needed on behalf of the user to ensure that the amount locked is +//! equivalent to the amount remaining to be vested. This is done through a dispatchable function, +//! either `vest` (in typical case where the sender is calling on their own behalf) or `vest_other` +//! in case the sender is calling on another account's behalf. +//! +//! ## Interface +//! +//! This module implements the `VestingSchedule` trait. +//! +//! ### Dispatchable Functions +//! +//! - `vest` - Update the lock, reducing it in line with the amount "vested" so far. +//! - `vest_other` - Update the lock of another account, reducing it in line with the amount +//! "vested" so far. +//! +//! [`Call`]: ./enum.Call.html +//! [`Trait`]: ./trait.Trait.html + +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_std::prelude::*; +use sp_std::fmt::Debug; +use codec::{Encode, Decode}; +use sp_runtime::{DispatchResult, RuntimeDebug, traits::{ + StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Convert +}}; +use frame_support::{decl_module, decl_event, decl_storage, decl_error}; +use frame_support::traits::{ + Currency, LockableCurrency, VestingSchedule, WithdrawReason, LockIdentifier +}; +use frame_system::{self as system, ensure_signed}; + +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + +pub trait Trait: frame_system::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; + + /// The currency trait. + type Currency: LockableCurrency; + + /// Convert the block number into a balance. + type BlockNumberToBalance: Convert>; +} + +const VESTING_ID: LockIdentifier = *b"vesting "; + +/// Struct to encode the vesting schedule of an individual account. +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct VestingInfo { + /// Locked amount at genesis. + pub locked: Balance, + /// Amount that gets unlocked every block after `starting_block`. + pub per_block: Balance, + /// Starting block for unlocking(vesting). + pub starting_block: BlockNumber, +} + +impl< + Balance: AtLeast32Bit + Copy, + BlockNumber: AtLeast32Bit + Copy, +> VestingInfo { + /// Amount locked at block `n`. + pub fn locked_at< + BlockNumberToBalance: Convert + >(&self, n: BlockNumber) -> Balance { + // Number of blocks that count toward vesting + // Saturating to 0 when n < starting_block + let vested_block_count = n.saturating_sub(self.starting_block); + let vested_block_count = BlockNumberToBalance::convert(vested_block_count); + // Return amount that is still locked in vesting + let maybe_balance = vested_block_count.checked_mul(&self.per_block); + if let Some(balance) = maybe_balance { + self.locked.saturating_sub(balance) + } else { + Zero::zero() + } + } +} + +decl_storage! { + trait Store for Module as Vesting { + /// Information regarding the vesting of a given account. + pub Vesting get(fn vesting): + map hasher(blake2_256) T::AccountId => Option, T::BlockNumber>>; + } + add_extra_genesis { + config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, BalanceOf)>; + build(|config: &GenesisConfig| { + use sp_runtime::traits::Saturating; + // Generate initial vesting configuration + // * who - Account which we are generating vesting configuration for + // * begin - Block when the account will start to vest + // * length - Number of blocks from `begin` until fully vested + // * liquid - Number of units which can be spent before vesting begins + for &(ref who, begin, length, liquid) in config.vesting.iter() { + let balance = T::Currency::free_balance(who); + assert!(!balance.is_zero(), "Currencies must be init'd before vesting"); + // Total genesis `balance` minus `liquid` equals funds locked for vesting + let locked = balance.saturating_sub(liquid); + let length_as_balance = T::BlockNumberToBalance::convert(length); + let per_block = locked / length_as_balance.max(sp_runtime::traits::One::one()); + + Vesting::::insert(who, VestingInfo { + locked: locked, + per_block: per_block, + starting_block: begin + }); + let reasons = WithdrawReason::Transfer | WithdrawReason::Reserve; + T::Currency::set_lock(VESTING_ID, who, locked, reasons); + } + }) + } +} + +decl_event!( + pub enum Event where AccountId = ::AccountId, Balance = BalanceOf { + /// The amount vested has been updated. This could indicate more funds are available. The + /// balance given is the amount which is left unvested (and thus locked). + VestingUpdated(AccountId, Balance), + /// An account (given) has become fully vested. No further vesting can happen. + VestingCompleted(AccountId), + } +); + +decl_error! { + /// Error for the vesting module. + pub enum Error for Module { + /// The account given is not vesting. + NotVesting, + /// An existing vesting schedule already exists for this account that cannot be clobbered. + ExistingVestingSchedule, + } +} + +decl_module! { + // Simple declaration of the `Module` type. Lets the macro know what it's working on. + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + + fn deposit_event() = default; + + /// Unlock any vested funds of the sender account. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have funds still + /// locked under this module. + /// + /// Emits either `VestingCompleted` or `VestingUpdated`. + /// + /// # + /// - `O(1)`. + /// - One balance-lock operation. + /// - One storage read (codec `O(1)`) and up to one removal. + /// - One event. + /// # + fn vest(origin) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::update_lock(who) + } + + /// Unlock any vested funds of a `target` account. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `target`: The account whose vested funds should be unlocked. Must have funds still + /// locked under this module. + /// + /// Emits either `VestingCompleted` or `VestingUpdated`. + /// + /// # + /// - `O(1)`. + /// - Up to one account lookup. + /// - One balance-lock operation. + /// - One storage read (codec `O(1)`) and up to one removal. + /// - One event. + /// # + fn vest_other(origin, target: ::Source) -> DispatchResult { + ensure_signed(origin)?; + Self::update_lock(T::Lookup::lookup(target)?) + } + } +} + +impl Module { + /// (Re)set or remove the module's currency lock on `who`'s account in accordance with their + /// current unvested amount. + fn update_lock(who: T::AccountId) -> DispatchResult { + let vesting = Self::vesting(&who).ok_or(Error::::NotVesting)?; + let now = >::block_number(); + let locked_now = vesting.locked_at::(now); + + if locked_now.is_zero() { + T::Currency::remove_lock(VESTING_ID, &who); + Vesting::::remove(&who); + Self::deposit_event(RawEvent::VestingCompleted(who)); + } else { + let reasons = WithdrawReason::Transfer | WithdrawReason::Reserve; + T::Currency::set_lock(VESTING_ID, &who, locked_now, reasons); + Self::deposit_event(RawEvent::VestingUpdated(who, locked_now)); + } + Ok(()) + } +} + +impl VestingSchedule for Module where + BalanceOf: MaybeSerializeDeserialize + Debug +{ + type Moment = T::BlockNumber; + type Currency = T::Currency; + + /// Get the amount that is currently being vested and cannot be transferred out of this account. + fn vesting_balance(who: &T::AccountId) -> BalanceOf { + if let Some(v) = Self::vesting(who) { + let now = >::block_number(); + let locked_now = v.locked_at::(now); + T::Currency::free_balance(who).min(locked_now) + } else { + Zero::zero() + } + } + + /// Adds a vesting schedule to a given account. + /// + /// If there already exists a vesting schedule for the given account, an `Err` is returned + /// and nothing is updated. + /// + /// On success, a linearly reducing amount of funds will be locked. In order to realise any + /// reduction of the lock over time as it diminishes, the account owner must use `vest` or + /// `vest_other`. + /// + /// Is a no-op if the amount to be vested is zero. + fn add_vesting_schedule( + who: &T::AccountId, + locked: BalanceOf, + per_block: BalanceOf, + starting_block: T::BlockNumber + ) -> DispatchResult { + if locked.is_zero() { return Ok(()) } + if Vesting::::contains_key(who) { + Err(Error::::ExistingVestingSchedule)? + } + let vesting_schedule = VestingInfo { + locked, + per_block, + starting_block + }; + Vesting::::insert(who, vesting_schedule); + // it can't fail, but even if somehow it did, we don't really care. + let _ = Self::update_lock(who.clone()); + Ok(()) + } + + /// Remove a vesting schedule for a given account. + fn remove_vesting_schedule(who: &T::AccountId) { + Vesting::::remove(who); + // it can't fail, but even if somehow it did, we don't really care. + let _ = Self::update_lock(who.clone()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::cell::RefCell; + use frame_support::{ + assert_ok, assert_noop, impl_outer_origin, parameter_types, weights::Weight, + traits::Get + }; + use sp_core::H256; + // The testing primitives are very useful for avoiding having to work with signatures + // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. + use sp_runtime::{ + Perbill, + testing::Header, + traits::{BlakeTwo256, IdentityLookup, Identity, OnInitialize}, + }; + use sp_storage::Storage; + + impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} + } + + // For testing the module, we construct most of a mock runtime. This means + // first constructing a configuration type (`Test`) which `impl`s each of the + // configuration traits of modules we want to use. + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + impl frame_system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = (); + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; + } + impl pallet_balances::Trait for Test { + type Balance = u64; + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + } + impl Trait for Test { + type Event = (); + type Currency = Balances; + type BlockNumberToBalance = Identity; + } + type System = frame_system::Module; + type Balances = pallet_balances::Module; + type Vesting = Module; + + thread_local! { + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); + } + pub struct ExistentialDeposit; + impl Get for ExistentialDeposit { + fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } + } + + pub struct ExtBuilder { + existential_deposit: u64, + } + impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + } + } + } + impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn build(self) -> sp_io::TestExternalities { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit) + ], + }.assimilate_storage(&mut t).unwrap(); + GenesisConfig:: { + vesting: vec![ + (1, 0, 10, 5 * self.existential_deposit), + (2, 10, 20, 0), + (12, 10, 20, 5 * self.existential_deposit) + ], + }.assimilate_storage(&mut t).unwrap(); + t.into() + } + } + + #[test] + fn vesting_info_via_migration_should_work() { + let mut s = Storage::default(); + use hex_literal::hex; + // A dump of data from the previous version for which we know account 6 vests 30 of its 60 + // over 5 blocks from block 3. + let data = vec![ + (hex!["26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"].to_vec(), hex!["0100000000000000"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"].to_vec(), hex!["02000000"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"].to_vec(), hex!["08000000000000000000000000"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc"].to_vec(), hex!["4545454545454545454545454545454545454545454545454545454545454545"].to_vec()), + (hex!["26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c11874681e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c"].to_vec(), hex!["4545454545454545454545454545454545454545454545454545454545454545"].to_vec()), + (hex!["3a636f6465"].to_vec(), hex![""].to_vec()), + (hex!["3a65787472696e7369635f696e646578"].to_vec(), hex!["00000000"].to_vec()), + (hex!["3a686561707061676573"].to_vec(), hex!["0800000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc61dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b"].to_vec(), hex!["046d697363202020200300000000000000ffffffffffffffff04"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc66cddb367afbd583bb48f9bbd7d5ba3b1d0738b4881b1cddd38169526d8158137"].to_vec(), hex!["0474786665657320200300000000000000ffffffffffffffff01"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6e88b43fded6323ef02ffeffbd8c40846ee09bf316271bd22369659c959dd733a"].to_vec(), hex!["08616c6c20202020200300000000000000ffffffffffffffff1f64656d6f63726163ffffffffffffffff030000000000000002"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f3c22813def93ef32c365b55cb92f10f91dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b"].to_vec(), hex!["0500000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80"].to_vec(), hex!["d200000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f5f27b51b5ec208ee9cb25b55d8728243b8788bb218b185b63e3e92653953f29b6b143fb8cf5159fc908632e6fe490501"].to_vec(), hex!["1e0000000000000006000000000000000200000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b41dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b"].to_vec(), hex!["0500000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b46cddb367afbd583bb48f9bbd7d5ba3b1d0738b4881b1cddd38169526d8158137"].to_vec(), hex!["1e00000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4b8788bb218b185b63e3e92653953f29b6b143fb8cf5159fc908632e6fe490501"].to_vec(), hex!["3c00000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4e88b43fded6323ef02ffeffbd8c40846ee09bf316271bd22369659c959dd733a"].to_vec(), hex!["1400000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4e96760d274653a39b429a87ebaae9d3aa4fdf58b9096cf0bebc7c4e5a4c2ed8d"].to_vec(), hex!["2800000000000000"].to_vec()), + (hex!["c2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4effb728943197fd12e694cbf3f3ede28fbf7498b0370c6dfa0013874b417c178"].to_vec(), hex!["3200000000000000"].to_vec()), + (hex!["f2794c22e353e9a839f12faab03a911b7f17cdfbfa73331856cca0acddd7842e"].to_vec(), hex!["00000000"].to_vec()), + (hex!["f2794c22e353e9a839f12faab03a911bbdcb0c5143a8617ed38ae3810dd45bc6"].to_vec(), hex!["00000000"].to_vec()), + (hex!["f2794c22e353e9a839f12faab03a911be2f6cb0456905c189bcb0458f9440f13"].to_vec(), hex!["00000000"].to_vec()), + ]; + s.top = data.into_iter().collect(); + sp_io::TestExternalities::new(s).execute_with(|| { + Balances::on_initialize(1); + assert_eq!(Balances::free_balance(6), 60); + assert_eq!(Balances::usable_balance(&6), 30); + System::set_block_number(2); + assert_ok!(Vesting::vest(Origin::signed(6))); + assert_eq!(Balances::usable_balance(&6), 30); + System::set_block_number(3); + assert_ok!(Vesting::vest(Origin::signed(6))); + assert_eq!(Balances::usable_balance(&6), 36); + System::set_block_number(4); + assert_ok!(Vesting::vest(Origin::signed(6))); + assert_eq!(Balances::usable_balance(&6), 42); + }); + } + + #[test] + fn check_vesting_status() { + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingInfo { + locked: 256 * 5, + per_block: 128, // Vesting over 10 blocks + starting_block: 0, + }; + let user2_vesting_schedule = VestingInfo { + locked: 256 * 20, + per_block: 256, // Vesting over 20 blocks + starting_block: 10, + }; + let user12_vesting_schedule = VestingInfo { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_eq!(Vesting::vesting(&1), Some(user1_vesting_schedule)); // Account 1 has a vesting schedule + assert_eq!(Vesting::vesting(&2), Some(user2_vesting_schedule)); // Account 2 has a vesting schedule + assert_eq!(Vesting::vesting(&12), Some(user12_vesting_schedule)); // Account 12 has a vesting schedule + + // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 + assert_eq!(Vesting::vesting_balance(&1), 128 * 9); + // Account 2 has their full balance locked + assert_eq!(Vesting::vesting_balance(&2), user2_free_balance); + // Account 12 has only their illiquid funds locked + assert_eq!(Vesting::vesting_balance(&12), user12_free_balance - 256 * 5); + + System::set_block_number(10); + assert_eq!(System::block_number(), 10); + + // Account 1 has fully vested by block 10 + assert_eq!(Vesting::vesting_balance(&1), 0); + // Account 2 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&2), user2_free_balance); + // Account 12 has started vesting by block 10 + assert_eq!(Vesting::vesting_balance(&12), user12_free_balance - 256 * 5); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + assert_eq!(Vesting::vesting_balance(&1), 0); // Account 1 is still fully vested, and not negative + assert_eq!(Vesting::vesting_balance(&2), 0); // Account 2 has fully vested by block 30 + assert_eq!(Vesting::vesting_balance(&12), 0); // Account 2 has fully vested by block 30 + + }); + } + + #[test] + fn unvested_balance_should_not_transfer() { + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), 45); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 56), + pallet_balances::Error::::LiquidityRestrictions, + ); // Account 1 cannot send more than vested amount + }); + } + + #[test] + fn vested_balance_should_transfer() { + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), 45); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + }); + } + + #[test] + fn vested_balance_should_transfer_using_vest_other() { + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Vesting::vesting_balance(&1), 45); + assert_ok!(Vesting::vest_other(Some(2).into(), 1)); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + }); + } + + #[test] + fn extra_balance_should_transfer() { + ExtBuilder::default() + .existential_deposit(10) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); + assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); + + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal + + let user2_free_balance = Balances::free_balance(&2); + assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal + + // Account 1 has only 5 units vested at block 1 (plus 150 unvested) + assert_eq!(Vesting::vesting_balance(&1), 45); + assert_ok!(Vesting::vest(Some(1).into())); + assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + + // Account 2 has no units vested at block 1, but gained 100 + assert_eq!(Vesting::vesting_balance(&2), 200); + assert_ok!(Vesting::vest(Some(2).into())); + assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained + }); + } + + #[test] + fn liquid_funds_should_transfer_with_delayed_vesting() { + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + assert_eq!(System::block_number(), 1); + let user12_free_balance = Balances::free_balance(&12); + + assert_eq!(user12_free_balance, 2560); // Account 12 has free balance + // Account 12 has liquid funds + assert_eq!(Vesting::vesting_balance(&12), user12_free_balance - 256 * 5); + + // Account 12 has delayed vesting + let user12_vesting_schedule = VestingInfo { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_eq!(Vesting::vesting(&12), Some(user12_vesting_schedule)); + + // Account 12 can still send liquid funds + assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + }); + } +} diff --git a/primitives/allocator/Cargo.toml b/primitives/allocator/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..3cfc9a4de618a122969cdb88b4f56546ccc3a4a3 --- /dev/null +++ b/primitives/allocator/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sp-allocator" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +sp-std = { version = "2.0.0", path = "../std", default-features = false } +sp-core = { version = "2.0.0", path = "../core", default-features = false } +sp-wasm-interface = { version = "2.0.0", path = "../wasm-interface", default-features = false } +log = { version = "0.4.8", optional = true } +derive_more = { version = "0.99.2", optional = true } + +[features] +default = [ "std" ] +std = [ + "sp-std/std", + "sp-core/std", + "sp-wasm-interface/std", + "log", + "derive_more", +] diff --git a/primitives/allocator/src/error.rs b/primitives/allocator/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..9357bc456003bd440f6067c41e567d1c8ad302b8 --- /dev/null +++ b/primitives/allocator/src/error.rs @@ -0,0 +1,38 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +/// The error type used by the allocators. +#[derive(sp_core::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(derive_more::Display))] +pub enum Error { + /// Someone tried to allocate more memory than the allowed maximum per allocation. + #[cfg_attr(feature = "std", display(fmt="Requested allocation size is too large"))] + RequestedAllocationTooLarge, + /// Allocator run out of space. + #[cfg_attr(feature = "std", display(fmt="Allocator ran out of space"))] + AllocatorOutOfSpace, + /// Some other error occurred. + Other(&'static str) +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + _ => None, + } + } +} diff --git a/primitives/allocator/src/freeing_bump.rs b/primitives/allocator/src/freeing_bump.rs new file mode 100644 index 0000000000000000000000000000000000000000..caac9dd6c417d2968c559c3eb33ad4f99562cb49 --- /dev/null +++ b/primitives/allocator/src/freeing_bump.rs @@ -0,0 +1,791 @@ +// Copyright 2017-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! This module implements a freeing-bump allocator. +//! +//! The heap is a continuous linear memory and chunks are allocated using a bump allocator. +//! +//! ```ignore +//! +-------------+-------------------------------------------------+ +//! | | | +//! +-------------+-------------------------------------------------+ +//! ^ +//! |_ bumper +//! ``` +//! +//! Only allocations with sizes of power of two can be allocated. If the incoming request has a non +//! power of two size it is increased to the nearest power of two. The power of two of size is +//! referred as **an order**. +//! +//! Each allocation has a header immediately preceding to it. The header is always 8 bytes and can +//! be of two types: free and occupied. +//! +//! For implementing freeing we maintain a linked lists for each order. The maximum supported +//! allocation size is capped, therefore the number of orders and thus the linked lists is as well +//! limited. +//! +//! When the allocater serves an allocation request it first checks the linked list for the respective +//! order. If it doesn't have any free chunks, the allocator requests memory from the bump allocator. +//! In any case the order is stored in the header of the allocation. +//! +//! Upon deallocation we get the order of the allocation from its header and then add that +//! allocation to the linked list for the respective order. + +use crate::Error; +use sp_std::{convert::{TryFrom, TryInto}, ops::{Range, Index, IndexMut}}; +use sp_wasm_interface::{Pointer, WordSize}; + +/// The minimal alignment guaranteed by this allocator. The alignment of 8 is choosen because it is +/// the alignment guaranteed by wasm32. +const ALIGNMENT: u32 = 8; + +// The pointer returned by `allocate()` needs to fulfill the alignment +// requirement. In our case a pointer will always be a multiple of +// 8, as long as the first pointer is aligned to 8 bytes. +// This is because all pointers will contain a 8 byte prefix (the list +// index) and then a subsequent item of 2^x bytes, where x = [3..24]. +const N: usize = 22; +const MAX_POSSIBLE_ALLOCATION: u32 = 16777216; // 2^24 bytes +const MIN_POSSIBLE_ALLOCATION: u32 = 8; + +// Each pointer is prefixed with 8 bytes, which identify the list index +// to which it belongs. +const HEADER_SIZE: u32 = 8; + +/// Create an allocator error. +fn error(msg: &'static str) -> Error { + Error::Other(msg) +} + +/// A custom "trace" implementation that is only activated when `feature = std`. +/// +/// Uses `wasm-heap` as default target. +macro_rules! trace { + ( $( $args:expr ),+ ) => { + sp_std::if_std! { + log::trace!(target: "wasm-heap", $( $args ),+); + } + } +} + +/// The exponent for the power of two sized block adjusted to the minimum size. +/// +/// This way, if `MIN_POSSIBLE_ALLOCATION == 8`, we would get: +/// +/// power_of_two_size | order +/// 8 | 0 +/// 16 | 1 +/// 32 | 2 +/// 64 | 3 +/// +/// and so on. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct Order(u32); + +impl Order { + /// Create `Order` object from a raw order. + /// + /// Returns `Err` if it is greater than the maximum supported order. + fn from_raw(order: u32) -> Result { + if order < N as u32 { + Ok(Self(order)) + } else { + Err(error("invalid order")) + } + } + + /// Compute the order by the given size + /// + /// The size is clamped, so that the following holds: + /// + /// `MIN_POSSIBLE_ALLOCATION <= size <= MAX_POSSIBLE_ALLOCATION` + fn from_size(size: u32) -> Result { + let clamped_size = if size > MAX_POSSIBLE_ALLOCATION { + return Err(Error::RequestedAllocationTooLarge); + } else if size < MIN_POSSIBLE_ALLOCATION { + MIN_POSSIBLE_ALLOCATION + } else { + size + }; + + // Round the clamped size to the next power of two. + // + // It returns the unchanged value if the value is already a power of two. + let power_of_two_size = clamped_size.next_power_of_two(); + + // Compute the number of trailing zeroes to get the order. We adjust it by the number of + // trailing zeroes in the minimum possible allocation. + let order = power_of_two_size.trailing_zeros() - MIN_POSSIBLE_ALLOCATION.trailing_zeros(); + + Ok(Self(order)) + } + + /// Returns the corresponding size for this order. + /// + /// Note that it is always a power of two. + fn size(&self) -> u32 { + MIN_POSSIBLE_ALLOCATION << self.0 + } + + /// Extract the order as `u32`. + fn into_raw(self) -> u32 { + self.0 + } +} + +/// A marker for denoting the end of the linked list. +const EMPTY_MARKER: u32 = u32::max_value(); + +/// A link between headers in the free list. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Link { + /// Null, denotes that there is no next element. + Null, + /// Link to the next element represented as a pointer to the a header. + Ptr(u32), +} + +impl Link { + /// Creates a link from raw value. + fn from_raw(raw: u32) -> Self { + if raw != EMPTY_MARKER { + Self::Ptr(raw) + } else { + Self::Null + } + } + + /// Converts this link into a raw u32. + fn into_raw(self) -> u32 { + match self { + Self::Null => EMPTY_MARKER, + Self::Ptr(ptr) => ptr, + } + } +} + +/// A header of an allocation. +/// +/// The header is encoded in memory as follows. +/// +/// ## Free header +/// +/// ```ignore +/// 64 32 0 +// +--------------+-------------------+ +/// | 0 | next element link | +/// +--------------+-------------------+ +/// ``` +/// +/// ## Occupied header +/// +/// ```ignore +/// 64 32 0 +// +--------------+-------------------+ +/// | 1 | order | +/// +--------------+-------------------+ +/// ``` +#[derive(Clone, Debug, PartialEq, Eq)] +enum Header { + /// A free header contains a link to the next element to form a free linked list. + Free(Link), + /// An occupied header has attached order to know in which free list we should put the + /// allocation upon deallocation. + Occupied(Order), +} + +impl Header { + fn read_from(memory: &M, header_ptr: u32) -> Result { + let raw_header = memory.read_le_u64(header_ptr)?; + + // Check if the header represents an occupied or free allocation and extract the header data + // by trimming (and discarding) the high bits. + let occupied = raw_header & 0x00000001_00000000 != 0; + let header_data = raw_header as u32; + + Ok(if occupied { + Self::Occupied(Order::from_raw(header_data)?) + } else { + Self::Free(Link::from_raw(header_data)) + }) + } + + /// Write out this header to memory. + fn write_into(&self, memory: &mut M, header_ptr: u32) -> Result<(), Error> { + let (header_data, occupied_mask) = match *self { + Self::Occupied(order) => (order.into_raw(), 0x00000001_00000000), + Self::Free(link) => (link.into_raw(), 0x00000000_00000000), + }; + let raw_header = header_data as u64 | occupied_mask; + memory.write_le_u64(header_ptr, raw_header)?; + Ok(()) + } + + /// Returns the order of the allocation if this is an occupied header. + fn into_occupied(self) -> Option { + match self { + Self::Occupied(order) => Some(order), + _ => None, + } + } + + /// Returns the link to the next element in the free list if this is a free header. + fn into_free(self) -> Option { + match self { + Self::Free(link) => Some(link), + _ => None, + } + } +} + +/// This struct represents a collection of intrusive linked lists for each order. +struct FreeLists { + heads: [Link; N], +} + +impl FreeLists { + /// Creates the free empty lists. + fn new() -> Self { + Self { + heads: [Link::Null; N] + } + } + + /// Replaces a given link for the specified order and returns the old one. + fn replace(&mut self, order: Order, new: Link) -> Link { + let prev = self[order]; + self[order] = new; + prev + } +} + +impl Index for FreeLists { + type Output = Link; + fn index(&self, index: Order) -> &Link { + &self.heads[index.0 as usize] + } +} + +impl IndexMut for FreeLists { + fn index_mut(&mut self, index: Order) -> &mut Link { + &mut self.heads[index.0 as usize] + } +} + +/// An implementation of freeing bump allocator. +/// +/// Refer to the module-level documentation for further details. +pub struct FreeingBumpHeapAllocator { + bumper: u32, + free_lists: FreeLists, + total_size: u32, +} + +impl FreeingBumpHeapAllocator { + /// Creates a new allocation heap which follows a freeing-bump strategy. + /// The maximum size which can be allocated at once is 16 MiB. + /// + /// # Arguments + /// + /// - `heap_base` - the offset from the beginning of the linear memory where the heap starts. + pub fn new(heap_base: u32) -> Self { + let aligned_heap_base = (heap_base + ALIGNMENT - 1) / ALIGNMENT * ALIGNMENT; + + FreeingBumpHeapAllocator { + bumper: aligned_heap_base, + free_lists: FreeLists::new(), + total_size: 0, + } + } + + /// Gets requested number of bytes to allocate and returns a pointer. + /// The maximum size which can be allocated at once is 16 MiB. + /// There is no minimum size, but whatever size is passed into + /// this function is rounded to the next power of two. If the requested + /// size is below 8 bytes it will be rounded up to 8 bytes. + /// + /// # Arguments + /// + /// - `mem` - a slice representing the linear memory on which this allocator operates. + /// - `size` - size in bytes of the allocation request + pub fn allocate( + &mut self, + mem: &mut M, + size: WordSize, + ) -> Result, Error> { + let order = Order::from_size(size)?; + + let header_ptr: u32 = match self.free_lists[order] { + Link::Ptr(header_ptr) => { + assert!( + header_ptr + order.size() + HEADER_SIZE <= mem.size(), + "Pointer is looked up in list of free entries, into which + only valid values are inserted; qed" + ); + + // Remove this header from the free list. + let next_free = Header::read_from(mem, header_ptr)? + .into_free() + .ok_or_else(|| error("free list points to a occupied header"))?; + self.free_lists[order] = next_free; + + header_ptr + } + Link::Null => { + // Corresponding free list is empty. Allocate a new item. + self.bump(order.size() + HEADER_SIZE, mem.size())? + } + }; + + // Write the order in the occupied header. + Header::Occupied(order).write_into(mem, header_ptr)?; + + self.total_size += order.size() + HEADER_SIZE; + trace!("Heap size is {} bytes after allocation", self.total_size); + + Ok(Pointer::new(header_ptr + HEADER_SIZE)) + } + + /// Deallocates the space which was allocated for a pointer. + /// + /// # Arguments + /// + /// - `mem` - a slice representing the linear memory on which this allocator operates. + /// - `ptr` - pointer to the allocated chunk + pub fn deallocate(&mut self, mem: &mut M, ptr: Pointer) -> Result<(), Error> { + let header_ptr = u32::from(ptr) + .checked_sub(HEADER_SIZE) + .ok_or_else(|| error("Invalid pointer for deallocation"))?; + + let order = Header::read_from(mem, header_ptr)? + .into_occupied() + .ok_or_else(|| error("the allocation points to an empty header"))?; + + // Update the just freed header and knit it back to the free list. + let prev_head = self.free_lists.replace(order, Link::Ptr(header_ptr)); + Header::Free(prev_head).write_into(mem, header_ptr)?; + + // Do the total_size book keeping. + self.total_size = self + .total_size + .checked_sub(order.size() + HEADER_SIZE) + .ok_or_else(|| error("Unable to subtract from total heap size without overflow"))?; + trace!("Heap size is {} bytes after deallocation", self.total_size); + + Ok(()) + } + + /// Increases the `bumper` by `size`. + /// + /// Returns the `bumper` from before the increase. + /// Returns an `Error::AllocatorOutOfSpace` if the operation + /// would exhaust the heap. + fn bump(&mut self, size: u32, heap_end: u32) -> Result { + if self.bumper + size > heap_end { + return Err(Error::AllocatorOutOfSpace); + } + + let res = self.bumper; + self.bumper += size; + Ok(res) + } +} + +/// A trait for abstraction of accesses to linear memory. +pub trait Memory { + /// Read a u64 from the heap in LE form. Used to read heap allocation prefixes. + fn read_le_u64(&self, ptr: u32) -> Result; + /// Write a u64 to the heap in LE form. Used to write heap allocation prefixes. + fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), Error>; + /// Returns the full size of the memory. + fn size(&self) -> u32; +} + +impl Memory for [u8] { + fn read_le_u64(&self, ptr: u32) -> Result { + let range = + heap_range(ptr, 8, self.len()).ok_or_else(|| error("read out of heap bounds"))?; + let bytes = self[range] + .try_into() + .expect("[u8] slice of length 8 must be convertible to [u8; 8]"); + Ok(u64::from_le_bytes(bytes)) + } + fn write_le_u64(&mut self, ptr: u32, val: u64) -> Result<(), Error> { + let range = + heap_range(ptr, 8, self.len()).ok_or_else(|| error("write out of heap bounds"))?; + let bytes = val.to_le_bytes(); + &mut self[range].copy_from_slice(&bytes[..]); + Ok(()) + } + fn size(&self) -> u32 { + u32::try_from(self.len()).expect("size of Wasm linear memory is <2^32; qed") + } +} + +fn heap_range(offset: u32, length: u32, heap_len: usize) -> Option> { + let start = offset as usize; + let end = offset.checked_add(length)? as usize; + if end <= heap_len { + Some(start..end) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const PAGE_SIZE: u32 = 65536; + + /// Makes a pointer out of the given address. + fn to_pointer(address: u32) -> Pointer { + Pointer::new(address) + } + + #[test] + fn should_allocate_properly() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + // when + let ptr = heap.allocate(&mut mem[..], 1).unwrap(); + + // then + // returned pointer must start right after `HEADER_SIZE` + assert_eq!(ptr, to_pointer(HEADER_SIZE)); + } + + #[test] + fn should_always_align_pointers_to_multiples_of_8() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(13); + + // when + let ptr = heap.allocate(&mut mem[..], 1).unwrap(); + + // then + // the pointer must start at the next multiple of 8 from 13 + // + the prefix of 8 bytes. + assert_eq!(ptr, to_pointer(24)); + } + + #[test] + fn should_increment_pointers_properly() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + // when + let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); + let ptr2 = heap.allocate(&mut mem[..], 9).unwrap(); + let ptr3 = heap.allocate(&mut mem[..], 1).unwrap(); + + // then + // a prefix of 8 bytes is prepended to each pointer + assert_eq!(ptr1, to_pointer(HEADER_SIZE)); + + // the prefix of 8 bytes + the content of ptr1 padded to the lowest possible + // item size of 8 bytes + the prefix of ptr1 + assert_eq!(ptr2, to_pointer(24)); + + // ptr2 + its content of 16 bytes + the prefix of 8 bytes + assert_eq!(ptr3, to_pointer(24 + 16 + HEADER_SIZE)); + } + + #[test] + fn should_free_properly() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); + // the prefix of 8 bytes is prepended to the pointer + assert_eq!(ptr1, to_pointer(HEADER_SIZE)); + + let ptr2 = heap.allocate(&mut mem[..], 1).unwrap(); + // the prefix of 8 bytes + the content of ptr 1 is prepended to the pointer + assert_eq!(ptr2, to_pointer(24)); + + // when + heap.deallocate(&mut mem[..], ptr2).unwrap(); + + // then + // then the heads table should contain a pointer to the + // prefix of ptr2 in the leftmost entry + assert_eq!(heap.free_lists.heads[0], Link::Ptr(u32::from(ptr2) - HEADER_SIZE)); + } + + #[test] + fn should_deallocate_and_reallocate_properly() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let padded_offset = 16; + let mut heap = FreeingBumpHeapAllocator::new(13); + + let ptr1 = heap.allocate(&mut mem[..], 1).unwrap(); + // the prefix of 8 bytes is prepended to the pointer + assert_eq!(ptr1, to_pointer(padded_offset + HEADER_SIZE)); + + let ptr2 = heap.allocate(&mut mem[..], 9).unwrap(); + // the padded_offset + the previously allocated ptr (8 bytes prefix + + // 8 bytes content) + the prefix of 8 bytes which is prepended to the + // current pointer + assert_eq!(ptr2, to_pointer(padded_offset + 16 + HEADER_SIZE)); + + // when + heap.deallocate(&mut mem[..], ptr2).unwrap(); + let ptr3 = heap.allocate(&mut mem[..], 9).unwrap(); + + // then + // should have re-allocated + assert_eq!(ptr3, to_pointer(padded_offset + 16 + HEADER_SIZE)); + assert_eq!(heap.free_lists.heads, [Link::Null; N]); + } + + #[test] + fn should_build_linked_list_of_free_areas_properly() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + let ptr1 = heap.allocate(&mut mem[..], 8).unwrap(); + let ptr2 = heap.allocate(&mut mem[..], 8).unwrap(); + let ptr3 = heap.allocate(&mut mem[..], 8).unwrap(); + + // when + heap.deallocate(&mut mem[..], ptr1).unwrap(); + heap.deallocate(&mut mem[..], ptr2).unwrap(); + heap.deallocate(&mut mem[..], ptr3).unwrap(); + + // then + assert_eq!(heap.free_lists.heads[0], Link::Ptr(u32::from(ptr3) - HEADER_SIZE)); + + let ptr4 = heap.allocate(&mut mem[..], 8).unwrap(); + assert_eq!(ptr4, ptr3); + + assert_eq!(heap.free_lists.heads[0], Link::Ptr(u32::from(ptr2) - HEADER_SIZE)); + } + + #[test] + fn should_not_allocate_if_too_large() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(13); + + // when + let ptr = heap.allocate(&mut mem[..], PAGE_SIZE - 13); + + // then + match ptr.unwrap_err() { + Error::AllocatorOutOfSpace => {}, + e => panic!("Expected allocator out of space error, got: {:?}", e), + } + } + + #[test] + fn should_not_allocate_if_full() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + let ptr1 = heap.allocate(&mut mem[..], (PAGE_SIZE / 2) - HEADER_SIZE).unwrap(); + assert_eq!(ptr1, to_pointer(HEADER_SIZE)); + + // when + let ptr2 = heap.allocate(&mut mem[..], PAGE_SIZE / 2); + + // then + // there is no room for another half page incl. its 8 byte prefix + match ptr2.unwrap_err() { + Error::AllocatorOutOfSpace => {}, + e => panic!("Expected allocator out of space error, got: {:?}", e), + } + } + + #[test] + fn should_allocate_max_possible_allocation_size() { + // given + let mut mem = vec![0u8; (MAX_POSSIBLE_ALLOCATION + PAGE_SIZE) as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + // when + let ptr = heap.allocate(&mut mem[..], MAX_POSSIBLE_ALLOCATION).unwrap(); + + // then + assert_eq!(ptr, to_pointer(HEADER_SIZE)); + } + + #[test] + fn should_not_allocate_if_requested_size_too_large() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + // when + let ptr = heap.allocate(&mut mem[..], MAX_POSSIBLE_ALLOCATION + 1); + + // then + match ptr.unwrap_err() { + Error::RequestedAllocationTooLarge => {}, + e => panic!("Expected allocation size too large error, got: {:?}", e), + } + } + + #[test] + fn should_return_error_when_bumper_greater_than_heap_size() { + // given + let mut mem = [0u8; 64]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + let ptr1 = heap.allocate(&mut mem[..], 32).unwrap(); + assert_eq!(ptr1, to_pointer(HEADER_SIZE)); + heap.deallocate(&mut mem[..], ptr1).expect("failed freeing ptr1"); + assert_eq!(heap.total_size, 0); + assert_eq!(heap.bumper, 40); + + let ptr2 = heap.allocate(&mut mem[..], 16).unwrap(); + assert_eq!(ptr2, to_pointer(48)); + heap.deallocate(&mut mem[..], ptr2).expect("failed freeing ptr2"); + assert_eq!(heap.total_size, 0); + assert_eq!(heap.bumper, 64); + + // when + // the `bumper` value is equal to `size` here and any + // further allocation which would increment the bumper must fail. + // we try to allocate 8 bytes here, which will increment the + // bumper since no 8 byte item has been allocated+freed before. + let ptr = heap.allocate(&mut mem[..], 8); + + // then + match ptr.unwrap_err() { + Error::AllocatorOutOfSpace => {}, + e => panic!("Expected allocator out of space error, got: {:?}", e), + } + } + + #[test] + fn should_include_prefixes_in_total_heap_size() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(1); + + // when + // an item size of 16 must be used then + heap.allocate(&mut mem[..], 9).unwrap(); + + // then + assert_eq!(heap.total_size, HEADER_SIZE + 16); + } + + #[test] + fn should_calculate_total_heap_size_to_zero() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(13); + + // when + let ptr = heap.allocate(&mut mem[..], 42).unwrap(); + assert_eq!(ptr, to_pointer(16 + HEADER_SIZE)); + heap.deallocate(&mut mem[..], ptr).unwrap(); + + // then + assert_eq!(heap.total_size, 0); + } + + #[test] + fn should_calculate_total_size_of_zero() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + let mut heap = FreeingBumpHeapAllocator::new(19); + + // when + for _ in 1..10 { + let ptr = heap.allocate(&mut mem[..], 42).unwrap(); + heap.deallocate(&mut mem[..], ptr).unwrap(); + } + + // then + assert_eq!(heap.total_size, 0); + } + + #[test] + fn should_read_and_write_u64_correctly() { + // given + let mut mem = [0u8; PAGE_SIZE as usize]; + + // when + Memory::write_le_u64(mem.as_mut(), 40, 4480113).unwrap(); + + // then + let value = Memory::read_le_u64(mem.as_mut(), 40).unwrap(); + assert_eq!(value, 4480113); + } + + #[test] + fn should_get_item_size_from_order() { + // given + let raw_order = 0; + + // when + let item_size = Order::from_raw(raw_order).unwrap().size(); + + // then + assert_eq!(item_size, 8); + } + + #[test] + fn should_get_max_item_size_from_index() { + // given + let raw_order = 21; + + // when + let item_size = Order::from_raw(raw_order).unwrap().size(); + + // then + assert_eq!(item_size as u32, MAX_POSSIBLE_ALLOCATION); + } + + #[test] + fn deallocate_needs_to_maintain_linked_list() { + let mut mem = [0u8; 8 * 2 * 4 + ALIGNMENT as usize]; + let mut heap = FreeingBumpHeapAllocator::new(0); + + // Allocate and free some pointers + let ptrs = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + ptrs.into_iter().for_each(|ptr| heap.deallocate(&mut mem[..], ptr).unwrap()); + + // Second time we should be able to allocate all of them again. + let _ = (0..4).map(|_| heap.allocate(&mut mem[..], 8).unwrap()).collect::>(); + } + + #[test] + fn header_read_write() { + let roundtrip = |header: Header| { + let mut memory = [0u8; 32]; + header.write_into(memory.as_mut(), 0).unwrap(); + + let read_header = Header::read_from(memory.as_mut(), 0).unwrap(); + assert_eq!(header, read_header); + }; + + roundtrip(Header::Occupied(Order(0))); + roundtrip(Header::Occupied(Order(1))); + roundtrip(Header::Free(Link::Null)); + roundtrip(Header::Free(Link::Ptr(0))); + roundtrip(Header::Free(Link::Ptr(4))); + } +} diff --git a/primitives/allocator/src/lib.rs b/primitives/allocator/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..0efadbc7f6d60299ee5104600884b3f34692d8e5 --- /dev/null +++ b/primitives/allocator/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Collection of allocator implementations. +//! +//! This crate provides the following allocator implementations: +//! - A freeing-bump allocator: [`FreeingBumpHeapAllocator`](freeing_bump::FreeingBumpHeapAllocator) + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +mod error; +mod freeing_bump; + +pub use freeing_bump::FreeingBumpHeapAllocator; +pub use error::Error; diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index b9f736570ac4d785310d6e8f3b91b87caa1070ea..f6966f94622db6e4ab51cfa54e58fbd32e65ed87 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-api" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index 122c889add49988c0bdd0198bc46e79222d9b1c2..66c2c5dba82d02999b3ca4e938a6961500a787b3 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-api-proc-macro" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true @@ -11,7 +12,7 @@ proc-macro = true quote = "1.0.2" syn = { version = "1.0.8", features = ["full", "fold", "extra-traits", "visit"] } proc-macro2 = "1.0.6" -blake2-rfc = "0.2.18" +blake2-rfc = { version = "0.2.18", default-features = false } proc-macro-crate = "0.1.4" # Required for the doc tests diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 5892063bd1fc4357ea8c2387042719798865ca40..c193c9dc5ef3cc97331059c959734806b6d0918c 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-api-test" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-api = { version = "2.0.0", path = "../" } diff --git a/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr b/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr index ca7e519344f3ededc5c426d5e3e468bfdae933ad..4f112e91bb7a9c45eec3692e7c4dc3c9f8fc2790 100644 --- a/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr +++ b/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr @@ -5,10 +5,10 @@ error[E0053]: method `test` has an incompatible type for trait | --- type in trait ... 19 | fn test(data: String) {} - | ^^^^^^ expected u64, found struct `std::string::String` + | ^^^^^^ expected `u64`, found struct `std::string::String` | - = note: expected type `fn(u64)` - found type `fn(std::string::String)` + = note: expected fn pointer `fn(u64)` + found fn pointer `fn(std::string::String)` error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait --> $DIR/impl_incorrect_method_signature.rs:17:1 @@ -23,7 +23,7 @@ error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for tr 17 | sp_api::impl_runtime_apis! { | -^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | _expected u64, found struct `std::string::String` + | _expected `u64`, found struct `std::string::String` | | 18 | | impl self::Api for Runtime { 19 | | fn test(data: String) {} @@ -33,8 +33,8 @@ error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for tr 33 | | } | |_- in this macro invocation | - = note: expected type `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec) -> std::result::Result, >::Error>` - found type `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec) -> std::result::Result, >::Error>` + = note: expected fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` + found fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` error[E0308]: mismatched types --> $DIR/impl_incorrect_method_signature.rs:17:1 @@ -48,17 +48,11 @@ error[E0308]: mismatched types 33 | | } | | ^ | | | - | |_expected u64, found struct `std::string::String` + | |_expected `u64`, found struct `std::string::String` | in this macro invocation - | - = note: expected type `u64` - found type `std::string::String` error[E0308]: mismatched types --> $DIR/impl_incorrect_method_signature.rs:19:11 | 19 | fn test(data: String) {} - | ^^^^ expected u64, found struct `std::string::String` - | - = note: expected type `u64` - found type `std::string::String` + | ^^^^ expected `u64`, found struct `std::string::String` diff --git a/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr b/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr index b793d832ff2244da5351617ae9ad39f53e910e86..1d7d0a78a86df87ac01b1d30a757a5a0695e265e 100644 --- a/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr +++ b/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr @@ -5,10 +5,10 @@ error[E0053]: method `test` has an incompatible type for trait | --- type in trait ... 19 | fn test(data: &u64) { - | ^^^^ expected u64, found &u64 + | ^^^^ expected `u64`, found `&u64` | - = note: expected type `fn(u64)` - found type `fn(&u64)` + = note: expected fn pointer `fn(u64)` + found fn pointer `fn(&u64)` error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait --> $DIR/type_reference_in_impl_runtime_apis_call.rs:17:1 @@ -23,7 +23,7 @@ error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for tr 17 | sp_api::impl_runtime_apis! { | -^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | _expected u64, found &u64 + | _expected `u64`, found `&u64` | | 18 | | impl self::Api for Runtime { 19 | | fn test(data: &u64) { @@ -33,8 +33,8 @@ error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for tr 35 | | } | |_- in this macro invocation | - = note: expected type `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec) -> std::result::Result, >::Error>` - found type `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<&u64>, std::vec::Vec) -> std::result::Result, >::Error>` + = note: expected fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` + found fn pointer `fn(&RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall>, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId<__SR_API_BLOCK__>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<&u64>, std::vec::Vec<_>) -> std::result::Result<_, _>` error[E0308]: mismatched types --> $DIR/type_reference_in_impl_runtime_apis_call.rs:17:1 @@ -48,11 +48,8 @@ error[E0308]: mismatched types 35 | | } | | ^ | | | - | |_expected u64, found &u64 + | |_expected `u64`, found `&u64` | in this macro invocation - | - = note: expected type `u64` - found type `&u64` error[E0308]: mismatched types --> $DIR/type_reference_in_impl_runtime_apis_call.rs:19:11 @@ -60,8 +57,5 @@ error[E0308]: mismatched types 19 | fn test(data: &u64) { | ^^^^^^^ | | - | expected u64, found &u64 + | expected `u64`, found `&u64` | help: consider removing the borrow: `data` - | - = note: expected type `u64` - found type `&u64` diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index f6487ebc298c39a8aee2ddfaba9f71876659ca02..1fa9a6631c4efd969e7bcfada6c29bbcfc0e19df 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" description = "Provides facilities for generating application specific crypto wrapper types." +license = "GPL-3.0" [dependencies] sp-core = { version = "2.0.0", default-features = false, path = "../core" } @@ -20,5 +21,9 @@ std = [ "full_crypto", "sp-core/std", "codec/std", "serde", "sp-std/std", "sp-io # or Intel SGX. # For the regular wasm runtime builds this should not be used. full_crypto = [ - "sp-core/full_crypto" + "sp-core/full_crypto", + # Don't add `panic_handler` and `alloc_error_handler` since they are expected to be provided + # by the user anyway. + "sp-io/disable_panic_handler", + "sp-io/disable_oom", ] diff --git a/primitives/application-crypto/src/ed25519.rs b/primitives/application-crypto/src/ed25519.rs index c92c70495e6cb56a5332ceba1161e7e3ef2f932d..414715a10684c18b49a33fe6bb1fc08c20639d4a 100644 --- a/primitives/application-crypto/src/ed25519.rs +++ b/primitives/application-crypto/src/ed25519.rs @@ -53,4 +53,8 @@ impl RuntimePublic for Public { fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { sp_io::crypto::ed25519_verify(&signature, msg.as_ref(), self) } + + fn to_raw_vec(&self) -> Vec { + sp_core::crypto::Public::to_raw_vec(self) + } } diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index db1bdf0c7a523522ce43efbd244ca293f9dc80af..fed4a6bf8e92ef65ad3818053e406261f8c236fb 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -301,6 +301,10 @@ macro_rules! app_crypto_public_common { fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { <$public as $crate::RuntimePublic>::verify(self.as_ref(), msg, &signature.as_ref()) } + + fn to_raw_vec(&self) -> $crate::Vec { + <$public as $crate::RuntimePublic>::to_raw_vec(&self.0) + } } } } diff --git a/primitives/application-crypto/src/sr25519.rs b/primitives/application-crypto/src/sr25519.rs index 34d9797bd4ea8feb15ca5fc5bbc972b7f21fcb34..59c6f19b6f2668939054127347f5a87ed6989907 100644 --- a/primitives/application-crypto/src/sr25519.rs +++ b/primitives/application-crypto/src/sr25519.rs @@ -53,4 +53,8 @@ impl RuntimePublic for Public { fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { sp_io::crypto::sr25519_verify(&signature, msg.as_ref(), self) } + + fn to_raw_vec(&self) -> Vec { + sp_core::crypto::Public::to_raw_vec(self) + } } diff --git a/primitives/application-crypto/src/traits.rs b/primitives/application-crypto/src/traits.rs index 4b500bb2a63c9adfe4e45790b0577f9a5b01c084..2af039a88df4c3734fbce447072ebcd051ebcc41 100644 --- a/primitives/application-crypto/src/traits.rs +++ b/primitives/application-crypto/src/traits.rs @@ -106,10 +106,13 @@ pub trait RuntimePublic: Sized { /// Verify that the given signature matches the given message using this public key. fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; + + /// Returns `Self` as raw vec. + fn to_raw_vec(&self) -> Vec; } /// A runtime interface for an application's public key. -pub trait RuntimeAppPublic: Sized { +pub trait RuntimeAppPublic: Sized { /// An identifier for this application-specific key type. const ID: KeyTypeId; @@ -136,6 +139,9 @@ pub trait RuntimeAppPublic: Sized { /// Verify that the given signature matches the given message using this public key. fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; + + /// Returns `Self` as raw vec. + fn to_raw_vec(&self) -> Vec; } /// Something that bound to a fixed `RuntimeAppPublic`. diff --git a/primitives/application-crypto/test/Cargo.toml b/primitives/application-crypto/test/Cargo.toml index 1931e6f84be114253680608c0f0f5fbb53d5f89d..de4412db2d7cb4b011a668f647b900125347bfd7 100644 --- a/primitives/application-crypto/test/Cargo.toml +++ b/primitives/application-crypto/test/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" description = "Integration tests for application-crypto" +license = "GPL-3.0" publish = false [dependencies] diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index 71be14862a0ab0ccbc81c5f2823f24bec0e7c426..995e36d5c905479e2eec506d134d35f25145f8c3 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-arithmetic" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } @@ -13,7 +14,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-debug-derive = { version = "2.0.0", default-features = false, path = "../../primitives/debug-derive" } [dev-dependencies] -primitive-types = "0.6.0" +primitive-types = "0.6.2" rand = "0.7.2" criterion = "0.3" diff --git a/primitives/arithmetic/benches/bench.rs b/primitives/arithmetic/benches/bench.rs index 1025e883ef69e08d8e1ac270211d0a69afbff050..1dbcf260affc33cd6962ebd64dd58d8636e581ad 100644 --- a/primitives/arithmetic/benches/bench.rs +++ b/primitives/arithmetic/benches/bench.rs @@ -73,8 +73,8 @@ fn bench_division(c: &mut Criterion) { } criterion_group!{ - name = benches; - config = Criterion::default(); - targets = bench_addition, bench_subtraction, bench_multiplication, bench_division + name = benches; + config = Criterion::default(); + targets = bench_addition, bench_subtraction, bench_multiplication, bench_division } criterion_main!(benches); diff --git a/primitives/arithmetic/fuzzer/Cargo.lock b/primitives/arithmetic/fuzzer/Cargo.lock index de8a764f1f9b14cea92d0e46f40f62205abeacfd..b196418375c59ee17bb8ba03680011d5b0efd1da 100644 --- a/primitives/arithmetic/fuzzer/Cargo.lock +++ b/primitives/arithmetic/fuzzer/Cargo.lock @@ -4,316 +4,326 @@ name = "arbitrary" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" [[package]] name = "arrayvec" -version = "0.4.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "autocfg" -version = "0.1.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "bitvec" -version = "0.14.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" [[package]] name = "byte-slice-cast" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" [[package]] name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" [[package]] name = "c2-chacha" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "fixed-hash" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "rand", + "rustc-hex", + "static_assertions", ] [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "honggfuzz" version = "0.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c27b4aa3049d6d10d8e33d52c9d03ca9aec18f8a449b246f8c4a5b0c10fb34" dependencies = [ - "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arbitrary", + "lazy_static", + "memmap", ] [[package]] name = "impl-codec" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" dependencies = [ - "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", ] [[package]] name = "integer-sqrt" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.65" +version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" [[package]] name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "num-bigint" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "parity-scale-codec" -version = "1.0.6" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec", + "bitvec", + "byte-slice-cast", + "parity-scale-codec-derive", + "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" dependencies = [ - "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "primitive-types" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" dependencies = [ - "fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash", + "impl-codec", + "uint", ] [[package]] name = "proc-macro-crate" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" dependencies = [ - "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.13" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha", + "rand_core", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core", ] [[package]] name = "rustc-hex" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "serde" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" dependencies = [ - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "sp-arithmetic" version = "2.0.0" dependencies = [ - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 2.0.0", - "sp-std 2.0.0", + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "serde", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-arithmetic-fuzzer" version = "2.0.0" dependencies = [ - "honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 2.0.0", + "honggfuzz", + "num-bigint", + "num-traits", + "primitive-types", + "sp-arithmetic", ] [[package]] name = "sp-debug-derive" version = "2.0.0" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -324,126 +334,70 @@ version = "2.0.0" name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.8" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "toml" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "uint" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", ] -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9633b74910e1870f50f5af189b08487195cdb83c0e27a71d6f64d5e09dd0538b" -"checksum byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f6209f3b2c1edea170002e016d5ead6903d3bb0a846477f53bbeb614967a52a9" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72fe7539e2c5692c6989f2f9c0457e42f1e5768f96b85c87d273574670ae459f" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" -"checksum honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)" = "24c27b4aa3049d6d10d8e33d52c9d03ca9aec18f8a449b246f8c4a5b0c10fb34" -"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" -"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "001fbbb956d8593f321c7a784f64d16b2c99b2657823976eea729006ad2c3668" -"checksum parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42af752f59119656fa3cb31e8852ed24e895b968c0bdb41847da7f0cea6d155f" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0253db64c26d8b4e7896dd2063b516d2a1b9e0a5da26b5b78335f236d1e9522" -"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" -"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" -"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" -"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" -"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" -"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index e8568db3707fb2fb8b13d6f332dadf4073ad2fe5..19d677f744502f1fdceb72920cf0d35a97760427 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -3,11 +3,12 @@ name = "sp-arithmetic-fuzzer" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-arithmetic = { version = "2.0.0", path = ".." } honggfuzz = "0.5" -primitive-types = "0.6" +primitive-types = "0.6.2" num-bigint = "0.2" num-traits = "0.2" diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index 0beef84758febd23454212a3c3031eedfb5f1b6b..c2feae00b74ff9a3e0d016558c2dfbaee2e1dffd 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -40,5 +40,5 @@ mod fixed64; mod rational128; pub use fixed64::Fixed64; -pub use per_things::{Percent, Permill, Perbill, Perquintill}; +pub use per_things::{PerThing, Percent, Permill, Perbill, Perquintill}; pub use rational128::Rational128; diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 554f3c3c2128b61b84a14bf563379220b4b803b9..cbb804baf5d49e97ce7d9817b7169dffe6e87643 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -19,50 +19,93 @@ use serde::{Serialize, Deserialize}; use sp_std::{ops, prelude::*, convert::TryInto}; use codec::{Encode, Decode, CompactAs}; -use crate::traits::{SaturatedConversion, UniqueSaturatedInto, Saturating}; +use crate::traits::{ + SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic, +}; use sp_debug_derive::RuntimeDebug; +/// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per +/// `X`_. +pub trait PerThing: Sized + Saturating + Copy { + /// The data type used to build this per-thingy. + type Inner: BaseArithmetic + Copy; + + /// accuracy of this type + const ACCURACY: Self::Inner; + + /// NoThing + fn zero() -> Self; + + /// `true` if this is nothing. + fn is_zero(&self) -> bool; + + /// Everything. + fn one() -> Self; + + /// Consume self and deconstruct into a raw numeric type. + fn deconstruct(self) -> Self::Inner; + + /// From an explicitly defined number of parts per maximum of the type. + fn from_parts(parts: Self::Inner) -> Self; + + /// Converts a percent into `Self`. Equal to `x / 100`. + fn from_percent(x: Self::Inner) -> Self; + + /// Return the product of multiplication of this value by itself. + fn square(self) -> Self; + + /// Converts a fraction into `Self`. + #[cfg(feature = "std")] + fn from_fraction(x: f64) -> Self; + + /// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow. + /// + /// The computation of this approximation is performed in the generic type `N`. Given + /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for + /// perbill), this can only work if `N == M` or `N: From + TryInto`. + fn from_rational_approximation(p: N, q: N) -> Self + where N: Clone + Ord + From + TryInto + ops::Div; +} + macro_rules! implement_per_thing { ($name:ident, $test_mod:ident, [$($test_units:tt),+], $max:tt, $type:ty, $upper_type:ty, $title:expr $(,)?) => { /// A fixed point representation of a number between in the range [0, 1]. /// #[doc = $title] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)] + #[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)] pub struct $name($type); - impl $name { + impl PerThing for $name { + type Inner = $type; + + /// The accuracy of this type. + const ACCURACY: Self::Inner = $max; + /// Nothing. - pub fn zero() -> Self { Self(0) } + fn zero() -> Self { Self(0) } /// `true` if this is nothing. - pub fn is_zero(&self) -> bool { self.0 == 0 } + fn is_zero(&self) -> bool { self.0 == 0 } /// Everything. - pub fn one() -> Self { Self($max) } + fn one() -> Self { Self($max) } /// Consume self and deconstruct into a raw numeric type. - pub fn deconstruct(self) -> $type { self.0 } - - /// Return the scale at which this per-thing is working. - pub const fn accuracy() -> $type { $max } + fn deconstruct(self) -> Self::Inner { self.0 } /// From an explicitly defined number of parts per maximum of the type. - /// - /// This can be called at compile time. - pub const fn from_parts(parts: $type) -> Self { + fn from_parts(parts: Self::Inner) -> Self { Self([parts, $max][(parts > $max) as usize]) } /// Converts a percent into `Self`. Equal to `x / 100`. - /// - /// This can be created at compile time. - pub const fn from_percent(x: $type) -> Self { + fn from_percent(x: Self::Inner) -> Self { Self([x, 100][(x > 100) as usize] * ($max / 100)) } /// Return the product of multiplication of this value by itself. - pub fn square(self) -> Self { + fn square(self) -> Self { // both can be safely casted and multiplied. let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type; let q: $upper_type = <$upper_type>::from($max) * <$upper_type>::from($max); @@ -71,33 +114,33 @@ macro_rules! implement_per_thing { /// Converts a fraction into `Self`. #[cfg(feature = "std")] - pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) } + fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as Self::Inner) } /// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow. /// /// The computation of this approximation is performed in the generic type `N`. Given /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for /// perbill), this can only work if `N == M` or `N: From + TryInto`. - pub fn from_rational_approximation(p: N, q: N) -> Self - where N: Clone + Ord + From<$type> + TryInto<$type> + ops::Div + fn from_rational_approximation(p: N, q: N) -> Self + where N: Clone + Ord + From + TryInto + ops::Div { // q cannot be zero. - let q = q.max((1 as $type).into()); + let q = q.max((1 as Self::Inner).into()); // p should not be bigger than q. let p = p.min(q.clone()); - let factor = (q.clone() / $max.into()).max((1 as $type).into()); + let factor = (q.clone() / $max.into()).max((1 as Self::Inner).into()); // q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow. - // this implies that $type must be able to fit 2 * $max. - let q_reduce: $type = (q / factor.clone()) + // this implies that Self::Inner must be able to fit 2 * $max. + let q_reduce: Self::Inner = (q / factor.clone()) .try_into() .map_err(|_| "Failed to convert") .expect( "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ does not satisfy this; qed" ); - let p_reduce: $type = (p / factor.clone()) + let p_reduce: Self::Inner = (p / factor.clone()) .try_into() .map_err(|_| "Failed to convert") .expect( @@ -105,14 +148,39 @@ macro_rules! implement_per_thing { does not satisfy this; qed" ); - // `p_reduced` and `q_reduced` are withing $type. Mul by another $max will always - // fit in $upper_type. This is guaranteed by the macro tests. + // `p_reduced` and `q_reduced` are withing Self::Inner. Mul by another $max will + // always fit in $upper_type. This is guaranteed by the macro tests. let part = p_reduce as $upper_type * <$upper_type>::from($max) / q_reduce as $upper_type; - $name(part as $type) + $name(part as Self::Inner) + } + } + + /// Implement const functions + impl $name { + /// From an explicitly defined number of parts per maximum of the type. + /// + /// This can be called at compile time. + pub const fn from_parts(parts: $type) -> Self { + Self([parts, $max][(parts > $max) as usize]) + } + + /// Converts a percent into `Self`. Equal to `x / 100`. + /// + /// This can be created at compile time. + pub const fn from_percent(x: $type) -> Self { + Self([x, 100][(x > 100) as usize] * ($max / 100)) + } + + /// Everything. + /// + /// To avoid having to import `PerThing` when one needs to be used in test mocks. + #[cfg(feature = "std")] + pub fn one() -> Self { + ::one() } } @@ -190,7 +258,7 @@ macro_rules! implement_per_thing { #[cfg(test)] mod $test_mod { use codec::{Encode, Decode}; - use super::{$name, Saturating, RuntimeDebug}; + use super::{$name, Saturating, RuntimeDebug, PerThing}; use crate::traits::Zero; @@ -248,7 +316,7 @@ macro_rules! implement_per_thing { // some really basic stuff assert_eq!($name::zero(), $name::from_parts(Zero::zero())); assert_eq!($name::one(), $name::from_parts($max)); - assert_eq!($name::accuracy(), $max); + assert_eq!($name::ACCURACY, $max); assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero())); assert_eq!($name::from_percent(10), $name::from_parts($max / 10)); assert_eq!($name::from_percent(100), $name::from_parts($max)); diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index ab525527e90644ae8cc6da0d3f45c9af013a0559..75adf0e1363dd6c460707d06a4852f2f39c4fbe2 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Primitives for the runtime modules. +//! Primitive traits for the runtime arithmetic. use sp_std::{self, convert::{TryFrom, TryInto}}; use codec::HasCompact; @@ -28,44 +28,55 @@ use sp_std::ops::{ RemAssign, Shl, Shr }; -/// A meta trait for arithmetic. -/// -/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to -/// be able to represent at least `u32` values without loss, hence the trait implies `From` -/// and smaller ints. All other conversions are fallible. -pub trait SimpleArithmetic: +/// A meta trait for arithmetic type operations, regardless of any limitation on size. +pub trait BaseArithmetic: + From + Zero + One + IntegerSquareRoot + - From + From + From + TryInto + TryInto + TryInto + - TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + - UniqueSaturatedInto + UniqueSaturatedInto + UniqueSaturatedInto + - UniqueSaturatedFrom + UniqueSaturatedInto + UniqueSaturatedFrom + UniqueSaturatedInto + Add + AddAssign + Sub + SubAssign + Mul + MulAssign + Div + DivAssign + Rem + RemAssign + Shl + Shr + - CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + - Saturating + PartialOrd + Ord + Bounded + - HasCompact + Sized + CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + Saturating + + PartialOrd + Ord + Bounded + HasCompact + Sized + + TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + + TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto {} + impl + Zero + One + IntegerSquareRoot + - From + From + From + TryInto + TryInto + TryInto + - TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + - UniqueSaturatedInto + UniqueSaturatedInto + UniqueSaturatedInto + - UniqueSaturatedFrom + UniqueSaturatedInto + UniqueSaturatedFrom + - UniqueSaturatedInto + UniqueSaturatedFrom + UniqueSaturatedInto + Add + AddAssign + Sub + SubAssign + Mul + MulAssign + Div + DivAssign + Rem + RemAssign + Shl + Shr + - CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + - Saturating + PartialOrd + Ord + Bounded + - HasCompact + Sized -> SimpleArithmetic for T {} + CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + Saturating + + PartialOrd + Ord + Bounded + HasCompact + Sized + + TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + + TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto +> BaseArithmetic for T {} + +/// A meta trait for arithmetic. +/// +/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to +/// be able to represent at least `u32` values without loss, hence the trait implies `From` +/// and smaller integers. All other conversions are fallible. +pub trait AtLeast32Bit: BaseArithmetic + From + From {} + +impl + From> AtLeast32Bit for T {} /// Just like `From` except that if the source value is too big to fit into the destination type /// then it'll saturate the destination. diff --git a/primitives/authority-discovery/Cargo.toml b/primitives/authority-discovery/Cargo.toml index 516ea413acd55cc5e6820538140dbc8f3da11ce2..44a7cad15559531edb91f5853b57d1a6c9b562b3 100644 --- a/primitives/authority-discovery/Cargo.toml +++ b/primitives/authority-discovery/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] description = "Authority discovery primitives" edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } diff --git a/primitives/authorship/Cargo.toml b/primitives/authorship/Cargo.toml index d8ddbb7a9218a07584fd9b363a0dbe5c26e1e1d5..7dc5fcfc95f1e1a8ff95c98cf8f7d4d410209a21 100644 --- a/primitives/authorship/Cargo.toml +++ b/primitives/authorship/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] description = "Authorship primitives" edition = "2018" +license = "GPL-3.0" [dependencies] sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } diff --git a/primitives/block-builder/Cargo.toml b/primitives/block-builder/Cargo.toml index 5bbe9ea123faf07b880a42a378dcca0468dd1789..1700209ec46c22444c69ee52697a5f9cb7b21ab8 100644 --- a/primitives/block-builder/Cargo.toml +++ b/primitives/block-builder/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-block-builder" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/blockchain/Cargo.toml b/primitives/blockchain/Cargo.toml index a3c442094daafdf378b1467d9398108086a0f0b4..8b93436f9a3dc116aed1f8f19c9bf7a4b83c666a 100644 --- a/primitives/blockchain/Cargo.toml +++ b/primitives/blockchain/Cargo.toml @@ -3,11 +3,12 @@ name = "sp-blockchain" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4.8" lru = "0.4.0" -parking_lot = "0.9.0" +parking_lot = "0.10.0" derive_more = "0.99.2" codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sp-consensus = { version = "0.8", path = "../consensus/common" } diff --git a/primitives/blockchain/src/backend.rs b/primitives/blockchain/src/backend.rs index 101dcd1443d6b4d501ab6eae71b372646b64da48..35cac1e481120e2a88f9dafeebfc26f6e7287326 100644 --- a/primitives/blockchain/src/backend.rs +++ b/primitives/blockchain/src/backend.rs @@ -59,19 +59,23 @@ pub trait HeaderBackend: Send + Sync { /// Get block header. Returns `UnknownBlock` error if block is not found. fn expect_header(&self, id: BlockId) -> Result { - self.header(id)?.ok_or_else(|| Error::UnknownBlock(format!("{}", id))) + self.header(id)?.ok_or_else(|| Error::UnknownBlock(format!("Expect header: {}", id))) } /// Convert an arbitrary block ID into a block number. Returns `UnknownBlock` error if block is not found. fn expect_block_number_from_id(&self, id: &BlockId) -> Result> { self.block_number_from_id(id) - .and_then(|n| n.ok_or_else(|| Error::UnknownBlock(format!("{}", id)))) + .and_then(|n| n.ok_or_else(|| + Error::UnknownBlock(format!("Expect block number from id: {}", id)) + )) } /// Convert an arbitrary block ID into a block hash. Returns `UnknownBlock` error if block is not found. fn expect_block_hash_from_id(&self, id: &BlockId) -> Result { self.block_hash_from_id(id) - .and_then(|n| n.ok_or_else(|| Error::UnknownBlock(format!("{}", id)))) + .and_then(|n| n.ok_or_else(|| + Error::UnknownBlock(format!("Expect block hash from id: {}", id)) + )) } } diff --git a/primitives/blockchain/src/error.rs b/primitives/blockchain/src/error.rs index d51ab787c72fb93ee80bc5a490eb58918ed869dd..24872448ab6f2781a78cc0230e665d9631d24838 100644 --- a/primitives/blockchain/src/error.rs +++ b/primitives/blockchain/src/error.rs @@ -126,6 +126,9 @@ pub enum Error { /// Invalid calculated state root on block import. #[display(fmt = "Calculated state root does not match.")] InvalidStateRoot, + /// Incomplete block import pipeline. + #[display(fmt = "Incomplete block import pipeline.")] + IncompletePipeline, /// A convenience variant for String #[display(fmt = "{}", _0)] Msg(String), diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index 57958ed851d344c2b9c4af3e244954d65d63328b..6e05bf4aac427699cfb25270f5b76a626c0f97ed 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for Aura consensus" edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../application-crypto" } diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index 11c186faa9a1be1f0da16263e009d0ca7279c523..7f0277a720205c500a1369311fcb6e466906de1e 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for BABE consensus" edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../application-crypto" } diff --git a/primitives/consensus/babe/src/digest.rs b/primitives/consensus/babe/src/digests.rs similarity index 85% rename from primitives/consensus/babe/src/digest.rs rename to primitives/consensus/babe/src/digests.rs index cca088b92bdd57baf414707b1e191046a99da9f2..7ec0f9b977cc7d921d64e0605db52d5c2c5bc3f3 100644 --- a/primitives/consensus/babe/src/digest.rs +++ b/primitives/consensus/babe/src/digests.rs @@ -41,7 +41,7 @@ use sp_std::vec::Vec; /// (VRF based) and to a secondary (slot number based). #[cfg(feature = "std")] #[derive(Clone, Debug)] -pub enum BabePreDigest { +pub enum PreDigest { /// A primary VRF-based slot assignment. Primary { /// VRF output @@ -63,20 +63,20 @@ pub enum BabePreDigest { } #[cfg(feature = "std")] -impl BabePreDigest { +impl PreDigest { /// Returns the slot number of the pre digest. pub fn authority_index(&self) -> AuthorityIndex { match self { - BabePreDigest::Primary { authority_index, .. } => *authority_index, - BabePreDigest::Secondary { authority_index, .. } => *authority_index, + PreDigest::Primary { authority_index, .. } => *authority_index, + PreDigest::Secondary { authority_index, .. } => *authority_index, } } /// Returns the slot number of the pre digest. pub fn slot_number(&self) -> SlotNumber { match self { - BabePreDigest::Primary { slot_number, .. } => *slot_number, - BabePreDigest::Secondary { slot_number, .. } => *slot_number, + PreDigest::Primary { slot_number, .. } => *slot_number, + PreDigest::Secondary { slot_number, .. } => *slot_number, } } @@ -84,18 +84,15 @@ impl BabePreDigest { /// of the chain. pub fn added_weight(&self) -> crate::BabeBlockWeight { match self { - BabePreDigest::Primary { .. } => 1, - BabePreDigest::Secondary { .. } => 0, + PreDigest::Primary { .. } => 1, + PreDigest::Secondary { .. } => 0, } } } -/// The prefix used by BABE for its VRF keys. -pub const BABE_VRF_PREFIX: &[u8] = b"substrate-babe-vrf"; - /// A raw version of `BabePreDigest`, usable on `no_std`. #[derive(Copy, Clone, Encode, Decode)] -pub enum RawBabePreDigest { +pub enum RawPreDigest { /// A primary VRF-based slot assignment. #[codec(index = "1")] Primary { @@ -123,38 +120,38 @@ pub enum RawBabePreDigest { }, } -impl RawBabePreDigest { +impl RawPreDigest { /// Returns the slot number of the pre digest. pub fn slot_number(&self) -> SlotNumber { match self { - RawBabePreDigest::Primary { slot_number, .. } => *slot_number, - RawBabePreDigest::Secondary { slot_number, .. } => *slot_number, + RawPreDigest::Primary { slot_number, .. } => *slot_number, + RawPreDigest::Secondary { slot_number, .. } => *slot_number, } } } #[cfg(feature = "std")] -impl Encode for BabePreDigest { +impl Encode for PreDigest { fn encode(&self) -> Vec { let raw = match self { - BabePreDigest::Primary { + PreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number, } => { - RawBabePreDigest::Primary { + RawPreDigest::Primary { vrf_output: *vrf_output.as_bytes(), vrf_proof: vrf_proof.to_bytes(), authority_index: *authority_index, slot_number: *slot_number, } }, - BabePreDigest::Secondary { + PreDigest::Secondary { authority_index, slot_number, } => { - RawBabePreDigest::Secondary { + RawPreDigest::Secondary { authority_index: *authority_index, slot_number: *slot_number, } @@ -166,26 +163,26 @@ impl Encode for BabePreDigest { } #[cfg(feature = "std")] -impl codec::EncodeLike for BabePreDigest {} +impl codec::EncodeLike for PreDigest {} #[cfg(feature = "std")] -impl Decode for BabePreDigest { +impl Decode for PreDigest { fn decode(i: &mut R) -> Result { let pre_digest = match Decode::decode(i)? { - RawBabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => { + RawPreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => { // Verify (at compile time) that the sizes in babe_primitives are correct let _: [u8; super::VRF_OUTPUT_LENGTH] = vrf_output; let _: [u8; super::VRF_PROOF_LENGTH] = vrf_proof; - BabePreDigest::Primary { + PreDigest::Primary { vrf_proof: VRFProof::from_bytes(&vrf_proof).map_err(convert_error)?, vrf_output: VRFOutput::from_bytes(&vrf_output).map_err(convert_error)?, authority_index, slot_number, } }, - RawBabePreDigest::Secondary { authority_index, slot_number } => { - BabePreDigest::Secondary { authority_index, slot_number } + RawPreDigest::Secondary { authority_index, slot_number } => { + PreDigest::Secondary { authority_index, slot_number } }, }; @@ -208,10 +205,10 @@ pub struct NextEpochDescriptor { #[cfg(feature = "std")] pub trait CompatibleDigestItem: Sized { /// Construct a digest item which contains a BABE pre-digest. - fn babe_pre_digest(seal: BabePreDigest) -> Self; + fn babe_pre_digest(seal: PreDigest) -> Self; /// If this item is an BABE pre-digest, return it. - fn as_babe_pre_digest(&self) -> Option; + fn as_babe_pre_digest(&self) -> Option; /// Construct a digest item which contains a BABE seal. fn babe_seal(signature: AuthoritySignature) -> Self; @@ -227,11 +224,11 @@ pub trait CompatibleDigestItem: Sized { impl CompatibleDigestItem for DigestItem where Hash: Debug + Send + Sync + Eq + Clone + Codec + 'static { - fn babe_pre_digest(digest: BabePreDigest) -> Self { + fn babe_pre_digest(digest: PreDigest) -> Self { DigestItem::PreRuntime(BABE_ENGINE_ID, digest.encode()) } - fn as_babe_pre_digest(&self) -> Option { + fn as_babe_pre_digest(&self) -> Option { self.try_to(OpaqueDigestItemId::PreRuntime(&BABE_ENGINE_ID)) } diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index 4cdeb072bd5e96f2b8ae607efc4e7caacd1b0560..78c63e5022a3cd3f408f2f0e6f3106ca9387f877 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -19,22 +19,22 @@ #![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)] #![cfg_attr(not(feature = "std"), no_std)] -mod digest; +pub mod digests; pub mod inherents; use codec::{Encode, Decode}; use sp_std::vec::Vec; use sp_runtime::{ConsensusEngineId, RuntimeDebug}; - -#[cfg(feature = "std")] -pub use digest::{BabePreDigest, CompatibleDigestItem}; -pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest, NextEpochDescriptor}; +use crate::digests::NextEpochDescriptor; mod app { use sp_application_crypto::{app_crypto, key_types::BABE, sr25519}; app_crypto!(sr25519, BABE); } +/// The prefix used by BABE for its VRF keys. +pub const BABE_VRF_PREFIX: &[u8] = b"substrate-babe-vrf"; + /// A Babe authority keypair. Necessarily equivalent to the schnorrkel public key used in /// the main Babe module. If that ever changes, then this must, too. #[cfg(feature = "std")] @@ -78,40 +78,6 @@ pub type BabeAuthorityWeight = u64; /// The weight of a BABE block. pub type BabeBlockWeight = u32; -/// BABE epoch information -#[derive(Decode, Encode, Default, PartialEq, Eq, Clone, RuntimeDebug)] -pub struct Epoch { - /// The epoch index - pub epoch_index: u64, - /// The starting slot of the epoch, - pub start_slot: SlotNumber, - /// The duration of this epoch - pub duration: SlotNumber, - /// The authorities and their weights - pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, - /// Randomness for this epoch - pub randomness: [u8; VRF_OUTPUT_LENGTH], -} - -impl Epoch { - /// "increment" the epoch, with given descriptor for the next. - pub fn increment(&self, descriptor: NextEpochDescriptor) -> Epoch { - Epoch { - epoch_index: self.epoch_index + 1, - start_slot: self.start_slot + self.duration, - duration: self.duration, - authorities: descriptor.authorities, - randomness: descriptor.randomness, - } - } - - /// Produce the "end slot" of the epoch. This is NOT inclusive to the epoch, - // i.e. the slots covered by the epoch are `self.start_slot .. self.end_slot()`. - pub fn end_slot(&self) -> SlotNumber { - self.start_slot + self.duration - } -} - /// An consensus log item for BABE. #[derive(Decode, Encode, Clone, PartialEq, Eq)] pub enum ConsensusLog { diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 3ae79caa4312625f54faf13829d6c639a2fcc142..2abc6f5617621c2b5fc67deb8edfceef665563b0 100644 --- a/primitives/consensus/common/Cargo.toml +++ b/primitives/consensus/common/Cargo.toml @@ -4,21 +4,24 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Common utilities for substrate consensus" edition = "2018" +license = "GPL-3.0" [dependencies] derive_more = "0.99.2" -libp2p = { version = "0.14.0-alpha.1", default-features = false } +libp2p = { version = "0.15.0", default-features = false } log = "0.4.8" sp-core = { path= "../../core" } sp-inherents = { version = "2.0.0", path = "../../inherents" } sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } futures = { version = "0.3.1", features = ["thread-pool"] } -futures-timer = "0.4.0" +futures-timer = "3.0.1" +futures-diagnose = "1.0" sp-std = { version = "2.0.0", path = "../../std" } sp-version = { version = "2.0.0", path = "../../version" } sp-runtime = { version = "2.0.0", path = "../../runtime" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } -parking_lot = "0.9.0" +parking_lot = "0.10.0" +serde = { version = "1.0", features = ["derive"] } [dev-dependencies] sp-test-primitives = { version = "2.0.0", path = "../../test-primitives" } diff --git a/primitives/consensus/common/src/block_import.rs b/primitives/consensus/common/src/block_import.rs index f8d05e10548d2877fb726f016b634ed04b5757aa..dabe6331e815003fbc142e52321792c7c90df0c4 100644 --- a/primitives/consensus/common/src/block_import.rs +++ b/primitives/consensus/common/src/block_import.rs @@ -16,14 +16,15 @@ //! Block import helpers. -use sp_runtime::{ - Justification, - traits::{Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor, HasherFor}, -}; +use sp_runtime::traits::{Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor, HasherFor}; +use sp_runtime::Justification; +use serde::{Serialize, Deserialize}; use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; +use std::any::Any; +use crate::Error; use crate::import_queue::{Verifier, CacheKeyId}; /// Block import result. @@ -42,7 +43,7 @@ pub enum ImportResult { } /// Auxiliary data associated with an imported block result. -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ImportedAux { /// Only the header has been imported. Block body verification was skipped. pub header_only: bool, @@ -142,13 +143,21 @@ pub struct BlockImportParams { /// Is this block finalized already? /// `true` implies instant finality. pub finalized: bool, + /// Intermediate values that are interpreted by block importers. Each block importer, + /// upon handling a value, removes it from the intermediate list. The final block importer + /// rejects block import if there are still intermediate values that remain unhandled. + pub intermediates: HashMap, Box>, /// Auxiliary consensus data produced by the block. /// Contains a list of key-value pairs. If values are `None`, the keys /// will be deleted. pub auxiliary: Vec<(Vec, Option>)>, /// Fork choice strategy of this import. This should only be set by a /// synchronous import, otherwise it may race against other imports. - pub fork_choice: ForkChoiceStrategy, + /// `None` indicates that the current verifier or importer cannot yet + /// determine the fork choice value, and it expects subsequent importer + /// to modify it. If `None` is passed all the way down to bottom block + /// importer, the import fails with an `IncompletePipeline` error. + pub fork_choice: Option, /// Allow importing the block skipping state verification if parent state is missing. pub allow_missing_state: bool, /// Re-validate existing block. @@ -210,11 +219,41 @@ impl BlockImportParams { storage_changes: None, finalized: self.finalized, auxiliary: self.auxiliary, + intermediates: self.intermediates, allow_missing_state: self.allow_missing_state, fork_choice: self.fork_choice, import_existing: self.import_existing, } } + + /// Take interemdiate by given key, and remove it from the processing list. + pub fn take_intermediate(&mut self, key: &[u8]) -> Result, Error> { + let (k, v) = self.intermediates.remove_entry(key).ok_or(Error::NoIntermediate)?; + + match v.downcast::() { + Ok(v) => Ok(v), + Err(v) => { + self.intermediates.insert(k, v); + Err(Error::InvalidIntermediate) + }, + } + } + + /// Get a reference to a given intermediate. + pub fn intermediate(&self, key: &[u8]) -> Result<&T, Error> { + self.intermediates.get(key) + .ok_or(Error::NoIntermediate)? + .downcast_ref::() + .ok_or(Error::InvalidIntermediate) + } + + /// Get a mutable reference to a given intermediate. + pub fn intermediate_mut(&mut self, key: &[u8]) -> Result<&mut T, Error> { + self.intermediates.get_mut(key) + .ok_or(Error::NoIntermediate)? + .downcast_mut::() + .ok_or(Error::InvalidIntermediate) + } } /// Block import trait. diff --git a/primitives/consensus/common/src/error.rs b/primitives/consensus/common/src/error.rs index 972e4a2d48b215021fac6a9b6a556e202e16694b..c802831d6501fec7c228e75e2cf5159f60e1ecc1 100644 --- a/primitives/consensus/common/src/error.rs +++ b/primitives/consensus/common/src/error.rs @@ -31,6 +31,12 @@ pub enum Error { /// I/O terminated unexpectedly #[display(fmt="I/O terminated unexpectedly.")] IoTerminated, + /// Intermediate missing. + #[display(fmt="Missing intermediate.")] + NoIntermediate, + /// Intermediate is of wrong type. + #[display(fmt="Invalid intermediate.")] + InvalidIntermediate, /// Unable to schedule wakeup. #[display(fmt="Timer error: {}", _0)] FaultyTimer(std::io::Error), diff --git a/primitives/consensus/common/src/import_queue/basic_queue.rs b/primitives/consensus/common/src/import_queue/basic_queue.rs index 4dcb7eeb45047b64bb15013325086ea0bf479519..63ba16b658c0158390b9ee993b00224e9e0f169b 100644 --- a/primitives/consensus/common/src/import_queue/basic_queue.rs +++ b/primitives/consensus/common/src/import_queue/basic_queue.rs @@ -71,7 +71,7 @@ impl BasicQueue { let manual_poll; if let Some(pool) = &mut pool { - pool.spawn_ok(future); + pool.spawn_ok(futures_diagnose::diagnose("import-queue", future)); manual_poll = None; } else { manual_poll = Some(Box::pin(future) as Pin>); diff --git a/primitives/consensus/pow/Cargo.toml b/primitives/consensus/pow/Cargo.toml index 12e97890d7a1c2e57ce61e5f9ffecf7168a9ec2c..8e964e0c18456e01b884c96f7f005d45b5b328f9 100644 --- a/primitives/consensus/pow/Cargo.toml +++ b/primitives/consensus/pow/Cargo.toml @@ -4,6 +4,7 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for Aura consensus" edition = "2018" +license = "GPL-3.0" [dependencies] sp-api = { version = "2.0.0", default-features = false, path = "../../api" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index f7eec82e55daf508fd700e18def046dc73e68a4c..574b9aa9cb58e1b73abe041a95eacf24504814b2 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-core" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-std = { version = "2.0.0", default-features = false, path = "../std" } @@ -11,7 +12,7 @@ rustc-hex = { version = "2.0.1", default-features = false } log = { version = "0.4.8", default-features = false } serde = { version = "1.0.101", optional = true, features = ["derive"] } byteorder = { version = "1.3.2", default-features = false } -primitive-types = { version = "0.6", default-features = false, features = ["codec"] } +primitive-types = { version = "0.6.2", default-features = false, features = ["codec"] } impl-serde = { version = "0.2.3", optional = true } wasmi = { version = "0.6.2", optional = true } hash-db = { version = "0.15.2", default-features = false } @@ -24,11 +25,12 @@ regex = { version = "1.3.1", optional = true } num-traits = { version = "0.2.8", default-features = false } zeroize = { version = "1.0.0", default-features = false } lazy_static = { version = "1.4.0", default-features = false, optional = true } -parking_lot = { version = "0.9.0", optional = true } +parking_lot = { version = "0.10.0", optional = true } sp-debug-derive = { version = "2.0.0", path = "../debug-derive" } sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" } sp-storage = { version = "2.0.0", default-features = false, path = "../storage" } -libsecp256k1 = { version = "0.3.2", default-features = false } +libsecp256k1 = { version = "0.3.2", default-features = false, features = ["hmac"] } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } # full crypto ed25519-dalek = { version = "1.0.0-pre.3", default-features = false, features = ["u64_backend", "alloc"], optional = true } diff --git a/primitives/core/benches/bench.rs b/primitives/core/benches/bench.rs index 36728742d894994b18e53a9e45b0185c7ec1b479..7db9d72e6b5b0d6afa04c28f07c91c6491a4c541 100644 --- a/primitives/core/benches/bench.rs +++ b/primitives/core/benches/bench.rs @@ -86,9 +86,29 @@ fn bench_ed25519(c: &mut Criterion) { }, vec![32, 1024, 1024 * 1024]); } +fn bench_sr25519(c: &mut Criterion) { + c.bench_function_over_inputs("signing - sr25519", |b, &msg_size| { + let msg = (0..msg_size) + .map(|_| rand::random::()) + .collect::>(); + let key = sp_core::sr25519::Pair::generate().0; + b.iter(|| key.sign(&msg)) + }, vec![32, 1024, 1024 * 1024]); + + c.bench_function_over_inputs("verifying - sr25519", |b, &msg_size| { + let msg = (0..msg_size) + .map(|_| rand::random::()) + .collect::>(); + let key = sp_core::sr25519::Pair::generate().0; + let sig = key.sign(&msg); + let public = key.public(); + b.iter(|| sp_core::sr25519::Pair::verify(&sig, &msg, &public)) + }, vec![32, 1024, 1024 * 1024]); +} + criterion_group!{ - name = benches; - config = Criterion::default().warm_up_time(Duration::from_millis(500)).without_plots(); - targets = bench_hash_128_fix_size, bench_hash_128_dyn_size, bench_ed25519 + name = benches; + config = Criterion::default().warm_up_time(Duration::from_millis(500)).without_plots(); + targets = bench_hash_128_fix_size, bench_hash_128_dyn_size, bench_ed25519, bench_sr25519 } criterion_main!(benches); diff --git a/primitives/core/src/changes_trie.rs b/primitives/core/src/changes_trie.rs index d38761ccf0fd1570150c9278fa35db8617a1b19f..cb21ffe13df969141cb056056694914e4006ca9e 100644 --- a/primitives/core/src/changes_trie.rs +++ b/primitives/core/src/changes_trie.rs @@ -22,7 +22,7 @@ use codec::{Encode, Decode}; use num_traits::Zero; /// Substrate changes trie configuration. -#[cfg_attr(any(feature = "std", test), derive(Serialize, Deserialize))] +#[cfg_attr(any(feature = "std", test), derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf))] #[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] pub struct ChangesTrieConfiguration { /// Interval (in blocks) at which level1-digests are created. Digests are not diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index ce2b75c37535d8fdd64513b677a5222d6de64ad7..5ad5e1981379b9ed46e6c5b725a65a949989a124 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -19,7 +19,6 @@ // end::description[] use sp_std::hash::Hash; -#[cfg(feature = "full_crypto")] use sp_std::vec::Vec; #[cfg(feature = "std")] use sp_std::convert::TryInto; @@ -443,6 +442,10 @@ ss58_address_format!( (16, "kulupu", "Kulupu mainnet, direct checksum, standard account (*25519).") EdgewareAccountDirect => (7, "edgeware", "Edgeware mainnet, direct checksum, standard account (*25519).") + CentrifugeAccountDirect => + (36, "centrifuge", "Centrifuge Chain mainnet, direct checksum, standard account (*25519).") + SubstraTeeAccountDirect => + (44, "substratee", "Any SubstraTEE off-chain network private account, direct checksum, standard account (*25519).") ); /// Set the default "version" (actually, this is a bit of a misnomer and the version byte is @@ -520,8 +523,7 @@ pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + Pa fn from_slice(data: &[u8]) -> Self; /// Return a `Vec` filled with raw data. - #[cfg(feature = "std")] - fn to_raw_vec(&self) -> Vec { self.as_slice().to_owned() } + fn to_raw_vec(&self) -> Vec { self.as_slice().to_vec() } /// Return a slice filled with raw data. fn as_slice(&self) -> &[u8] { self.as_ref() } diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index ee768ee4a6226b338fec1b958a40c6d20bbf09e4..b1a5916c921f112d684aff248af006c193f7d457 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -70,9 +70,6 @@ mod changes_trie; pub mod traits; pub mod testing; -#[cfg(test)] -mod tests; - pub use self::hash::{H160, H256, H512, convert_hash}; pub use self::uint::U256; pub use changes_trie::{ChangesTrieConfiguration, ChangesTrieConfigurationRange}; @@ -309,3 +306,43 @@ pub fn to_substrate_wasm_fn_return_value(value: &impl Encode) -> u64 { res } + +/// Macro for creating `Maybe*` marker traits. +/// +/// Such a maybe-marker trait requires the given bound when `feature = std` and doesn't require +/// the bound on `no_std`. This is useful for situations where you require that a type implements +/// a certain trait with `feature = std`, but not on `no_std`. +/// +/// # Example +/// +/// ``` +/// sp_core::impl_maybe_marker! { +/// /// A marker for a type that implements `Debug` when `feature = std`. +/// trait MaybeDebug: std::fmt::Debug; +/// /// A marker for a type that implements `Debug + Display` when `feature = std`. +/// trait MaybeDebugDisplay: std::fmt::Debug, std::fmt::Display; +/// } +/// ``` +#[macro_export] +macro_rules! impl_maybe_marker { + ( + $( + $(#[$doc:meta] )+ + trait $trait_name:ident: $( $trait_bound:path ),+; + )+ + ) => { + $( + $(#[$doc])+ + #[cfg(feature = "std")] + pub trait $trait_name: $( $trait_bound + )+ {} + #[cfg(feature = "std")] + impl $trait_name for T {} + + $(#[$doc])+ + #[cfg(not(feature = "std"))] + pub trait $trait_name {} + #[cfg(not(feature = "std"))] + impl $trait_name for T {} + )+ + } +} diff --git a/primitives/core/src/offchain/mod.rs b/primitives/core/src/offchain/mod.rs index e2e00c36e0769cd77379c839b506051be9511888..425957a21f67e348ddb2725050fe30c5e7fae383 100644 --- a/primitives/core/src/offchain/mod.rs +++ b/primitives/core/src/offchain/mod.rs @@ -50,6 +50,7 @@ pub trait OffchainStorage: Clone + Send + Sync { /// A type of supported crypto. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] #[repr(C)] pub enum StorageKind { /// Persistent storage is non-revertible and not fork-aware. It means that any value diff --git a/primitives/core/src/sandbox.rs b/primitives/core/src/sandbox.rs index a0673c49db10276b6d6d34ab1825a833815f3d5d..73fbcfb572ee355f645dc716f197ef8f29a7292d 100644 --- a/primitives/core/src/sandbox.rs +++ b/primitives/core/src/sandbox.rs @@ -24,99 +24,6 @@ use sp_std::vec::Vec; #[derive(crate::RuntimeDebug)] pub struct HostError; -/// Representation of a typed wasm value. -#[derive(Clone, Copy, PartialEq, Encode, Decode)] -#[derive(crate::RuntimeDebug)] -pub enum TypedValue { - /// Value of 32-bit signed or unsigned integer. - #[codec(index = "1")] - I32(i32), - - /// Value of 64-bit signed or unsigned integer. - #[codec(index = "2")] - I64(i64), - - /// Value of 32-bit IEEE 754-2008 floating point number represented as a bit pattern. - #[codec(index = "3")] - F32(i32), - - /// Value of 64-bit IEEE 754-2008 floating point number represented as a bit pattern. - #[codec(index = "4")] - F64(i64), -} - -impl TypedValue { - /// Returns `Some` if this value of type `I32`. - pub fn as_i32(&self) -> Option { - match *self { - TypedValue::I32(v) => Some(v), - _ => None, - } - } -} - -#[cfg(feature = "std")] -impl From<::wasmi::RuntimeValue> for TypedValue { - fn from(val: ::wasmi::RuntimeValue) -> TypedValue { - use ::wasmi::RuntimeValue; - match val { - RuntimeValue::I32(v) => TypedValue::I32(v), - RuntimeValue::I64(v) => TypedValue::I64(v), - RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32), - RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64), - } - } -} - -#[cfg(feature = "std")] -impl From for ::wasmi::RuntimeValue { - fn from(val: TypedValue) -> ::wasmi::RuntimeValue { - use ::wasmi::RuntimeValue; - use ::wasmi::nan_preserving_float::{F32, F64}; - match val { - TypedValue::I32(v) => RuntimeValue::I32(v), - TypedValue::I64(v) => RuntimeValue::I64(v), - TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)), - TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)), - } - } -} - -/// Typed value that can be returned from a function. -/// -/// Basically a `TypedValue` plus `Unit`, for functions which return nothing. -#[derive(Clone, Copy, PartialEq, Encode, Decode)] -#[derive(crate::RuntimeDebug)] -pub enum ReturnValue { - /// For returning nothing. - Unit, - /// For returning some concrete value. - Value(TypedValue), -} - -impl From for ReturnValue { - fn from(v: TypedValue) -> ReturnValue { - ReturnValue::Value(v) - } -} - -impl ReturnValue { - /// Maximum number of bytes `ReturnValue` might occupy when serialized with - /// `Codec`. - /// - /// Breakdown: - /// 1 byte for encoding unit/value variant - /// 1 byte for encoding value type - /// 8 bytes for encoding the biggest value types available in wasm: f64, i64. - pub const ENCODED_MAX_SIZE: usize = 10; -} - -#[test] -fn return_value_encoded_max_size() { - let encoded = ReturnValue::Value(TypedValue::I64(-1)).encode(); - assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE); -} - /// Describes an entity to define or import into the environment. #[derive(Clone, PartialEq, Eq, Encode, Decode)] #[derive(crate::RuntimeDebug)] diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 6e542f230f25879f0741496dd98bd4034906f271..3495f32872f1694abe6756b643acb3ab7b7d3891 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -729,6 +729,27 @@ mod test { assert!(Pair::verify(&signature, &message[..], &public)); } + #[test] + fn messed_signature_should_not_work() { + let (pair, _) = Pair::generate(); + let public = pair.public(); + let message = b"Signed payload"; + let Signature(mut bytes) = pair.sign(&message[..]); + bytes[0] = !bytes[0]; + bytes[2] = !bytes[2]; + let signature = Signature(bytes); + assert!(!Pair::verify(&signature, &message[..], &public)); + } + + #[test] + fn messed_message_should_not_work() { + let (pair, _) = Pair::generate(); + let public = pair.public(); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + assert!(!Pair::verify(&signature, &b"Something unimportant", &public)); + } + #[test] fn seeded_pair_should_work() { let pair = Pair::from_seed(b"12345678901234567890123456789012"); diff --git a/primitives/core/src/testing.rs b/primitives/core/src/testing.rs index 35286acb63f310d2bc7bf8e560c17e84903cc171..0def3454bc8991ef37f6694a40b738a99dfc076b 100644 --- a/primitives/core/src/testing.rs +++ b/primitives/core/src/testing.rs @@ -127,6 +127,10 @@ impl crate::traits::BareCryptoStore for KeyStore { fn password(&self) -> Option<&str> { None } + + fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { + public_keys.iter().all(|(k, t)| self.keys.get(&t).and_then(|s| s.get(k)).is_some()) + } } /// Macro for exporting functions from wasm in with the expected signature for using it with the diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 020b5e2dc54612604b870773ace9a16aeafb0082..bd02d39fb55a50e400fdd50a3666b9a1a7c28cec 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -69,6 +69,11 @@ pub trait BareCryptoStore: Send + Sync { /// Get the password for this store. fn password(&self) -> Option<&str>; + + /// Checks if the private keys for the given public key and key type combinations exist. + /// + /// Returns `true` iff all private keys could be found. + fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool; } /// A pointer to the key store. diff --git a/primitives/debug-derive/Cargo.toml b/primitives/debug-derive/Cargo.toml index 9c65cb34f5fa93b828e75e7a1190657dd4813c24..9b12bfb2059e2b630308bedf011e67b98537193f 100644 --- a/primitives/debug-derive/Cargo.toml +++ b/primitives/debug-derive/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-debug-derive" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true diff --git a/primitives/externalities/src/lib.rs b/primitives/externalities/src/lib.rs index 350b65d190840023981849a2f83defc6e4f28428..75193a4b9c3922ed1517b95939477ceed586ea9b 100644 --- a/primitives/externalities/src/lib.rs +++ b/primitives/externalities/src/lib.rs @@ -202,6 +202,14 @@ pub trait Externalities: ExtensionStore { /// /// Returns the SCALE encoded hash. fn storage_changes_root(&mut self, parent: &[u8]) -> Result>, ()>; + + fn wipe(&mut self) { + unimplemented!() + } + + fn commit(&mut self) { + unimplemented!() + } } /// Extension for the [`Externalities`] trait. diff --git a/primitives/finality-grandpa/Cargo.toml b/primitives/finality-grandpa/Cargo.toml index c2856e524ff212e038c4d9b55005062c961d6b76..6498763e4c72cb7185b3e9483f7d1c4c44434223 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/finality-grandpa/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-finality-grandpa" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] app-crypto = { version = "2.0.0", default-features = false, package = "sp-application-crypto", path = "../application-crypto" } @@ -22,3 +23,6 @@ std = [ "sp-api/std", "sp-runtime/std", ] +full_crypto = [ + "app-crypto/full_crypto" +] diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index f1481c0aed4e5e71fbf372c22032916426b3aa3a..98fce4508156c995371b12f4ec922c212b3f501d 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -34,7 +34,7 @@ mod app { } /// The grandpa crypto scheme defined via the keypair type. -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "full_crypto"))] pub type AuthorityPair = app::Pair; /// Identity of a Grandpa authority. diff --git a/primitives/finality-tracker/Cargo.toml b/primitives/finality-tracker/Cargo.toml index 32458039458fd70bdccfe45a5804bab79d8bb5e4..1b0e81da9175a361854488462a27e3967553d029 100644 --- a/primitives/finality-tracker/Cargo.toml +++ b/primitives/finality-tracker/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-finality-tracker" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index 0dc465e2883d1c851057177d5d7c40c0fc42708d..698ab5389a543f0ff8fdfa3f87f8fd168e483f24 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -3,9 +3,10 @@ name = "sp-inherents" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] -parking_lot = { version = "0.9.0", optional = true } +parking_lot = { version = "0.10.0", optional = true } sp-std = { version = "2.0.0", default-features = false, path = "../std" } sp-core = { version = "2.0.0", default-features = false, path = "../core" } codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] } diff --git a/primitives/inherents/src/lib.rs b/primitives/inherents/src/lib.rs index f79d8357a566ce796aced7b9dcee7b5d33b79395..f1b8db982d33716f70a96aee73b5d6794be7b17a 100644 --- a/primitives/inherents/src/lib.rs +++ b/primitives/inherents/src/lib.rs @@ -147,6 +147,11 @@ impl InherentData { None => Ok(None) } } + + /// Get the number of inherents in this instance + pub fn len(&self) -> usize { + self.data.len() + } } /// The result of checking inherents. diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index 2f03f230ef8e09bd08f65c162d0c49de5155c4e2..f494697b4b57242ab4eb303d38827991e8937724 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-io" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false } @@ -11,6 +12,7 @@ sp-core = { version = "2.0.0", default-features = false, path = "../core" } sp-std = { version = "2.0.0", default-features = false, path = "../std" } libsecp256k1 = { version = "0.3.4", optional = true } sp-state-machine = { version = "0.8", optional = true, path = "../../primitives/state-machine" } +sp-wasm-interface = { version = "2.0.0", path = "../../primitives/wasm-interface", default-features = false } sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../runtime-interface" } sp-trie = { version = "2.0.0", optional = true, path = "../../primitives/trie" } sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" } @@ -28,6 +30,7 @@ std = [ "libsecp256k1", "sp-runtime-interface/std", "sp-externalities", + "sp-wasm-interface/std", "log", ] diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 704477b1fb2bcf095727e1e0b8e1d4bf89797d3b..ce8a546e86ff87543047fcf0d9fb4a87068735a4 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -20,7 +20,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] @@ -772,6 +771,30 @@ pub trait Logging { } } +/// Interface that provides functions for benchmarking the runtime. +#[runtime_interface] +pub trait Benchmarking { + /// Get the number of nanoseconds passed since the UNIX epoch + /// + /// WARNING! This is a non-deterministic call. Do not use this within + /// consensus critical logic. + fn current_time() -> u128 { + std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH) + .expect("Unix time doesn't go backwards; qed") + .as_nanos() + } + + /// Reset the trie database to the genesis state. + fn wipe_db(&mut self) { + self.wipe() + } + + /// Commit pending storage changes to the trie database and clear the database cache. + fn commit_db(&mut self) { + self.commit() + } +} + /// Wasm-only interface that provides functions for interacting with the sandbox. #[runtime_interface(wasm_only)] pub trait Sandbox { @@ -850,6 +873,14 @@ pub trait Sandbox { fn instance_teardown(&mut self, instance_idx: u32) { self.sandbox().instance_teardown(instance_idx).expect("Failed to teardown sandbox instance") } + + /// Get the value from a global with the given `name`. The sandbox is determined by the given + /// `instance_idx`. + /// + /// Returns `Some(_)` when the requested global variable could be found. + fn get_global_val(&mut self, instance_idx: u32, name: &str) -> Option { + self.sandbox().get_global_val(instance_idx, name).expect("Failed to get global from sandbox") + } } /// Allocator used by Substrate when executing the Wasm runtime. @@ -884,7 +915,7 @@ pub fn panic(info: &core::panic::PanicInfo) -> ! { unsafe { let message = sp_std::alloc::format!("{}", info); logging::log(LogLevel::Error, "runtime", message.as_bytes()); - core::intrinsics::abort() + core::arch::wasm32::unreachable(); } } @@ -894,7 +925,7 @@ pub fn panic(info: &core::panic::PanicInfo) -> ! { pub fn oom(_: core::alloc::Layout) -> ! { unsafe { logging::log(LogLevel::Error, "runtime", b"Runtime memory exhausted. Aborting"); - core::intrinsics::abort(); + core::arch::wasm32::unreachable(); } } diff --git a/primitives/keyring/Cargo.toml b/primitives/keyring/Cargo.toml index 61ebf89ad2464b572232bc674a4995ea7e0d2bb9..e2603a02623ab3d3a6e093a53ee83cc4df97a17b 100644 --- a/primitives/keyring/Cargo.toml +++ b/primitives/keyring/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-keyring" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-core = { version = "2.0.0", path = "../core" } diff --git a/primitives/keyring/src/sr25519.rs b/primitives/keyring/src/sr25519.rs index 27627a0c50d7e3b461c0239bbf9c67da163c4b9f..476997f2db79b00b79b3214bd8f20f6d49898ffb 100644 --- a/primitives/keyring/src/sr25519.rs +++ b/primitives/keyring/src/sr25519.rs @@ -86,7 +86,6 @@ impl Keyring { pub fn public(self) -> Public { self.pair().public() } - pub fn to_seed(self) -> String { format!("//{}", self) } @@ -113,6 +112,33 @@ impl From for sp_runtime::MultiSigner { } } +#[derive(Debug)] +pub struct ParseKeyringError; + +impl std::fmt::Display for ParseKeyringError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ParseKeyringError") + } +} + +impl std::str::FromStr for Keyring { + type Err = ParseKeyringError; + + fn from_str(s: &str) -> Result::Err> { + match s { + "alice" => Ok(Keyring::Alice), + "bob" => Ok(Keyring::Bob), + "charlie" => Ok(Keyring::Charlie), + "dave" => Ok(Keyring::Dave), + "eve" => Ok(Keyring::Eve), + "ferdie" => Ok(Keyring::Ferdie), + "one" => Ok(Keyring::One), + "two" => Ok(Keyring::Two), + _ => Err(ParseKeyringError) + } + } +} + lazy_static! { static ref PRIVATE_KEYS: HashMap = { Keyring::iter().map(|i| (i, i.pair())).collect() diff --git a/primitives/offchain/src/lib.rs b/primitives/offchain/src/lib.rs index 1dd20db0dd846d4b33ca18e6bd83ad8223b6e8c4..ae02fed4967685e7cb419d49064a63634497a749 100644 --- a/primitives/offchain/src/lib.rs +++ b/primitives/offchain/src/lib.rs @@ -19,8 +19,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] -use sp_runtime::traits::NumberFor; - /// Local Storage Prefix used by the Offchain Worker API to pub const STORAGE_PREFIX: &[u8] = b"storage"; @@ -31,7 +29,7 @@ sp_api::decl_runtime_apis! { /// Starts the off-chain task for given block number. #[skip_initialize_block] #[changed_in(2)] - fn offchain_worker(number: NumberFor); + fn offchain_worker(number: sp_runtime::traits::NumberFor); /// Starts the off-chain task for given block header. #[skip_initialize_block] diff --git a/primitives/panic-handler/Cargo.toml b/primitives/panic-handler/Cargo.toml index 19e34352cf80696eb2113c5288a574b9e3f867ce..0fb6e3b173e15d81d15311f6ae3a82b535e7fc5a 100644 --- a/primitives/panic-handler/Cargo.toml +++ b/primitives/panic-handler/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] description = "Substrate panic handler." edition = "2018" +license = "GPL-3.0" [dependencies] backtrace = "0.3.38" diff --git a/primitives/phragmen/Cargo.toml b/primitives/phragmen/Cargo.toml index 92807376de3b5941151c090b2862f40e2b0ecd22..3bfff32d2a481a86e1aca8b1261210006086ddc6 100644 --- a/primitives/phragmen/Cargo.toml +++ b/primitives/phragmen/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-phragmen" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } diff --git a/primitives/phragmen/benches/phragmen.rs b/primitives/phragmen/benches/phragmen.rs index 7bebda564e435a489e7eedc2a2ef7ab0228a0e00..aa99e6f384ba65fd1d4621d3a3e608cb632e1a12 100644 --- a/primitives/phragmen/benches/phragmen.rs +++ b/primitives/phragmen/benches/phragmen.rs @@ -158,8 +158,8 @@ fn do_phragmen( macro_rules! phragmen_benches { ($($name:ident: $tup:expr,)*) => { $( - #[bench] - fn $name(b: &mut Bencher) { + #[bench] + fn $name(b: &mut Bencher) { let (v, n, t, e, eq_iter, eq_tol) = $tup; println!("----------------------"); println!( @@ -168,8 +168,8 @@ macro_rules! phragmen_benches { v, n, e, e * n, t, eq_iter, eq_tol, ); do_phragmen(b, v, n, t, e, eq_iter, eq_tol); - } - )* + } + )* } } diff --git a/primitives/phragmen/src/lib.rs b/primitives/phragmen/src/lib.rs index c13654543dafe823f52b245300a3dcae4f4dcdbc..e2f77e545816a81b6c643292d1a937a6dc4aa553 100644 --- a/primitives/phragmen/src/lib.rs +++ b/primitives/phragmen/src/lib.rs @@ -33,10 +33,14 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, collections::btree_map::BTreeMap}; -use sp_runtime::RuntimeDebug; -use sp_runtime::{helpers_128bit::multiply_by_rational, Perbill, Rational128}; -use sp_runtime::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bounded}; +use sp_std::{prelude::*, collections::btree_map::BTreeMap, convert::TryFrom}; +use sp_runtime::{ + PerThing, Rational128, RuntimeDebug, + helpers_128bit::multiply_by_rational, +}; +use sp_runtime::traits::{ + Zero, Convert, Member, AtLeast32Bit, SaturatedConversion, Bounded, Saturating, +}; #[cfg(test)] mod mock; @@ -93,21 +97,21 @@ pub struct Edge { candidate_index: usize, } -/// Means a particular `AccountId` was backed by `Perbill`th of a nominator's stake. -pub type PhragmenAssignment = (AccountId, Perbill); +/// Particular `AccountId` was backed by `T`-ratio of a nominator's stake. +pub type PhragmenAssignment = (AccountId, T); -/// Means a particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake. +/// Particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake. pub type PhragmenStakedAssignment = (AccountId, ExtendedBalance); /// Final result of the phragmen election. #[derive(RuntimeDebug)] -pub struct PhragmenResult { +pub struct PhragmenResult { /// Just winners zipped with their approval stake. Note that the approval stake is merely the /// sub of their received stake and could be used for very basic sorting and approval voting. pub winners: Vec<(AccountId, ExtendedBalance)>, /// Individual assignments. for each tuple, the first elements is a voter and the second /// is the list of candidates that it supports. - pub assignments: Vec<(AccountId, Vec>)> + pub assignments: Vec<(AccountId, Vec>)> } /// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how @@ -145,23 +149,24 @@ pub type SupportMap = BTreeMap>; /// responsibility of the caller to make sure only those candidates who have a sensible economic /// value are passed in. From the perspective of this function, a candidate can easily be among the /// winner with no backing stake. -pub fn elect( +pub fn elect( candidate_count: usize, minimum_candidate_count: usize, initial_candidates: Vec, initial_voters: Vec<(AccountId, Vec)>, stake_of: FS, -) -> Option> where +) -> Option> where AccountId: Default + Ord + Member, - Balance: Default + Copy + SimpleArithmetic, + Balance: Default + Copy + AtLeast32Bit, for<'r> FS: Fn(&'r AccountId) -> Balance, C: Convert + Convert, + R: PerThing, { let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; // return structures let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>; - let mut assigned: Vec<(AccountId, Vec>)>; + let mut assigned: Vec<(AccountId, Vec>)>; // used to cache and access candidates index. let mut c_idx_cache = BTreeMap::::new(); @@ -272,20 +277,29 @@ pub fn elect( let mut assignment = (n.who.clone(), vec![]); for e in &mut n.edges { if elected_candidates.iter().position(|(ref c, _)| *c == e.who).is_some() { - let per_bill_parts = + let per_bill_parts: R::Inner = { if n.load == e.load { // Full support. No need to calculate. - Perbill::accuracy().into() + R::ACCURACY } else { if e.load.d() == n.load.d() { // return e.load / n.load. - let desired_scale: u128 = Perbill::accuracy().into(); - multiply_by_rational( + let desired_scale: u128 = R::ACCURACY.saturated_into(); + let parts = multiply_by_rational( desired_scale, e.load.n(), n.load.n(), - ).unwrap_or(Bounded::max_value()) + ) + // If result cannot fit in u128. Not much we can do about it. + .unwrap_or(Bounded::max_value()); + + TryFrom::try_from(parts) + // If the result cannot fit into R::Inner. Defensive only. This can + // never happen. `desired_scale * e / n`, where `e / n < 1` always + // yields a value smaller than `desired_scale`, which will fit into + // R::Inner. + .unwrap_or(Bounded::max_value()) } else { // defensive only. Both edge and nominator loads are built from // scores, hence MUST have the same denominator. @@ -293,10 +307,7 @@ pub fn elect( } } }; - // safer to .min() inside as well to argue as u32 is safe. - let per_thing = Perbill::from_parts( - per_bill_parts.min(Perbill::accuracy().into()) as u32 - ); + let per_thing = R::from_parts(per_bill_parts); assignment.1.push((e.who.clone(), per_thing)); } } @@ -304,20 +315,19 @@ pub fn elect( if assignment.1.len() > 0 { // To ensure an assertion indicating: no stake from the nominator going to waste, // we add a minimal post-processing to equally assign all of the leftover stake ratios. - let vote_count = assignment.1.len() as u32; + let vote_count: R::Inner = assignment.1.len().saturated_into(); let len = assignment.1.len(); - let sum = assignment.1.iter() - .map(|a| a.1.deconstruct()) - .sum::(); - let accuracy = Perbill::accuracy(); - let diff = accuracy.checked_sub(sum).unwrap_or(0); + let mut sum: R::Inner = Zero::zero(); + assignment.1.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct())); + let accuracy = R::ACCURACY; + let diff = accuracy.saturating_sub(sum); let diff_per_vote = (diff / vote_count).min(accuracy); - if diff_per_vote > 0 { + if !diff_per_vote.is_zero() { for i in 0..len { let current_ratio = assignment.1[i % len].1; let next_ratio = current_ratio - .saturating_add(Perbill::from_parts(diff_per_vote)); + .saturating_add(R::from_parts(diff_per_vote)); assignment.1[i % len].1 = next_ratio; } } @@ -325,9 +335,9 @@ pub fn elect( // `remainder` is set to be less than maximum votes of a nominator (currently 16). // safe to cast it to usize. let remainder = diff - diff_per_vote * vote_count; - for i in 0..remainder as usize { + for i in 0..remainder.saturated_into::() { let current_ratio = assignment.1[i % len].1; - let next_ratio = current_ratio.saturating_add(Perbill::from_parts(1)); + let next_ratio = current_ratio.saturating_add(R::from_parts(1u8.into())); assignment.1[i % len].1 = next_ratio; } assigned.push(assignment); @@ -341,15 +351,16 @@ pub fn elect( } /// Build the support map from the given phragmen result. -pub fn build_support_map( +pub fn build_support_map( elected_stashes: &Vec, - assignments: &Vec<(AccountId, Vec>)>, + assignments: &Vec<(AccountId, Vec>)>, stake_of: FS, ) -> SupportMap where AccountId: Default + Ord + Member, - Balance: Default + Copy + SimpleArithmetic, + Balance: Default + Copy + AtLeast32Bit, C: Convert + Convert, for<'r> FS: Fn(&'r AccountId) -> Balance, + R: PerThing + sp_std::ops::Mul, { let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; // Initialize the support of each candidate. diff --git a/primitives/phragmen/src/mock.rs b/primitives/phragmen/src/mock.rs index 59b8d4eb5c62425b0bfbcdb942b1cd2beb1fdeef..b3110a5dba713d232b881f73f35e96d7493b3fe9 100644 --- a/primitives/phragmen/src/mock.rs +++ b/primitives/phragmen/src/mock.rs @@ -20,7 +20,7 @@ use crate::{elect, PhragmenResult, PhragmenAssignment}; use sp_runtime::{ - assert_eq_error_rate, Perbill, + assert_eq_error_rate, Perbill, PerThing, traits::{Convert, Member, SaturatedConversion} }; use sp_std::collections::btree_map::BTreeMap; @@ -320,10 +320,10 @@ pub(crate) fn create_stake_of(stakes: &[(AccountId, Balance)]) } -pub fn check_assignments(assignments: Vec<(AccountId, Vec>)>) { +pub fn check_assignments(assignments: Vec<(AccountId, Vec>)>) { for (_, a) in assignments { let sum: u32 = a.iter().map(|(_, p)| p.deconstruct()).sum(); - assert_eq_error_rate!(sum, Perbill::accuracy(), 5); + assert_eq_error_rate!(sum, Perbill::ACCURACY, 5); } } @@ -335,7 +335,7 @@ pub(crate) fn run_and_compare( min_to_elect: usize, ) { // run fixed point code. - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>( to_elect, min_to_elect, candidates.clone(), diff --git a/primitives/phragmen/src/tests.rs b/primitives/phragmen/src/tests.rs index b182cfca05a9aa14dc76b56aa6a253e4369b9ce5..9027cc335fce73a6ea1a4b163ae1b6823ef4b914 100644 --- a/primitives/phragmen/src/tests.rs +++ b/primitives/phragmen/src/tests.rs @@ -23,6 +23,8 @@ use crate::{elect, PhragmenResult, PhragmenStakedAssignment, build_support_map, use substrate_test_utils::assert_eq_uvec; use sp_runtime::Perbill; +type Output = Perbill; + #[test] fn float_phragmen_poc_works() { let candidates = vec![1, 2, 3]; @@ -78,7 +80,7 @@ fn phragmen_poc_works() { (30, vec![2, 3]), ]; - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( 2, 2, candidates, @@ -147,7 +149,7 @@ fn phragmen_accuracy_on_large_scale_only_validators() { (5, (u64::max_value() - 2).into()), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( 2, 2, candidates.clone(), @@ -178,7 +180,7 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() { (14, u64::max_value().into()), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( 2, 2, candidates, @@ -210,7 +212,7 @@ fn phragmen_accuracy_on_small_scale_self_vote() { (30, 1), ]); - let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>( 3, 3, candidates, @@ -241,7 +243,7 @@ fn phragmen_accuracy_on_small_scale_no_self_vote() { (3, 1), ]); - let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>( 3, 3, candidates, @@ -275,7 +277,7 @@ fn phragmen_large_scale_test() { (50, 990000000000000000), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( 2, 2, candidates, @@ -302,7 +304,7 @@ fn phragmen_large_scale_test_2() { (50, nom_budget.into()), ]); - let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>( 2, 2, candidates, @@ -367,7 +369,7 @@ fn elect_has_no_entry_barrier() { (2, 10), ]); - let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>( + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>( 3, 3, candidates, @@ -395,7 +397,7 @@ fn minimum_to_elect_is_respected() { (2, 10), ]); - let maybe_result = elect::<_, _, _, TestCurrencyToVote>( + let maybe_result = elect::<_, _, _, TestCurrencyToVote, Output>( 10, 10, candidates, @@ -422,7 +424,7 @@ fn self_votes_should_be_kept() { (1, 8), ]); - let result = elect::<_, _, _, TestCurrencyToVote>( + let result = elect::<_, _, _, TestCurrencyToVote, Output>( 2, 2, candidates, @@ -448,7 +450,8 @@ fn self_votes_should_be_kept() { Balance, AccountId, _, - TestCurrencyToVote + TestCurrencyToVote, + Output, >( &result.winners.into_iter().map(|(who, _)| who).collect(), &result.assignments, diff --git a/primitives/rpc/Cargo.toml b/primitives/rpc/Cargo.toml index 33a854e6b79f1eb1118ec3b5d4c566486b70f2fd..448f8b8c9cd4d714961d1c1f5b496e6cde7c3c13 100644 --- a/primitives/rpc/Cargo.toml +++ b/primitives/rpc/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-rpc" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", features = ["derive"] } diff --git a/primitives/runtime-interface/Cargo.toml b/primitives/runtime-interface/Cargo.toml index 7ed2257556b470c58d3a42c7b83c30c137dd1bf5..cef3acdbfbbb3ce54a42738ea597fbf4a06ceb6a 100644 --- a/primitives/runtime-interface/Cargo.toml +++ b/primitives/runtime-interface/Cargo.toml @@ -3,15 +3,16 @@ name = "sp-runtime-interface" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] -sp-wasm-interface = { version = "2.0.0", optional = true, path = "../wasm-interface" } +sp-wasm-interface = { version = "2.0.0", path = "../wasm-interface", default-features = false } sp-std = { version = "2.0.0", default-features = false, path = "../std" } sp-runtime-interface-proc-macro = { version = "2.0.0", path = "proc-macro" } sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" } codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false } static_assertions = "1.0.0" -primitive-types = { version = "0.6.1", default-features = false } +primitive-types = { version = "0.6.2", default-features = false } [dev-dependencies] sp-runtime-interface-test-wasm = { version = "2.0.0", path = "test-wasm" } @@ -24,7 +25,7 @@ trybuild = "1.0.17" [features] default = [ "std" ] std = [ - "sp-wasm-interface", + "sp-wasm-interface/std", "sp-std/std", "codec/std", "sp-externalities", diff --git a/primitives/runtime-interface/proc-macro/Cargo.toml b/primitives/runtime-interface/proc-macro/Cargo.toml index a5d12de0a648bf22ceef845f9823d7f24bc289a6..b239fbcce32aa0ef3276ea66c9b9bf7e868bccaa 100644 --- a/primitives/runtime-interface/proc-macro/Cargo.toml +++ b/primitives/runtime-interface/proc-macro/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-runtime-interface-proc-macro" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [lib] proc-macro = true diff --git a/primitives/runtime-interface/src/impls.rs b/primitives/runtime-interface/src/impls.rs index cde8e60eea350e805c0bcd72c6848b2f027dfe9e..084b5e11eb3b128c61aae7eefe7bc9baabe71413 100644 --- a/primitives/runtime-interface/src/impls.rs +++ b/primitives/runtime-interface/src/impls.rs @@ -17,7 +17,7 @@ //! Provides implementations for the runtime interface traits. use crate::{ - RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner}, + RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner, Enum}, util::{unpack_ptr_and_len, pack_ptr_and_len}, }; #[cfg(feature = "std")] @@ -38,9 +38,6 @@ use sp_std::{any::TypeId, mem, vec::Vec}; #[cfg(feature = "std")] use sp_std::borrow::Cow; -#[cfg(not(feature = "std"))] -use sp_std::{slice, boxed::Box}; - // Make sure that our assumptions for storing a pointer + its size in `u64` is valid. #[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))] assert_eq_size!(usize, u32); @@ -196,11 +193,16 @@ impl FromFFIValue for Vec { let (ptr, len) = unpack_ptr_and_len(arg); let len = len as usize; + if len == 0 { + return Vec::new(); + } + + let data = unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) }; + if TypeId::of::() == TypeId::of::() { - unsafe { mem::transmute(Vec::from_raw_parts(ptr as *mut u8, len, len)) } + unsafe { mem::transmute(data) } } else { - let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) }; - Self::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed") + Self::decode(&mut &data[..]).expect("Host to wasm values are encoded correctly; qed") } } } @@ -302,10 +304,9 @@ macro_rules! impl_traits_for_arrays { impl FromFFIValue for [u8; $n] { fn from_ffi_value(arg: u32) -> [u8; $n] { let mut res = [0u8; $n]; - res.copy_from_slice(unsafe { slice::from_raw_parts(arg as *const u8, $n) }); + let data = unsafe { Vec::from_raw_parts(arg as *mut u8, $n, $n) }; - // Make sure we free the pointer. - let _ = unsafe { Box::from_raw(arg as *mut u8) }; + res.copy_from_slice(&data); res } @@ -523,3 +524,11 @@ macro_rules! for_u128_i128 { for_u128_i128!(u128); for_u128_i128!(i128); + +impl PassBy for sp_wasm_interface::ValueType { + type PassBy = Enum; +} + +impl PassBy for sp_wasm_interface::Value { + type PassBy = Codec; +} diff --git a/primitives/runtime-interface/src/pass_by.rs b/primitives/runtime-interface/src/pass_by.rs index 597a0284eee2a8354e3a963f007cfba244a6f58e..d6767b5ebbe93dfb131c13564b4f900d6bdca0bd 100644 --- a/primitives/runtime-interface/src/pass_by.rs +++ b/primitives/runtime-interface/src/pass_by.rs @@ -32,7 +32,7 @@ use sp_wasm_interface::{FunctionContext, Pointer, Result}; use sp_std::{marker::PhantomData, convert::TryFrom}; #[cfg(not(feature = "std"))] -use sp_std::{slice, vec::Vec}; +use sp_std::vec::Vec; /// Derive macro for implementing [`PassBy`] with the [`Codec`] strategy. /// @@ -255,8 +255,13 @@ impl PassByImpl for Codec { let (ptr, len) = unpack_ptr_and_len(arg); let len = len as usize; - let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) }; - T::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed") + let encoded = if len == 0 { + Vec::new() + } else { + unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) } + }; + + T::decode(&mut &encoded[..]).expect("Host to wasm values are encoded correctly; qed") } } diff --git a/primitives/runtime-interface/test-wasm/Cargo.toml b/primitives/runtime-interface/test-wasm/Cargo.toml index b3a400a12d39382d249fed80bb7392ef40803024..c38413aee78e8711129158958291e8c08e14fb2a 100644 --- a/primitives/runtime-interface/test-wasm/Cargo.toml +++ b/primitives/runtime-interface/test-wasm/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" +license = "GPL-3.0" [dependencies] sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../" } diff --git a/primitives/runtime-interface/test-wasm/build.rs b/primitives/runtime-interface/test-wasm/build.rs index 9c81ea6f38b8df4812805e75501414c862e50d11..647b4768141d2203155bf5325f9ff87f7d9c2446 100644 --- a/primitives/runtime-interface/test-wasm/build.rs +++ b/primitives/runtime-interface/test-wasm/build.rs @@ -14,17 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; +use wasm_builder_runner::WasmBuilder; fn main() { - build_current_project_with_rustflags( - "wasm_binary.rs", - WasmBuilderSource::CratesOrPath { - path: "../../../utils/wasm-builder", - version: "1.0.9", - }, - // This instructs LLD to export __heap_base as a global variable, which is used by the - // external memory allocator. - "-Clink-arg=--export=__heap_base", - ); + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_from_crates_or_path("1.0.9", "../../../utils/wasm-builder") + .export_heap_base() + .import_memory() + .build() } diff --git a/primitives/runtime-interface/test-wasm/src/lib.rs b/primitives/runtime-interface/test-wasm/src/lib.rs index 67fbfdcfec602041f1ac5881090d5781ceb9e1f9..467f58cb30c7c181f219bcf9deed1db6a9525731 100644 --- a/primitives/runtime-interface/test-wasm/src/lib.rs +++ b/primitives/runtime-interface/test-wasm/src/lib.rs @@ -39,6 +39,17 @@ pub trait TestApi { data } + /// Returns 16kb data. + /// + /// # Note + /// + /// We return a `Vec` because this will use the code path that uses SCALE + /// to pass the data between native/wasm. (Vec is passed without encoding the + /// data) + fn return_16kb() -> Vec { + vec![0; 4 * 1024] + } + /// Set the storage at key with value. fn set_storage(&mut self, key: &[u8], data: &[u8]) { self.place_storage(key.to_vec(), Some(data.to_vec())); @@ -97,6 +108,7 @@ pub trait TestApi { /// Two random external functions from the old runtime interface. /// This ensures that we still inherently export these functions from the host and that we are still /// compatible with old wasm runtimes. +#[cfg(not(feature = "std"))] extern "C" { pub fn ext_clear_storage(key_data: *const u8, key_len: u32); pub fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8); @@ -104,6 +116,7 @@ extern "C" { /// Make sure the old runtime interface needs to be imported. #[no_mangle] +#[cfg(not(feature = "std"))] pub fn force_old_runtime_interface_import() { unsafe { ext_clear_storage(sp_std::ptr::null(), 0); } unsafe { ext_keccak_256(sp_std::ptr::null(), 0, sp_std::ptr::null_mut()); } @@ -211,4 +224,28 @@ wasm_export_functions! { assert_eq!(*val, test_api::get_and_return_i128(*val)); } } + + fn test_vec_return_value_memory_is_freed() { + let mut len = 0; + for _ in 0..1024 { + len += test_api::return_16kb().len(); + } + assert_eq!(1024 * 1024 * 4, len); + } + + fn test_encoded_return_value_memory_is_freed() { + let mut len = 0; + for _ in 0..1024 { + len += test_api::return_option_input(vec![0; 16 * 1024]).map(|v| v.len()).unwrap(); + } + assert_eq!(1024 * 1024 * 16, len); + } + + fn test_array_return_value_memory_is_freed() { + let mut len = 0; + for _ in 0..1024 * 1024 { + len += test_api::get_and_return_array([0; 34])[1]; + } + assert_eq!(0, len); + } } diff --git a/primitives/runtime-interface/test/Cargo.toml b/primitives/runtime-interface/test/Cargo.toml index f0a4c0edd4a5399dc5b6577159e94f7f1b37de39..d6d724da9db0d76e8b6f22265e3bd0c8184ccfb2 100644 --- a/primitives/runtime-interface/test/Cargo.toml +++ b/primitives/runtime-interface/test/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-runtime-interface-test" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" publish = false [dependencies] diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index 48c120b2c9fd1c9b875a3024f8279fc904605805..559a4281e09f211a0c5553b76dac1e3231c06675 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -113,3 +113,18 @@ fn test_overwrite_native_function_implementation() { fn test_u128_i128_as_parameter_and_return_value() { call_wasm_method::("test_u128_i128_as_parameter_and_return_value"); } + +#[test] +fn test_vec_return_value_memory_is_freed() { + call_wasm_method::("test_vec_return_value_memory_is_freed"); +} + +#[test] +fn test_encoded_return_value_memory_is_freed() { + call_wasm_method::("test_encoded_return_value_memory_is_freed"); +} + +#[test] +fn test_array_return_value_memory_is_freed() { + call_wasm_method::("test_array_return_value_memory_is_freed"); +} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 2574fef4b982635d67cd249afb2884097555f58f..bf7b2b80a3c5a278dc61745855c5c1096c8e9e3d 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -3,10 +3,11 @@ name = "sp-runtime" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.1.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.1.2", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0", default-features = false, path = "../core" } sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } sp-arithmetic = { version = "2.0.0", default-features = false, path = "../arithmetic" } @@ -17,6 +18,7 @@ paste = "0.1.6" rand = { version = "0.7.2", optional = true } impl-trait-for-tuples = "0.1.3" sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [dev-dependencies] serde_json = "1.0.41" @@ -36,4 +38,5 @@ std = [ "sp-io/std", "serde", "sp-inherents/std", + "parity-util-mem/std", ] diff --git a/primitives/runtime/src/curve.rs b/primitives/runtime/src/curve.rs index b45501fdb764e32b2e00df31d8069b7e9c39980b..a230e2f32d56f98f49be9884d913f0cc5f79e4fa 100644 --- a/primitives/runtime/src/curve.rs +++ b/primitives/runtime/src/curve.rs @@ -16,7 +16,7 @@ //! Provides some utilities to define a piecewise linear function. -use crate::{Perbill, traits::{SimpleArithmetic, SaturatedConversion}}; +use crate::{Perbill, PerThing, traits::{AtLeast32Bit, SaturatedConversion}}; use core::ops::Sub; /// Piecewise Linear function in [0, 1] -> [0, 1]. @@ -35,7 +35,7 @@ fn abs_sub + Clone>(a: N, b: N) -> N where { impl<'a> PiecewiseLinear<'a> { /// Compute `f(n/d)*d` with `n <= d`. This is useful to avoid loss of precision. pub fn calculate_for_fraction_times_denominator(&self, n: N, d: N) -> N where - N: SimpleArithmetic + Clone + N: AtLeast32Bit + Clone { let n = n.min(d.clone()); @@ -79,7 +79,7 @@ impl<'a> PiecewiseLinear<'a> { // This is guaranteed not to overflow on whatever values nor lose precision. // `q` must be superior to zero. fn multiply_by_rational_saturating(value: N, p: u32, q: u32) -> N - where N: SimpleArithmetic + Clone + where N: AtLeast32Bit + Clone { let q = q.max(1); diff --git a/primitives/runtime/src/generic/block.rs b/primitives/runtime/src/generic/block.rs index 21e65d1fb52339c28aae93ae92b83a577990319d..a46396dce08f45601bdea40dd0dc62aba82d546a 100644 --- a/primitives/runtime/src/generic/block.rs +++ b/primitives/runtime/src/generic/block.rs @@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize}; use sp_std::prelude::*; use sp_core::RuntimeDebug; use crate::codec::{Codec, Encode, Decode}; -use crate::traits::{self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize}; +use crate::traits::{self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf}; use crate::Justification; /// Something to identify a block. @@ -63,7 +63,7 @@ impl fmt::Display for BlockId { /// Abstraction over a substrate block. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))] pub struct Block { @@ -76,7 +76,7 @@ pub struct Block { impl traits::Block for Block where Header: HeaderT, - Extrinsic: Member + Codec + traits::Extrinsic, + Extrinsic: Member + Codec + traits::Extrinsic + MaybeMallocSizeOf, { type Extrinsic = Extrinsic; type Header = Header; diff --git a/primitives/runtime/src/generic/digest.rs b/primitives/runtime/src/generic/digest.rs index fef02d4f00959394fc2745ea9397bfbb0c6ae00b..9d4fbe48d55a57e80ad8e3872f85d86c1d631e3d 100644 --- a/primitives/runtime/src/generic/digest.rs +++ b/primitives/runtime/src/generic/digest.rs @@ -27,7 +27,7 @@ use sp_core::{ChangesTrieConfiguration, RuntimeDebug}; /// Generic header digest. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf))] pub struct Digest { /// A list of logs in the digest. pub logs: Vec>, @@ -74,6 +74,7 @@ impl Digest { /// Digest item that is able to encode/decode 'system' digest items and /// provide opaque access to other items. #[derive(PartialEq, Eq, Clone, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(parity_util_mem::MallocSizeOf))] pub enum DigestItem { /// System digest item that contains the root of changes trie at given /// block. It is created for every block iff runtime supports changes @@ -86,6 +87,12 @@ pub enum DigestItem { /// the consensus engine can (and should) read them itself to avoid /// code and state duplication. It is erroneous for a runtime to produce /// these, but this is not (yet) checked. + /// + /// NOTE: the runtime is not allowed to panic or fail in an `on_initialize` + /// call if an expected `PreRuntime` digest is not present. It is the + /// responsibility of a external block verifier to check this. Runtime API calls + /// will initialize the block without pre-runtime digests, so initialization + /// cannot fail when they are missing. PreRuntime(ConsensusEngineId, Vec), /// A message from the runtime to the consensus engine. This should *never* @@ -107,7 +114,7 @@ pub enum DigestItem { /// Available changes trie signals. #[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[cfg_attr(feature = "std", derive(Debug, parity_util_mem::MallocSizeOf))] pub enum ChangesTrieSignal { /// New changes trie configuration is enacted, starting from **next block**. /// diff --git a/primitives/runtime/src/generic/era.rs b/primitives/runtime/src/generic/era.rs index 238130db00c7e9a0a8bfbd717ac46c232e53f907..37b4b495fef540eb3b942098e3f54f11dfd41d1e 100644 --- a/primitives/runtime/src/generic/era.rs +++ b/primitives/runtime/src/generic/era.rs @@ -47,14 +47,14 @@ pub enum Era { } /* -E.g. with period == 4: -0 10 20 30 40 -0123456789012345678901234567890123456789012 - |...| - authored -/ \- expiry -phase = 1 -n = Q(current - phase, period) + phase -*/ + * E.g. with period == 4: + * 0 10 20 30 40 + * 0123456789012345678901234567890123456789012 + * |...| + * authored -/ \- expiry + * phase = 1 + * n = Q(current - phase, period) + phase + */ impl Era { /// Create a new era based on a period (which should be a power of two between 4 and 65536 inclusive) /// and a block number on which it should start (or, for long periods, be shortly after the start). diff --git a/primitives/runtime/src/generic/header.rs b/primitives/runtime/src/generic/header.rs index 51f31af0781d02c1ce3f9804d1c5895815801930..5efb36603d572fd7be350f7b046e8b5969408cc6 100644 --- a/primitives/runtime/src/generic/header.rs +++ b/primitives/runtime/src/generic/header.rs @@ -20,8 +20,9 @@ use serde::{Deserialize, Serialize}; use crate::codec::{Decode, Encode, Codec, Input, Output, HasCompact, EncodeAsRef, Error}; use crate::traits::{ - self, Member, SimpleArithmetic, SimpleBitOps, Hash as HashT, + self, Member, AtLeast32Bit, SimpleBitOps, Hash as HashT, MaybeSerializeDeserialize, MaybeSerialize, MaybeDisplay, + MaybeMallocSizeOf, }; use crate::generic::Digest; use sp_core::U256; @@ -51,6 +52,22 @@ pub struct Header + TryFrom, Hash: HashT> { pub digest: Digest, } +#[cfg(feature = "std")] +impl parity_util_mem::MallocSizeOf for Header +where + Number: Copy + Into + TryFrom + parity_util_mem::MallocSizeOf, + Hash: HashT, + Hash::Output: parity_util_mem::MallocSizeOf, +{ + fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + self.parent_hash.size_of(ops) + + self.number.size_of(ops) + + self.state_root.size_of(ops) + + self.extrinsics_root.size_of(ops) + + self.digest.size_of(ops) + } +} + #[cfg(feature = "std")] pub fn serialize_number + TryFrom>( val: &T, s: S, @@ -105,10 +122,11 @@ impl codec::EncodeLike for Header where impl traits::Header for Header where Number: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash + MaybeDisplay + - SimpleArithmetic + Codec + Copy + Into + TryFrom + sp_std::str::FromStr, + AtLeast32Bit + Codec + Copy + Into + TryFrom + sp_std::str::FromStr + + MaybeMallocSizeOf, Hash: HashT, Hash::Output: Default + sp_std::hash::Hash + Copy + Member + Ord + - MaybeSerialize + Debug + MaybeDisplay + SimpleBitOps + Codec, + MaybeSerialize + Debug + MaybeDisplay + SimpleBitOps + Codec + MaybeMallocSizeOf, { type Number = Number; type Hash = ::Output; @@ -152,7 +170,7 @@ impl traits::Header for Header where } impl Header where - Number: Member + sp_std::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec + Into + TryFrom, + Number: Member + sp_std::hash::Hash + Copy + MaybeDisplay + AtLeast32Bit + Codec + Into + TryFrom, Hash: HashT, Hash::Output: Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, { diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index 0a435242485cbacc52941c0ebd0ec12698f227e3..a516bc1f7fa999f1087718f93f072ff8eff45099 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -20,7 +20,10 @@ use sp_std::{fmt, prelude::*}; use sp_io::hashing::blake2_256; use codec::{Decode, Encode, EncodeLike, Input, Error}; use crate::{ - traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, IdentifyAccount}, + traits::{ + self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, ExtrinsicMetadata, + IdentifyAccount, + }, generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction}, }; @@ -41,6 +44,18 @@ where pub function: Call, } +#[cfg(feature = "std")] +impl parity_util_mem::MallocSizeOf + for UncheckedExtrinsic +where + Extra: SignedExtension +{ + fn size_of(&self, _ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + // Instantiated only in runtime. + 0 + } +} + impl UncheckedExtrinsic { @@ -130,6 +145,15 @@ where } } +impl ExtrinsicMetadata + for UncheckedExtrinsic + where + Extra: SignedExtension, +{ + const VERSION: u8 = TRANSACTION_VERSION; + type SignedExtensions = Extra; +} + /// A payload that has been signed for an unchecked extrinsics. /// /// Note that the payload that we sign to produce unchecked extrinsic signature @@ -262,6 +286,19 @@ impl s } } +#[cfg(feature = "std")] +impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension> serde::Deserialize<'a> + for UncheckedExtrinsic +{ + fn deserialize(de: D) -> Result where + D: serde::Deserializer<'a>, + { + let r = sp_core::bytes::deserialize(de)?; + Decode::decode(&mut &r[..]) + .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e))) + } +} + impl fmt::Debug for UncheckedExtrinsic where @@ -316,6 +353,7 @@ mod tests { #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)] struct TestExtra; impl SignedExtension for TestExtra { + const IDENTIFIER: &'static str = "TestExtra"; type AccountId = u64; type Call = (); type AdditionalSigned = (); diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 46930c35e8e8ddb8217cd967b4002ed675e6cee9..517141a210e0678b45f7031f3dec705ed99dad86 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -68,7 +68,7 @@ pub use sp_application_crypto::{RuntimeAppPublic, BoundToRuntimeAppPublic}; pub use sp_core::RuntimeDebug; /// Re-export top-level arithmetic stuff. -pub use sp_arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64}; +pub use sp_arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64, PerThing}; /// Re-export 128 bit helpers. pub use sp_arithmetic::helpers_128bit; /// Re-export big_uint stuff. @@ -405,13 +405,13 @@ impl From<&'static str> for DispatchError { } } -impl Into<&'static str> for DispatchError { - fn into(self) -> &'static str { - match self { - Self::Other(msg) => msg, - Self::CannotLookup => "Can not lookup", - Self::BadOrigin => "Bad origin", - Self::Module { message, .. } => message.unwrap_or("Unknown module error"), +impl From for &'static str { + fn from(err: DispatchError) -> &'static str { + match err { + DispatchError::Other(msg) => msg, + DispatchError::CannotLookup => "Can not lookup", + DispatchError::BadOrigin => "Bad origin", + DispatchError::Module { message, .. } => message.unwrap_or("Unknown module error"), } } } @@ -639,6 +639,13 @@ macro_rules! assert_eq_error_rate { #[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] pub struct OpaqueExtrinsic(pub Vec); +#[cfg(feature = "std")] +impl parity_util_mem::MallocSizeOf for OpaqueExtrinsic { + fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + self.0.size_of(ops) + } +} + impl sp_std::fmt::Debug for OpaqueExtrinsic { #[cfg(feature = "std")] fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { @@ -678,6 +685,18 @@ pub fn print(print: impl traits::Printable) { print.print(); } +/// An alphabet of possible parameters to use for benchmarking. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug)] +#[allow(missing_docs)] +pub enum BenchmarkParameter { + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, +} + +/// Results from running benchmarks on a FRAME pallet. +/// Contains duration of the function call in nanoseconds along with the benchmark parameters +/// used for that benchmark result. +pub type BenchmarkResults = (Vec<(BenchmarkParameter, u32)>, u128); + #[cfg(test)] mod tests { use super::*; diff --git a/primitives/runtime/src/offchain/mod.rs b/primitives/runtime/src/offchain/mod.rs index 742388f9ec422bc7c31fa56b310b76c049247b21..dfc15360c6c91aa995e48f946d84eda741cb454c 100644 --- a/primitives/runtime/src/offchain/mod.rs +++ b/primitives/runtime/src/offchain/mod.rs @@ -17,3 +17,4 @@ //! A collection of higher lever helpers for offchain calls. pub mod http; +pub mod storage; diff --git a/primitives/runtime/src/offchain/storage.rs b/primitives/runtime/src/offchain/storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..d5c2b47298450ff1f5f714f69d8c0abdaccb40a6 --- /dev/null +++ b/primitives/runtime/src/offchain/storage.rs @@ -0,0 +1,156 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! A set of storage helpers for offchain workers. + +use sp_core::offchain::StorageKind; + +/// A storage value with a static key. +pub type StorageValue = StorageValueRef<'static>; + +/// An abstraction over local storage value. +pub struct StorageValueRef<'a> { + key: &'a [u8], + kind: StorageKind, +} + +impl<'a> StorageValueRef<'a> { + /// Create a new reference to a value in the persistent local storage. + pub fn persistent(key: &'a [u8]) -> Self { + Self { key, kind: StorageKind::PERSISTENT } + } + + /// Create a new reference to a value in the fork-aware local storage. + pub fn local(key: &'a [u8]) -> Self { + Self { key, kind: StorageKind::LOCAL } + } + + /// Set the value of the storage to encoding of given parameter. + /// + /// Note that the storage may be accessed by workers running concurrently, + /// if you happen to write a `get-check-set` pattern you should most likely + /// be using `mutate` instead. + pub fn set(&self, value: &impl codec::Encode) { + value.using_encoded(|val| { + sp_io::offchain::local_storage_set(self.kind, self.key, val) + }) + } + + /// Retrieve & decode the value from storage. + /// + /// Note that if you want to do some checks based on the value + /// and write changes after that you should rather be using `mutate`. + /// + /// The function returns `None` if the value was not found in storage, + /// otherwise a decoding of the value to requested type. + pub fn get(&self) -> Option> { + sp_io::offchain::local_storage_get(self.kind, self.key) + .map(|val| T::decode(&mut &*val).ok()) + } + + /// Retrieve & decode the value and set it to a new one atomicaly. + /// + /// Function `f` should return a new value that we should attempt to write to storage. + /// This function returns: + /// 1. `Ok(Ok(T))` in case the value has been succesfuly set. + /// 2. `Ok(Err(T))` in case the value was returned, but it couldn't have been set. + /// 3. `Err(_)` in case `f` returns an error. + pub fn mutate(&self, f: F) -> Result, E> where + T: codec::Codec, + F: FnOnce(Option>) -> Result + { + let value = sp_io::offchain::local_storage_get(self.kind, self.key); + let decoded = value.as_deref().map(|mut v| T::decode(&mut v).ok()); + let val = f(decoded)?; + let set = val.using_encoded(|new_val| { + sp_io::offchain::local_storage_compare_and_set( + self.kind, + self.key, + value, + new_val, + ) + }); + + if set { + Ok(Ok(val)) + } else { + Ok(Err(val)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_io::TestExternalities; + use sp_core::offchain::{ + OffchainExt, + OffchainStorage, + testing, + }; + + #[test] + fn should_set_and_get() { + let (offchain, state) = testing::TestOffchainExt::new(); + let mut t = TestExternalities::default(); + t.register_extension(OffchainExt::new(offchain)); + + t.execute_with(|| { + let val = StorageValue::persistent(b"testval"); + + assert_eq!(val.get::(), None); + + val.set(&15_u32); + + assert_eq!(val.get::(), Some(Some(15_u32))); + assert_eq!(val.get::>(), Some(None)); + assert_eq!( + state.read().persistent_storage.get(b"", b"testval"), + Some(vec![15_u8, 0, 0, 0]) + ); + }) + } + + #[test] + fn should_mutate() { + let (offchain, state) = testing::TestOffchainExt::new(); + let mut t = TestExternalities::default(); + t.register_extension(OffchainExt::new(offchain)); + + t.execute_with(|| { + let val = StorageValue::persistent(b"testval"); + + let result = val.mutate::(|val| { + assert_eq!(val, None); + + Ok(16_u32) + }); + assert_eq!(result, Ok(Ok(16_u32))); + assert_eq!(val.get::(), Some(Some(16_u32))); + assert_eq!( + state.read().persistent_storage.get(b"", b"testval"), + Some(vec![16_u8, 0, 0, 0]) + ); + + // mutate again, but this time early-exit. + let res = val.mutate::(|val| { + assert_eq!(val, Some(Some(16_u32))); + Err(()) + }); + assert_eq!(res, Err(())); + }) + } +} diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 516d58112647db643a3e0c88e7ff50b4f9fa9a10..e3e94c3c9f08999ca4034618cda6edcb697f453c 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -115,6 +115,10 @@ impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId { u64::from_le_bytes(msg_signature) == *signature } + + fn to_raw_vec(&self) -> Vec { + AsRef::<[u8]>::as_ref(self).to_vec() + } } impl OpaqueKeys for UintAuthorityId { @@ -144,7 +148,7 @@ pub type DigestItem = generic::DigestItem; pub type Digest = generic::Digest; /// Block Header -#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, Default)] +#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, Default, parity_util_mem::MallocSizeOf)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub struct Header { @@ -216,10 +220,12 @@ impl<'a> Deserialize<'a> for Header { } /// An opaque extrinsic wrapper type. -#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] +#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode, parity_util_mem::MallocSizeOf)] pub struct ExtrinsicWrapper(Xt); -impl traits::Extrinsic for ExtrinsicWrapper { +impl traits::Extrinsic for ExtrinsicWrapper +where Xt: parity_util_mem::MallocSizeOf +{ type Call = (); type SignaturePayload = (); @@ -249,7 +255,7 @@ impl Deref for ExtrinsicWrapper { } /// Testing block -#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode)] +#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, parity_util_mem::MallocSizeOf)] pub struct Block { /// Block header pub header: Header, @@ -296,6 +302,9 @@ impl<'a, Xt> Deserialize<'a> for Block where Block: Decode { #[derive(PartialEq, Eq, Clone, Encode, Decode)] pub struct TestXt(pub Option<(u64, Extra)>, pub Call); +// Non-opaque extrinsics always 0. +parity_util_mem::malloc_size_of_is_0!(any: TestXt); + impl Serialize for TestXt where TestXt: Encode { fn serialize(&self, seq: S) -> Result where S: Serializer { self.using_encoded(|bytes| seq.serialize_bytes(bytes)) diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 2547ce1072185d07a3404740f75d5b87ef302ab0..4cb9cb32123e4fe81c1ced327c2c2c27c86dad02 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -22,15 +22,18 @@ use sp_io; #[cfg(feature = "std")] use std::fmt::Display; #[cfg(feature = "std")] +use std::str::FromStr; +#[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; use sp_core::{self, Hasher, Blake2Hasher, TypeId, RuntimeDebug}; +use crate::BenchmarkParameter; use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction, }; use crate::generic::{Digest, DigestItem}; pub use sp_arithmetic::traits::{ - SimpleArithmetic, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion, + AtLeast32Bit, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion, Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr, IntegerSquareRoot }; @@ -458,53 +461,25 @@ impl CheckEqual for super::generic::DigestItem whe } } -macro_rules! impl_maybe_marker { - ( $( $(#[$doc:meta])+ $trait_name:ident: $($trait_bound:path),+ );+ ) => { - $( - $(#[$doc])+ - #[cfg(feature = "std")] - pub trait $trait_name: $($trait_bound +)+ {} - #[cfg(feature = "std")] - impl $trait_name for T {} - - $(#[$doc])+ - #[cfg(not(feature = "std"))] - pub trait $trait_name {} - #[cfg(not(feature = "std"))] - impl $trait_name for T {} - )+ - } -} - -impl_maybe_marker!( +sp_core::impl_maybe_marker!( /// A type that implements Display when in std environment. - MaybeDisplay: Display; + trait MaybeDisplay: Display; + + /// A type that implements FromStr when in std environment. + trait MaybeFromStr: FromStr; /// A type that implements Hash when in std environment. - MaybeHash: sp_std::hash::Hash; + trait MaybeHash: sp_std::hash::Hash; /// A type that implements Serialize when in std environment. - MaybeSerialize: Serialize; + trait MaybeSerialize: Serialize; /// A type that implements Serialize, DeserializeOwned and Debug when in std environment. - MaybeSerializeDeserialize: DeserializeOwned, Serialize -); + trait MaybeSerializeDeserialize: DeserializeOwned, Serialize; -/// A type that provides a randomness beacon. -pub trait RandomnessBeacon { - /// Returns 32 bytes of random data. The output will change eventually, but - /// is not guaranteed to be different between any two calls. - /// - /// # Security - /// - /// This MUST NOT be used for gambling, as it can be influenced by a - /// malicious validator in the short term. It MAY be used in many - /// cryptographic protocols, however, so long as one remembers that this - /// (like everything else on-chain) is public. For example, it can be - /// used where a number is needed that cannot have been chosen by an - /// adversary, for purposes such as public-coin zero-knowledge proofs. - fn random() -> [u8; 32]; -} + /// A type that implements MallocSizeOf. + trait MaybeMallocSizeOf: parity_util_mem::MallocSizeOf; +); /// A type that can be used in runtime structures. pub trait Member: Send + Sync + Sized + Debug + Eq + PartialEq + Clone + 'static {} @@ -521,13 +496,18 @@ pub trait IsMember { /// `parent_hash`, as well as a `digest` and a block `number`. /// /// You can also create a `new` one from those fields. -pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 'static { +pub trait Header: + Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + + MaybeMallocSizeOf + 'static +{ /// Header number. type Number: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash - + Copy + MaybeDisplay + SimpleArithmetic + Codec + sp_std::str::FromStr; + + Copy + MaybeDisplay + AtLeast32Bit + Codec + sp_std::str::FromStr + + MaybeMallocSizeOf; /// Header hash type type Hash: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash + Ord - + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>; + + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + + AsMut<[u8]> + MaybeMallocSizeOf; /// Hashing algorithm type Hashing: Hash; @@ -575,14 +555,15 @@ pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 's /// `Extrinsic` pieces of information as well as a `Header`. /// /// You can get an iterator over each of the `extrinsics` and retrieve the `header`. -pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 'static { +pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + MaybeMallocSizeOf + 'static { /// Type for extrinsics. - type Extrinsic: Member + Codec + Extrinsic + MaybeSerialize; + type Extrinsic: Member + Codec + Extrinsic + MaybeSerialize + MaybeMallocSizeOf; /// Header type. - type Header: Header; + type Header: Header + MaybeMallocSizeOf; /// Block hash type. type Hash: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash + Ord - + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>; + + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]> + + MaybeMallocSizeOf; /// Returns a reference to the header. fn header(&self) -> &Self::Header; @@ -601,8 +582,9 @@ pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 'st fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec; } + /// Something that acts like an `Extrinsic`. -pub trait Extrinsic: Sized { +pub trait Extrinsic: Sized + MaybeMallocSizeOf { /// The function call. type Call; @@ -626,6 +608,15 @@ pub trait Extrinsic: Sized { fn new(_call: Self::Call, _signed_data: Option) -> Option { None } } +/// Implementor is an [`Extrinsic`] and provides metadata about this extrinsic. +pub trait ExtrinsicMetadata { + /// The version of the `Extrinsic`. + const VERSION: u8; + + /// Signed extensions attached to this `Extrinsic`. + type SignedExtensions: SignedExtension; +} + /// Extract the hasher type for a block. pub type HasherFor = as Hash>::Hasher; /// Extract the hashing type for a block. @@ -686,6 +677,12 @@ pub trait Dispatchable { /// Means by which a transaction may be extended. This type embodies both the data and the logic /// that should be additionally associated with the transaction. It should be plain old data. pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq { + /// Unique identifier of this signed extension. + /// + /// This will be exposed in the metadata to identify the signed extension used + /// in an extrinsic. + const IDENTIFIER: &'static str; + /// The type which encodes the sender identity. type AccountId; @@ -783,6 +780,17 @@ pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq /// Do any post-flight stuff for a transaction. fn post_dispatch(_pre: Self::Pre, _info: Self::DispatchInfo, _len: usize) { } + + /// Returns the list of unique identifier for this signed extension. + /// + /// As a [`SignedExtension`] can be a tuple of [`SignedExtension`]`s we need to return a `Vec` + /// that holds all the unique identifiers. Each individual `SignedExtension` must return + /// *exactly* one identifier. + /// + /// This method provides a default implementation that returns `vec![SELF::IDENTIFIER]`. + fn identifier() -> Vec<&'static str> { + sp_std::vec![Self::IDENTIFIER] + } } #[impl_for_tuples(1, 12)] @@ -791,6 +799,7 @@ impl SignedExtension for Tuple { type AccountId = AccountId; type Call = Call; type DispatchInfo = Info; + const IDENTIFIER: &'static str = "You should call `identifier()`!"; for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); for_tuples!( type Pre = ( #( Tuple::Pre ),* ); ); @@ -841,6 +850,12 @@ impl SignedExtension for Tuple { ) { for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info.clone(), len); )* ) } + + fn identifier() -> Vec<&'static str> { + let mut ids = Vec::new(); + for_tuples!( #( ids.extend(Tuple::identifier()); )* ); + ids + } } /// Only for bare bone testing when you don't care about signed extensions at all. @@ -851,6 +866,7 @@ impl SignedExtension for () { type Call = (); type Pre = (); type DispatchInfo = (); + const IDENTIFIER: &'static str = "UnitSignedExtension"; fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } } @@ -1124,12 +1140,14 @@ macro_rules! count { #[macro_export] macro_rules! impl_opaque_keys { ( + $( #[ $attr:meta ] )* pub struct $name:ident { $( pub $field:ident: $type:ty, )* } ) => { + $( #[ $attr ] )* #[derive( Default, Clone, PartialEq, Eq, $crate::codec::Encode, @@ -1161,6 +1179,37 @@ macro_rules! impl_opaque_keys { }; $crate::codec::Encode::encode(&keys) } + + /// Converts `Self` into a `Vec` of `(raw public key, KeyTypeId)`. + pub fn into_raw_public_keys( + self, + ) -> $crate::sp_std::vec::Vec<($crate::sp_std::vec::Vec, $crate::KeyTypeId)> { + let mut keys = Vec::new(); + $( + keys.push(( + $crate::RuntimeAppPublic::to_raw_vec(&self.$field), + < + < + $type as $crate::BoundToRuntimeAppPublic + >::Public as $crate::RuntimeAppPublic + >::ID, + )); + )* + + keys + } + + /// Decode `Self` from the given `encoded` slice and convert `Self` into the raw public + /// keys (see [`Self::into_raw_public_keys`]). + /// + /// Returns `None` when the decoding failed, otherwise `Some(_)`. + pub fn decode_into_raw_public_keys( + encoded: &[u8], + ) -> Option<$crate::sp_std::vec::Vec<($crate::sp_std::vec::Vec, $crate::KeyTypeId)>> { + ::decode(&mut &encoded[..]) + .ok() + .map(|s| s.into_raw_public_keys()) + } } impl $crate::traits::OpaqueKeys for $name { @@ -1201,6 +1250,12 @@ pub trait Printable { fn print(&self); } +impl Printable for &T { + fn print(&self) { + (*self).print() + } +} + impl Printable for u8 { fn print(&self) { (*self as u64).print() @@ -1263,6 +1318,75 @@ pub trait BlockIdTo { ) -> Result>, Self::Error>; } +/// The pallet benchmarking trait. +pub trait Benchmarking { + /// Run the benchmarks for this pallet. + /// + /// Parameters + /// - `extrinsic`: The name of extrinsic function you want to benchmark encoded as bytes. + /// - `steps`: The number of sample points you want to take across the range of parameters. + /// - `repeat`: The number of times you want to repeat a benchmark. + fn run_benchmark(extrinsic: Vec, steps: u32, repeat: u32) -> Result, &'static str>; +} + +/// The required setup for creating a benchmark. +pub trait BenchmarkingSetup { + /// Return the components and their ranges which should be tested in this benchmark. + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)>; + + /// Set up the storage, and prepare a call and caller to test in a single run of the benchmark. + fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(Call, RawOrigin), &'static str>; +} + +/// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`. +/// +/// Every variant must implement [`BenchmarkingSetup`](crate::traits::BenchmarkingSetup). +/// +/// ```nocompile +/// +/// struct Transfer; +/// impl BenchmarkingSetup for Transfer { ... } +/// +/// struct SetBalance; +/// impl BenchmarkingSetup for SetBalance { ... } +/// +/// selected_benchmark!(Transfer, SetBalance); +/// ``` +#[macro_export] +macro_rules! selected_benchmark { + ($($bench:ident),*) => { + // The list of available benchmarks for this pallet. + enum SelectedBenchmark { + $( $bench, )* + } + + // Allow us to select a benchmark from the list of available benchmarks. + impl $crate::traits::BenchmarkingSetup, RawOrigin> for SelectedBenchmark { + fn components(&self) -> Vec<(BenchmarkParameter, u32, u32)> { + match self { + $( Self::$bench => <$bench as $crate::traits::BenchmarkingSetup< + T, + Call, + RawOrigin, + >>::components(&$bench), )* + } + } + + fn instance(&self, components: &[(BenchmarkParameter, u32)]) + -> Result<(Call, RawOrigin), &'static str> + { + match self { + $( Self::$bench => <$bench as $crate::traits::BenchmarkingSetup< + T, + Call, + RawOrigin, + >>::instance(&$bench, components), )* + } + } + } + }; +} + #[cfg(test)] mod tests { use super::*; diff --git a/primitives/sandbox/Cargo.toml b/primitives/sandbox/Cargo.toml index 5d161a6deb9dacca4e452131811eb13eb55387bb..c942e019fe6f68462e3ac6f73a1ee6d125e56334 100755 --- a/primitives/sandbox/Cargo.toml +++ b/primitives/sandbox/Cargo.toml @@ -3,12 +3,14 @@ name = "sp-sandbox" version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] wasmi = { version = "0.6.2", optional = true } sp-core = { version = "2.0.0", default-features = false, path = "../core" } sp-std = { version = "2.0.0", default-features = false, path = "../std" } sp-io = { version = "2.0.0", default-features = false, path = "../io" } +sp-wasm-interface = { version = "2.0.0", default-features = false, path = "../wasm-interface" } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } [dev-dependencies] @@ -23,5 +25,6 @@ std = [ "sp-std/std", "codec/std", "sp-io/std", + "sp-wasm-interface/std", ] strict = [] diff --git a/primitives/sandbox/src/lib.rs b/primitives/sandbox/src/lib.rs index 86098e73af15be3d8b5dbf024bf363f338135366..e7cd684b458a4dd13ad6e46c8888213dabba702d 100755 --- a/primitives/sandbox/src/lib.rs +++ b/primitives/sandbox/src/lib.rs @@ -36,11 +36,11 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] use sp_std::prelude::*; -pub use sp_core::sandbox::{TypedValue, ReturnValue, HostError}; +pub use sp_core::sandbox::HostError; +pub use sp_wasm_interface::{Value, ReturnValue}; mod imp { #[cfg(feature = "std")] @@ -75,16 +75,13 @@ impl From for HostError { /// supervisor in [`EnvironmentDefinitionBuilder`]. /// /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html -pub type HostFuncType = fn(&mut T, &[TypedValue]) -> Result; +pub type HostFuncType = fn(&mut T, &[Value]) -> Result; /// Reference to a sandboxed linear memory, that /// will be used by the guest module. /// /// The memory can't be directly accessed by supervisor, but only -/// through designated functions [`get`] and [`set`]. -/// -/// [`get`]: #method.get -/// [`set`]: #method.set +/// through designated functions [`get`](Memory::get) and [`set`](Memory::set). #[derive(Clone)] pub struct Memory { inner: imp::Memory, @@ -200,9 +197,16 @@ impl Instance { pub fn invoke( &mut self, name: &str, - args: &[TypedValue], + args: &[Value], state: &mut T, ) -> Result { self.inner.invoke(name, args, state) } + + /// Get the value from a global with the given `name`. + /// + /// Returns `Some(_)` if the global could be found. + pub fn get_global_val(&self, name: &str) -> Option { + self.inner.get_global_val(name) + } } diff --git a/primitives/sandbox/with_std.rs b/primitives/sandbox/with_std.rs index 5f20342a5a9e482945872a33b7a6fb8c9453982d..cc3119003d11bb128b882230c187155e08d063f1 100755 --- a/primitives/sandbox/with_std.rs +++ b/primitives/sandbox/with_std.rs @@ -23,7 +23,7 @@ use wasmi::{ RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind }; use wasmi::memory_units::Pages; -use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError}; +use super::{Error, Value, ReturnValue, HostFuncType, HostError}; #[derive(Clone)] pub struct Memory { @@ -88,27 +88,7 @@ impl fmt::Display for DummyHostError { } } -impl wasmi::HostError for DummyHostError { -} - -fn from_runtime_value(v: RuntimeValue) -> TypedValue { - match v { - RuntimeValue::I32(v) => TypedValue::I32(v), - RuntimeValue::I64(v) => TypedValue::I64(v), - RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32), - RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64), - } -} - -fn to_runtime_value(v: TypedValue) -> RuntimeValue { - use wasmi::nan_preserving_float::{F32, F64}; - match v { - TypedValue::I32(v) => RuntimeValue::I32(v as i32), - TypedValue::I64(v) => RuntimeValue::I64(v as i64), - TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)), - TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)), - } -} +impl wasmi::HostError for DummyHostError {} struct GuestExternals<'a, T: 'a> { state: &'a mut T, @@ -124,13 +104,13 @@ impl<'a, T> Externals for GuestExternals<'a, T> { let args = args.as_ref() .iter() .cloned() - .map(from_runtime_value) + .map(Into::into) .collect::>(); let result = (self.defined_host_functions.funcs[index])(self.state, &args); match result { Ok(value) => Ok(match value { - ReturnValue::Value(v) => Some(to_runtime_value(v)), + ReturnValue::Value(v) => Some(v.into()), ReturnValue::Unit => None, }), Err(HostError) => Err(TrapKind::Host(Box::new(DummyHostError)).into()), @@ -253,7 +233,7 @@ impl ImportResolver for EnvironmentDefinitionBuilder { pub struct Instance { instance: ModuleRef, defined_host_functions: DefinedHostFunctions, - _marker: ::std::marker::PhantomData, + _marker: std::marker::PhantomData, } impl Instance { @@ -281,14 +261,14 @@ impl Instance { Ok(Instance { instance, defined_host_functions, - _marker: ::std::marker::PhantomData::, + _marker: std::marker::PhantomData::, }) } pub fn invoke( &mut self, name: &str, - args: &[TypedValue], + args: &[Value], state: &mut T, ) -> Result { let args = args.iter().cloned().map(Into::into).collect::>(); @@ -306,20 +286,29 @@ impl Instance { Err(_err) => Err(Error::Execution), } } + + pub fn get_global_val(&self, name: &str) -> Option { + let global = self.instance + .export_by_name(name)? + .as_global()? + .get(); + + Some(global.into()) + } } #[cfg(test)] mod tests { use wabt; - use crate::{Error, TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance}; + use crate::{Error, Value, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance}; use assert_matches::assert_matches; - fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result { + fn execute_sandboxed(code: &[u8], args: &[Value]) -> Result { struct State { counter: u32, } - fn env_assert(_e: &mut State, args: &[TypedValue]) -> Result { + fn env_assert(_e: &mut State, args: &[Value]) -> Result { if args.len() != 1 { return Err(HostError); } @@ -330,16 +319,16 @@ mod tests { Err(HostError) } } - fn env_inc_counter(e: &mut State, args: &[TypedValue]) -> Result { + fn env_inc_counter(e: &mut State, args: &[Value]) -> Result { if args.len() != 1 { return Err(HostError); } let inc_by = args[0].as_i32().ok_or_else(|| HostError)?; e.counter += inc_by as u32; - Ok(ReturnValue::Value(TypedValue::I32(e.counter as i32))) + Ok(ReturnValue::Value(Value::I32(e.counter as i32))) } /// Function that takes one argument of any type and returns that value. - fn env_polymorphic_id(_e: &mut State, args: &[TypedValue]) -> Result { + fn env_polymorphic_id(_e: &mut State, args: &[Value]) -> Result { if args.len() != 1 { return Err(HostError); } @@ -387,8 +376,8 @@ mod tests { let result = execute_sandboxed( &code, &[ - TypedValue::I32(0x12345678), - TypedValue::I64(0x1234567887654321), + Value::I32(0x12345678), + Value::I64(0x1234567887654321), ] ); assert!(result.is_ok()); @@ -410,10 +399,10 @@ mod tests { let return_val = execute_sandboxed( &code, &[ - TypedValue::I32(0x1336), + Value::I32(0x1336), ] ).unwrap(); - assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337))); + assert_eq!(return_val, ReturnValue::Value(Value::I32(0x1337))); } #[test] @@ -453,8 +442,8 @@ mod tests { #[test] fn cant_return_unmatching_type() { - fn env_returns_i32(_e: &mut (), _args: &[TypedValue]) -> Result { - Ok(ReturnValue::Value(TypedValue::I32(42))) + fn env_returns_i32(_e: &mut (), _args: &[Value]) -> Result { + Ok(ReturnValue::Value(Value::I32(42))) } let mut env_builder = EnvironmentDefinitionBuilder::new(); diff --git a/primitives/sandbox/without_std.rs b/primitives/sandbox/without_std.rs index 901354aee34ccccc0c55530b6a8392f540df4fc4..c31c6fe3494b942f479f6d5ffe17f8acdcc65a5b 100755 --- a/primitives/sandbox/without_std.rs +++ b/primitives/sandbox/without_std.rs @@ -18,7 +18,7 @@ use codec::{Decode, Encode}; use sp_core::sandbox as sandbox_primitives; use sp_io::sandbox; use sp_std::{prelude::*, slice, marker, mem, vec, rc::Rc}; -use super::{Error, TypedValue, ReturnValue, HostFuncType}; +use super::{Error, Value, ReturnValue, HostFuncType}; mod ffi { use sp_std::mem; @@ -183,7 +183,7 @@ extern "C" fn dispatch_thunk( slice::from_raw_parts(serialized_args_ptr, serialized_args_len) } }; - let args = Vec::::decode(&mut &serialized_args[..]).expect( + let args = Vec::::decode(&mut &serialized_args[..]).expect( "serialized args should be provided by the runtime; correctly serialized data should be deserializable; qed", @@ -244,11 +244,11 @@ impl Instance { pub fn invoke( &mut self, name: &str, - args: &[TypedValue], + args: &[Value], state: &mut T, ) -> Result { let serialized_args = args.to_vec().encode(); - let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE]; + let mut return_val = vec![0u8; ReturnValue::ENCODED_MAX_SIZE]; let result = sandbox::invoke( self.instance_idx, @@ -261,7 +261,7 @@ impl Instance { match result { sandbox_primitives::ERR_OK => { - let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..]) + let return_val = ReturnValue::decode(&mut &return_val[..]) .map_err(|_| Error::Execution)?; Ok(return_val) } @@ -269,6 +269,10 @@ impl Instance { _ => unreachable!(), } } + + pub fn get_global_val(&self, name: &str) -> Option { + sandbox::get_global_val(self.instance_idx, name) + } } impl Drop for Instance { diff --git a/primitives/serializer/Cargo.toml b/primitives/serializer/Cargo.toml index 39fdeb0e7ee3c03329e59380f88bcba96386517b..b9e78d968c66c2d538e45cb0a6bb93255398a372 100644 --- a/primitives/serializer/Cargo.toml +++ b/primitives/serializer/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-serializer" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] serde = "1.0.101" diff --git a/primitives/session/Cargo.toml b/primitives/session/Cargo.toml index b7c72e0c681fec52e161a92048cbb8a2a966ae15..143ff87942fd1b47cbe4b365b7ce7f69277d00a7 100644 --- a/primitives/session/Cargo.toml +++ b/primitives/session/Cargo.toml @@ -3,12 +3,14 @@ name = "sp-session" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-api = { version = "2.0.0", default-features = false, path = "../api" } sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } sp-runtime = { version = "2.0.0", optional = true, path = "../runtime" } [features] default = [ "std" ] -std = [ "sp-api/std", "sp-std/std", "sp-runtime" ] +std = [ "sp-api/std", "sp-std/std", "sp-runtime", "sp-core/std" ] diff --git a/primitives/session/src/lib.rs b/primitives/session/src/lib.rs index c61218ca6bacd1afb2c9714a650a94c9e9d05b72..8e2a68d0509832e855226e904f9e872f343d17d4 100644 --- a/primitives/session/src/lib.rs +++ b/primitives/session/src/lib.rs @@ -25,6 +25,8 @@ use sp_runtime::{generic::BlockId, traits::Block as BlockT}; #[cfg(feature = "std")] use sp_api::ProvideRuntimeApi; +use sp_core::crypto::KeyTypeId; + sp_api::decl_runtime_apis! { /// Session keys runtime api. pub trait SessionKeys { @@ -36,6 +38,11 @@ sp_api::decl_runtime_apis! { /// /// Returns the concatenated SCALE encoded public keys. fn generate_session_keys(seed: Option>) -> Vec; + + /// Decode the given public session keys. + /// + /// Returns the list of public raw public keys + key type. + fn decode_session_keys(encoded: Vec) -> Option, KeyTypeId)>>; } } diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index 35725c72e334baeaca061819338873d3e11a1922..97afa0d0a76109549b62cd9f8f57ae85d6e8377f 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-staking" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index d390471aca2d6299c2df92892e69aaf81f3bbadf..2408cce099b24f47fd168e6bb5e899b6964ccc4d 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -4,13 +4,14 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Substrate State Machine" edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4.8" -parking_lot = "0.9.0" +parking_lot = "0.10.0" hash-db = "0.15.2" -trie-db = "0.19.2" -trie-root = "0.15.2" +trie-db = "0.20.0" +trie-root = "0.16.0" sp-trie = { version = "2.0.0", path = "../trie" } sp-core = { version = "2.0.0", path = "../core" } sp-panic-handler = { version = "2.0.0", path = "../panic-handler" } diff --git a/primitives/state-machine/src/backend.rs b/primitives/state-machine/src/backend.rs index 4ef9b970ae21ddbc200098a6eda7ec423a29077e..396ef6575af075c5926660249a6908b26a246e5b 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -213,6 +213,16 @@ pub trait Backend: std::fmt::Debug { fn usage_info(&self) -> UsageInfo { UsageInfo::empty() } + + /// Wipe the state database. + fn wipe(&self) -> Result<(), Self::Error> { + unimplemented!() + } + + /// Commit given transaction to storage. + fn commit(&self, _storage_root: H::Out, _transaction: Self::Transaction) -> Result<(), Self::Error> { + unimplemented!() + } } impl<'a, T: Backend, H: Hasher> Backend for &'a T { diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index f293ae9f51615c602dfdc5f21ac58e47b99914a5..53156cb18619c667a66654dddb7c4c194b573848 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -231,7 +231,7 @@ where fn child_storage_hash( &self, storage_key: ChildStorageKey, - _child_info: ChildInfo, + child_info: ChildInfo, key: &[u8], ) -> Option> { let _guard = sp_panic_handler::AbortGuard::force_abort(); @@ -239,7 +239,8 @@ where .child_storage(storage_key.as_ref(), key) .map(|x| x.map(|x| H::hash(x))) .unwrap_or_else(|| - self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL) + self.backend.child_storage_hash(storage_key.as_ref(), child_info, key) + .expect(EXT_NOT_ALLOWED_TO_FAIL) ); trace!(target: "state-trace", "{:04x}: ChildHash({}) {}={:?}", @@ -582,6 +583,25 @@ where root.map(|r| r.map(|o| o.encode())) } + + fn wipe(&mut self) { + self.overlay.discard_prospective(); + self.overlay.drain_storage_changes(&self.backend, None, Default::default(), self.storage_transaction_cache) + .expect(EXT_NOT_ALLOWED_TO_FAIL); + self.storage_transaction_cache.reset(); + self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL) + } + + fn commit(&mut self) { + self.overlay.commit_prospective(); + let changes = self.overlay.drain_storage_changes(&self.backend, None, Default::default(), self.storage_transaction_cache) + .expect(EXT_NOT_ALLOWED_TO_FAIL); + self.backend.commit( + changes.transaction_storage_root, + changes.transaction, + ).expect(EXT_NOT_ALLOWED_TO_FAIL); + self.storage_transaction_cache.reset(); + } } impl<'a, H, B, N> sp_externalities::ExtensionStore for Ext<'a, H, N, B> @@ -613,6 +633,12 @@ mod tests { type TestBackend = InMemoryBackend; type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend>; + const CHILD_KEY_1: &[u8] = b":child_storage:default:Child1"; + + const CHILD_UUID_1: &[u8] = b"unique_id_1"; + const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(CHILD_UUID_1); + + fn prepare_overlay_with_changes() -> OverlayedChanges { OverlayedChanges { prospective: vec![ @@ -769,4 +795,50 @@ mod tests { // next_overlay exist but next_backend doesn't exist assert_eq!(ext.next_child_storage_key(child(), CHILD_INFO_1, &[40]), Some(vec![50])); } + + #[test] + fn child_storage_works() { + let mut cache = StorageTransactionCache::default(); + let child = || ChildStorageKey::from_slice(CHILD_KEY_1).unwrap(); + let mut overlay = OverlayedChanges::default(); + overlay.set_child_storage(child().as_ref().to_vec(), CHILD_INFO_1, vec![20], None); + overlay.set_child_storage(child().as_ref().to_vec(), CHILD_INFO_1, vec![30], Some(vec![31])); + let backend = Storage { + top: map![], + children: map![ + child().as_ref().to_vec() => StorageChild { + data: map![ + vec![10] => vec![10], + vec![20] => vec![20], + vec![30] => vec![40] + ], + child_info: CHILD_INFO_1.to_owned(), + } + ], + }.into(); + + let ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None); + + assert_eq!(ext.child_storage(child(), CHILD_INFO_1, &[10]), Some(vec![10])); + assert_eq!(ext.original_child_storage(child(), CHILD_INFO_1, &[10]), Some(vec![10])); + assert_eq!( + ext.child_storage_hash(child(), CHILD_INFO_1, &[10]), + Some(Blake2Hasher::hash(&[10]).as_ref().to_vec()), + ); + + assert_eq!(ext.child_storage(child(), CHILD_INFO_1, &[20]), None); + assert_eq!(ext.original_child_storage(child(), CHILD_INFO_1, &[20]), Some(vec![20])); + assert_eq!( + ext.child_storage_hash(child(), CHILD_INFO_1, &[20]), + None, + ); + + assert_eq!(ext.child_storage(child(), CHILD_INFO_1, &[30]), Some(vec![31])); + assert_eq!(ext.original_child_storage(child(), CHILD_INFO_1, &[30]), Some(vec![40])); + assert_eq!( + ext.child_storage_hash(child(), CHILD_INFO_1, &[30]), + Some(Blake2Hasher::hash(&[31]).as_ref().to_vec()), + ); + + } } diff --git a/primitives/state-machine/src/overlayed_changes.rs b/primitives/state-machine/src/overlayed_changes.rs index ed6f30a4f596bfbe705951d2761abb289f5ebab1..37187e163fe1c4775bbe895729b10f45b385296b 100644 --- a/primitives/state-machine/src/overlayed_changes.rs +++ b/primitives/state-machine/src/overlayed_changes.rs @@ -428,15 +428,18 @@ impl OverlayedChanges { /// /// Panics: /// Will panic if there are any uncommitted prospective changes. - pub fn into_committed(self) -> ( + fn drain_committed(&mut self) -> ( impl Iterator)>, impl Iterator)>, OwnedChildInfo))>, - ){ + ) { assert!(self.prospective.is_empty()); ( - self.committed.top.into_iter().map(|(k, v)| (k, v.value)), - self.committed.children.into_iter() - .map(|(sk, (v, ci))| (sk, (v.into_iter().map(|(k, v)| (k, v.value)), ci))) + std::mem::replace(&mut self.committed.top, Default::default()) + .into_iter() + .map(|(k, v)| (k, v.value)), + std::mem::replace(&mut self.committed.children, Default::default()) + .into_iter() + .map(|(sk, (v, ci))| (sk, (v.into_iter().map(|(k, v)| (k, v.value)), ci))), ) } @@ -444,11 +447,22 @@ impl OverlayedChanges { pub fn into_storage_changes< B: Backend, H: Hasher, N: BlockNumber >( - self, + mut self, backend: &B, changes_trie_state: Option<&ChangesTrieState>, parent_hash: H::Out, mut cache: StorageTransactionCache, + ) -> Result, String> where H::Out: Ord + Encode + 'static { + self.drain_storage_changes(backend, changes_trie_state, parent_hash, &mut cache) + } + + /// Drain all changes into a [`StorageChanges`] instance. Leave empty overlay in place. + pub fn drain_storage_changes, H: Hasher, N: BlockNumber>( + &mut self, + backend: &B, + changes_trie_state: Option<&ChangesTrieState>, + parent_hash: H::Out, + mut cache: &mut StorageTransactionCache, ) -> Result, String> where H::Out: Ord + Encode + 'static { // If the transaction does not exist, we generate it. if cache.transaction.is_none() { @@ -474,7 +488,7 @@ impl OverlayedChanges { .take() .expect("Changes trie transaction was generated by `changes_trie_root`; qed"); - let (main_storage_changes, child_storage_changes) = self.into_committed(); + let (main_storage_changes, child_storage_changes) = self.drain_committed(); Ok(StorageChanges { main_storage_changes: main_storage_changes.collect(), diff --git a/primitives/state-machine/src/stats.rs b/primitives/state-machine/src/stats.rs index 38286be545af5804e4dd9c6696949197bdccf438..aa69b5be9d652846f2c78ae1638bbe45aa08b877 100644 --- a/primitives/state-machine/src/stats.rs +++ b/primitives/state-machine/src/stats.rs @@ -21,42 +21,42 @@ use std::time::{Instant, Duration}; /// Measured count of operations and total bytes. #[derive(Clone, Debug, Default)] pub struct UsageUnit { - /// Number of operations. - pub ops: u64, - /// Number of bytes. - pub bytes: u64, + /// Number of operations. + pub ops: u64, + /// Number of bytes. + pub bytes: u64, } /// Usage statistics for state backend. #[derive(Clone, Debug)] pub struct UsageInfo { - /// Read statistics (total). - pub reads: UsageUnit, - /// Write statistics. - pub writes: UsageUnit, - /// Cache read statistics. - pub cache_reads: UsageUnit, - /// Memory used. - pub memory: usize, - - /// Moment at which current statistics has been started being collected. - pub started: Instant, - /// Timespan of the statistics. - pub span: Duration, + /// Read statistics (total). + pub reads: UsageUnit, + /// Write statistics. + pub writes: UsageUnit, + /// Cache read statistics. + pub cache_reads: UsageUnit, + /// Memory used. + pub memory: usize, + + /// Moment at which current statistics has been started being collected. + pub started: Instant, + /// Timespan of the statistics. + pub span: Duration, } impl UsageInfo { - /// Empty statistics. - /// - /// Means no data was collected. - pub fn empty() -> Self { - Self { - reads: UsageUnit::default(), - writes: UsageUnit::default(), - cache_reads: UsageUnit::default(), - memory: 0, - started: Instant::now(), - span: Default::default(), - } - } -} \ No newline at end of file + /// Empty statistics. + /// + /// Means no data was collected. + pub fn empty() -> Self { + Self { + reads: UsageUnit::default(), + writes: UsageUnit::default(), + cache_reads: UsageUnit::default(), + memory: 0, + started: Instant::now(), + span: Default::default(), + } + } +} diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index 2598682ae0668d4dd65afe8bb13d90bc854e36d9..125a823f57fc1c505118b4ccde480ec017fd1129 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -23,7 +23,7 @@ use log::{debug, warn}; use hash_db::{self, Hasher, EMPTY_PREFIX, Prefix}; use sp_trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue, default_child_trie_root, read_trie_value, read_child_trie_value, - for_keys_in_child_trie, KeySpacedDB}; + for_keys_in_child_trie, KeySpacedDB, TrieDBIterator}; use sp_trie::trie_types::{TrieDB, TrieError, Layout}; use crate::{backend::Consolidate, StorageKey, StorageValue}; use sp_core::storage::ChildInfo; @@ -252,16 +252,11 @@ impl, H: Hasher> TrieBackendEssence where H::Out: let mut iter = move |db| -> Result<(), Box>> { let trie = TrieDB::::new(db, root)?; - let mut iter = trie.iter()?; - iter.seek(prefix)?; - - for x in iter { + for x in TrieDBIterator::new_prefixed(&trie, prefix)? { let (key, value) = x?; - if !key.starts_with(prefix) { - break; - } + debug_assert!(key.starts_with(prefix)); f(&key, &value); } diff --git a/primitives/std/Cargo.toml b/primitives/std/Cargo.toml index 7fdf7d1144b7b108208c2f6caaeb62720995e149..dd4b7e4511f3369a6faf71552fc2281c51c77ff5 100644 --- a/primitives/std/Cargo.toml +++ b/primitives/std/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-std" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [features] default = ["std"] diff --git a/primitives/std/src/lib.rs b/primitives/std/src/lib.rs index 3fcf5daeb4b0007f1b22b9720ac5b7bb9f800ce5..856b09540355c922214ea28dc511ceac1f9b9529 100644 --- a/primitives/std/src/lib.rs +++ b/primitives/std/src/lib.rs @@ -18,7 +18,7 @@ //! or client/alloc to be used with any code that depends on the runtime. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] + #![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] diff --git a/primitives/std/without_std.rs b/primitives/std/without_std.rs index ad587531ddc976bc0af596fe91780b4feaf634d7..4424ca0e7de681b3b99b9dd0238deb6b19e408bd 100755 --- a/primitives/std/without_std.rs +++ b/primitives/std/without_std.rs @@ -27,7 +27,6 @@ pub use core::convert; pub use core::default; pub use core::fmt; pub use core::hash; -pub use core::intrinsics; pub use core::iter; pub use core::marker; pub use core::mem; diff --git a/primitives/storage/Cargo.toml b/primitives/storage/Cargo.toml index 384519cc1d69d99502b6e0cd115c85ccfe5ea8c7..c9fda1816b55e52d73885ceda1c1aaec0b422f9d 100644 --- a/primitives/storage/Cargo.toml +++ b/primitives/storage/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" description = "Storage related primitives" +license = "GPL-3.0" [dependencies] sp-std = { version = "2.0.0", default-features = false, path = "../std" } diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index 5c2f2dcc0a68e70c1ecdb5e8c8c24bf4c65199de..2edd5f05751d9f142e0613d4b9fca7b110f0ba17 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-test-primitives" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } @@ -10,6 +11,7 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = sp-core = { version = "2.0.0", default-features = false, path = "../core" } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [features] default = [ diff --git a/primitives/test-primitives/src/lib.rs b/primitives/test-primitives/src/lib.rs index 74115bdb8f46b52c2488444847dd70ac9443dfbf..302b24fcc1e3e7ed5a6d2c8a858cf86e36d63f18 100644 --- a/primitives/test-primitives/src/lib.rs +++ b/primitives/test-primitives/src/lib.rs @@ -28,6 +28,7 @@ use sp_runtime::traits::{BlakeTwo256, Verify, Extrinsic as ExtrinsicT,}; /// Extrinsic for test-runtime. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(parity_util_mem::MallocSizeOf))] pub enum Extrinsic { IncludeData(Vec), StorageChange(Vec, Option>), diff --git a/primitives/timestamp/Cargo.toml b/primitives/timestamp/Cargo.toml index ee86d6e3bc97f02813e71c4d3ce0533c0ad35f0f..815aaf5305fb536f3333c89214795c4dccded3f7 100644 --- a/primitives/timestamp/Cargo.toml +++ b/primitives/timestamp/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-timestamp" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sp-api = { version = "2.0.0", default-features = false, path = "../api" } @@ -11,6 +12,7 @@ sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } impl-trait-for-tuples = "0.1.3" +wasm-timer = "0.2" [features] default = [ "std" ] diff --git a/primitives/timestamp/src/lib.rs b/primitives/timestamp/src/lib.rs index 50b871d73ea6b8d710163840f897a60d05dc65e2..979b98b4952c92eaf3ad9b7f73d47807ada1d547 100644 --- a/primitives/timestamp/src/lib.rs +++ b/primitives/timestamp/src/lib.rs @@ -90,7 +90,7 @@ impl ProvideInherentData for InherentDataProvider { &self, inherent_data: &mut InherentData, ) -> Result<(), sp_inherents::Error> { - use std::time::SystemTime; + use wasm_timer::SystemTime; let now = SystemTime::now(); now.duration_since(SystemTime::UNIX_EPOCH) diff --git a/primitives/transaction-pool/Cargo.toml b/primitives/transaction-pool/Cargo.toml index 3a3e15e611af11f80cf669175bfe5a9e51e78831..18b254f2c093b9a63fda2284629a8f99ecf76f5a 100644 --- a/primitives/transaction-pool/Cargo.toml +++ b/primitives/transaction-pool/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-transaction-pool" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", optional = true } diff --git a/primitives/transaction-pool/src/pool.rs b/primitives/transaction-pool/src/pool.rs index e67a9890755d74a1f3f9afcbe7fe88df51ffe4b7..d4e6a5caf2cbacee9cf48550be5237d09652da11 100644 --- a/primitives/transaction-pool/src/pool.rs +++ b/primitives/transaction-pool/src/pool.rs @@ -20,6 +20,7 @@ use std::{ collections::HashMap, hash::Hash, sync::Arc, + pin::Pin, }; use futures::{ Future, Stream, @@ -112,7 +113,7 @@ pub enum TransactionStatus { pub type TransactionStatusStream = dyn Stream> + Send + Unpin; /// The import notification event stream. -pub type ImportNotificationStream = mpsc::UnboundedReceiver<()>; +pub type ImportNotificationStream = mpsc::UnboundedReceiver; /// Transaction hash type for a pool. pub type TxHash

=

::Hash; @@ -123,6 +124,9 @@ pub type TransactionFor

= <

::Block as BlockT>::Extrinsi /// Type of transactions event stream for a pool. pub type TransactionStatusStreamFor

= TransactionStatusStream, BlockHash

>; +/// Typical future type used in transaction pool api. +pub type PoolFuture = std::pin::Pin> + Send>>; + /// In-pool transaction interface. /// /// The pool is container of transactions that are implementing this trait. @@ -163,66 +167,61 @@ pub trait TransactionPool: Send + Sync { /// Error type. type Error: From + crate::error::IntoPoolError; - // Networking + // *** RPC /// Returns a future that imports a bunch of unverified transactions to the pool. fn submit_at( &self, at: &BlockId, - xts: impl IntoIterator> + 'static, - ) -> Box, Self::Error>>, - Self::Error - >> + Send + Unpin>; + xts: Vec>, + ) -> PoolFuture, Self::Error>>, Self::Error>; /// Returns a future that imports one unverified transaction to the pool. fn submit_one( &self, at: &BlockId, xt: TransactionFor, - ) -> Box, - Self::Error - >> + Send + Unpin>; - - // RPC + ) -> PoolFuture, Self::Error>; /// Returns a future that import a single transaction and starts to watch their progress in the pool. fn submit_and_watch( &self, at: &BlockId, xt: TransactionFor, - ) -> Box>, Self::Error>> + Send + Unpin>; - - - // Block production / Networking + ) -> PoolFuture>, Self::Error>; + // *** Block production / Networking /// Get an iterator for ready transactions ordered by priority fn ready(&self) -> Box>>; - - // Block production - + // *** Block production /// Remove transactions identified by given hashes (and dependent transactions) from the pool. fn remove_invalid(&self, hashes: &[TxHash]) -> Vec>; - // logging - + // *** logging /// Returns pool status. fn status(&self) -> PoolStatus; - // logging / RPC / networking - + // *** logging / RPC / networking /// Return an event stream of transactions imported to the pool. - fn import_notification_stream(&self) -> ImportNotificationStream; - - // networking + fn import_notification_stream(&self) -> ImportNotificationStream>; + // *** networking /// Notify the pool about transactions broadcast. fn on_broadcasted(&self, propagations: HashMap, Vec>); /// Returns transaction hash fn hash_of(&self, xt: &TransactionFor) -> TxHash; + + /// Return specific ready transaction by hash, if there is one. + fn ready_transaction(&self, hash: &TxHash) -> Option>; +} + +/// Trait for transaction pool maintenance. +pub trait MaintainedTransactionPool : TransactionPool { + /// Perform maintenance + fn maintain(&self, block: &BlockId, retracted: &[BlockHash]) + -> Pin + Send>>; } /// An abstraction for transaction pool. @@ -265,108 +264,3 @@ impl OffchainSubmitTransaction for TPool { )) } } - -/// Transaction pool maintainer interface. -pub trait TransactionPoolMaintainer: Send + Sync { - /// Block type. - type Block: BlockT; - /// Transaction Hash type. - type Hash: Hash + Eq + Member + Serialize; - - /// Returns a future that performs maintenance procedures on the pool when - /// with given hash is imported. - fn maintain( - &self, - id: &BlockId, - retracted: &[Self::Hash], - ) -> Box + Send + Unpin>; -} - -/// Maintainable pool implementation. -pub struct MaintainableTransactionPool { - pool: Pool, - maintainer: Maintainer, -} - -impl MaintainableTransactionPool { - /// Create new maintainable pool using underlying pool and maintainer. - pub fn new(pool: Pool, maintainer: Maintainer) -> Self { - MaintainableTransactionPool { pool, maintainer } - } -} - -impl TransactionPool for MaintainableTransactionPool - where - Pool: TransactionPool, - Maintainer: Send + Sync, -{ - type Block = Pool::Block; - type Hash = Pool::Hash; - type InPoolTransaction = Pool::InPoolTransaction; - type Error = Pool::Error; - - fn submit_at( - &self, - at: &BlockId, - xts: impl IntoIterator> + 'static, - ) -> Box, Self::Error>>, Self::Error>> + Send + Unpin> { - self.pool.submit_at(at, xts) - } - - fn submit_one( - &self, - at: &BlockId, - xt: TransactionFor, - ) -> Box, Self::Error>> + Send + Unpin> { - self.pool.submit_one(at, xt) - } - - fn submit_and_watch( - &self, - at: &BlockId, - xt: TransactionFor, - ) -> Box>, Self::Error>> + Send + Unpin> { - self.pool.submit_and_watch(at, xt) - } - - fn remove_invalid(&self, hashes: &[TxHash]) -> Vec> { - self.pool.remove_invalid(hashes) - } - - fn status(&self) -> PoolStatus { - self.pool.status() - } - - fn ready(&self) -> Box>> { - self.pool.ready() - } - - fn import_notification_stream(&self) -> ImportNotificationStream { - self.pool.import_notification_stream() - } - - fn hash_of(&self, xt: &TransactionFor) -> TxHash { - self.pool.hash_of(xt) - } - - fn on_broadcasted(&self, propagations: HashMap, Vec>) { - self.pool.on_broadcasted(propagations) - } -} - -impl TransactionPoolMaintainer for MaintainableTransactionPool - where - Pool: Send + Sync, - Maintainer: TransactionPoolMaintainer -{ - type Block = Maintainer::Block; - type Hash = Maintainer::Hash; - - fn maintain( - &self, - id: &BlockId, - retracted: &[Self::Hash], - ) -> Box + Send + Unpin> { - self.maintainer.maintain(id, retracted) - } -} diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index a78a26db736c480c5c2ae50014eb4183d569840c..361ddb186cca7077bc423bfd2c9c0759da885ecb 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -15,13 +15,13 @@ harness = false codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } sp-std = { version = "2.0.0", default-features = false, path = "../std" } hash-db = { version = "0.15.2", default-features = false } -trie-db = { version = "0.19.2", default-features = false } -trie-root = { version = "0.15.2", default-features = false } -memory-db = { version = "0.18.1", default-features = false } +trie-db = { version = "0.20.0", default-features = false } +trie-root = { version = "0.16.0", default-features = false } +memory-db = { version = "0.19.0", default-features = false } sp-core = { version = "2.0.0", default-features = false, path = "../core" } [dev-dependencies] -trie-bench = "0.19.0" +trie-bench = "0.20.0" trie-standardmap = "0.15.2" criterion = "0.2.11" hex-literal = "0.2.1" diff --git a/primitives/trie/src/lib.rs b/primitives/trie/src/lib.rs index c71d3fb84ce79f61bbea020426d86663470630d5..0cf268856bb459019ef8d610e4336fb16e1cf1c7 100644 --- a/primitives/trie/src/lib.rs +++ b/primitives/trie/src/lib.rs @@ -37,7 +37,7 @@ pub use trie_stream::TrieStream; pub use node_codec::NodeCodec; /// Various re-exports from the `trie-db` crate. pub use trie_db::{ - Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, + Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, TrieDBIterator, }; /// Various re-exports from the `memory-db` crate. pub use memory_db::KeyFunction; diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index 784051b56e5e1d01d685543ad7eccc0c93b85980..1219d9840c30e772907016fccd26e647a9286140 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -3,6 +3,7 @@ name = "sp-version" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] impl-serde = { version = "0.2.3", optional = true } diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 8f44c8895cfa7a45d98e57cec2ba92effdb04e13..bb9ea85872a87761b027d2b50f9ee159b45ef680 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -3,7 +3,14 @@ name = "sp-wasm-interface" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] -wasmi = "0.6.2" +wasmi = { version = "0.6.2", optional = true } impl-trait-for-tuples = "0.1.2" +sp-std = { version = "2.0.0", path = "../std", default-features = false } +codec = { package = "parity-scale-codec", version = "1.1.2", default-features = false, features = ["derive"] } + +[features] +default = [ "std" ] +std = [ "wasmi", "sp-std/std", "codec/std" ] diff --git a/primitives/wasm-interface/src/lib.rs b/primitives/wasm-interface/src/lib.rs index b0bbbf82db3c11c0c589ae5b152d824ae32f25ef..7bb4469c771ffb0066677fa2c2bda3a719de244b 100644 --- a/primitives/wasm-interface/src/lib.rs +++ b/primitives/wasm-interface/src/lib.rs @@ -16,12 +16,20 @@ //! Types and traits for interfacing between the host and the wasm runtime. -use std::{borrow::Cow, marker::PhantomData, mem, iter::Iterator, result}; +#![cfg_attr(not(feature = "std"), no_std)] +use sp_std::{ + borrow::Cow, marker::PhantomData, mem, iter::Iterator, result, vec::Vec, +}; + +#[cfg(feature = "std")] mod wasmi_impl; /// Result type used by traits in this crate. +#[cfg(feature = "std")] pub type Result = result::Result; +#[cfg(not(feature = "std"))] +pub type Result = result::Result; /// Value types supported by Substrate on the boundary between host/Wasm. #[derive(Copy, Clone, PartialEq, Debug, Eq)] @@ -36,8 +44,33 @@ pub enum ValueType { F64, } +impl From for u8 { + fn from(val: ValueType) -> u8 { + match val { + ValueType::I32 => 0, + ValueType::I64 => 1, + ValueType::F32 => 2, + ValueType::F64 => 3, + } + } +} + +impl sp_std::convert::TryFrom for ValueType { + type Error = (); + + fn try_from(val: u8) -> sp_std::result::Result { + match val { + 0 => Ok(Self::I32), + 1 => Ok(Self::I64), + 2 => Ok(Self::F32), + 3 => Ok(Self::F64), + _ => Err(()), + } + } +} + /// Values supported by Substrate on the boundary between host/Wasm. -#[derive(PartialEq, Debug, Clone, Copy)] +#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)] pub enum Value { /// A 32-bit integer. I32(i32), @@ -63,6 +96,14 @@ impl Value { Value::F64(_) => ValueType::F64, } } + + /// Return `Self` as `i32`. + pub fn as_i32(&self) -> Option { + match self { + Self::I32(val) => Some(*val), + _ => None, + } + } } /// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate. @@ -189,11 +230,22 @@ impl Signature { return_value: None, } } - } +/// A trait that requires `RefUnwindSafe` when `feature = std`. +#[cfg(feature = "std")] +pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {} +#[cfg(feature = "std")] +impl MaybeRefUnwindSafe for T {} + +/// A trait that requires `RefUnwindSafe` when `feature = std`. +#[cfg(not(feature = "std"))] +pub trait MaybeRefUnwindSafe {} +#[cfg(not(feature = "std"))] +impl MaybeRefUnwindSafe for T {} + /// Something that provides a function implementation on the host for a wasm function. -pub trait Function: std::panic::RefUnwindSafe + Send + Sync { +pub trait Function: MaybeRefUnwindSafe + Send + Sync { /// Returns the name of this function. fn name(&self) -> &str; /// Returns the signature of this function. @@ -279,6 +331,12 @@ pub trait Sandbox { raw_env_def: &[u8], state: u32, ) -> Result; + + /// Get the value from a global with the given `name`. The sandbox is determined by the + /// given `instance_idx` instance. + /// + /// Returns `Some(_)` when the requested global variable could be found. + fn get_global_val(&self, instance_idx: u32, name: &str) -> Result>; } /// Something that provides implementations for host functions. @@ -390,9 +448,37 @@ impl ReadPrimitive for &mut dyn FunctionContext { } } +/// Typed value that can be returned from a function. +/// +/// Basically a `TypedValue` plus `Unit`, for functions which return nothing. +#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)] +pub enum ReturnValue { + /// For returning nothing. + Unit, + /// For returning some concrete value. + Value(Value), +} + +impl From for ReturnValue { + fn from(v: Value) -> ReturnValue { + ReturnValue::Value(v) + } +} + +impl ReturnValue { + /// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`. + /// + /// Breakdown: + /// 1 byte for encoding unit/value variant + /// 1 byte for encoding value type + /// 8 bytes for encoding the biggest value types available in wasm: f64, i64. + pub const ENCODED_MAX_SIZE: usize = 10; +} + #[cfg(test)] mod tests { use super::*; + use codec::Encode; #[test] fn pointer_offset_works() { @@ -406,4 +492,11 @@ mod tests { assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80)); assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256)); } + + + #[test] + fn return_value_encoded_max_size() { + let encoded = ReturnValue::Value(Value::I64(-1)).encode(); + assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE); + } } diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index ac4c9954192dc99f9aa7caa6586874acd5856927..0a552973c9aeea3fed1ec6fbb1e6a4075268fe3d 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -3,3 +3,4 @@ name = "substrate-test-utils" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" diff --git a/test-utils/client/Cargo.toml b/test-utils/client/Cargo.toml index 8b1ebc39f4724a4d3f8feedc000544c5f767029c..2107f3bdd8b4c81a6f03c61f0b700176390c78db 100644 --- a/test-utils/client/Cargo.toml +++ b/test-utils/client/Cargo.toml @@ -3,6 +3,7 @@ name = "substrate-test-client" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-client-api = { version = "2.0.0", path = "../../client/api" } diff --git a/test-utils/client/src/client_ext.rs b/test-utils/client/src/client_ext.rs index aa7383e3ab367fc3560b464236ed8be2857ddc9a..e29ee787d030c6ac2e7afe0700378083e10b1a47 100644 --- a/test-utils/client/src/client_ext.rs +++ b/test-utils/client/src/client_ext.rs @@ -96,7 +96,8 @@ impl ClientBlockImportExt for std::sync::A storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }; @@ -115,7 +116,8 @@ impl ClientBlockImportExt for std::sync::A storage_changes: None, finalized: false, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::Custom(true), + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::Custom(true)), allow_missing_state: false, import_existing: false, }; @@ -134,7 +136,8 @@ impl ClientBlockImportExt for std::sync::A storage_changes: None, finalized: true, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::Custom(true), + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::Custom(true)), allow_missing_state: false, import_existing: false, }; @@ -158,7 +161,8 @@ impl ClientBlockImportExt for std::sync::A storage_changes: None, finalized: true, auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, + intermediates: Default::default(), + fork_choice: Some(ForkChoiceStrategy::LongestChain), allow_missing_state: false, import_existing: false, }; @@ -182,7 +186,8 @@ impl ClientBlockImportExt for Client ClientBlockImportExt for Client ClientBlockImportExt for Client ClientBlockImportExt for Client { +pub struct TestClientBuilder { execution_strategies: ExecutionStrategies, genesis_init: G, child_storage_extension: HashMap, StorageChild>, backend: Arc, _executor: std::marker::PhantomData, keystore: Option, + fork_blocks: ForkBlocks, + bad_blocks: BadBlocks, } impl Default - for TestClientBuilder, G> { + for TestClientBuilder, G> { fn default() -> Self { Self::with_default_backend() } } -impl TestClientBuilder, G> { +impl TestClientBuilder, G> { /// Create new `TestClientBuilder` with default backend. pub fn with_default_backend() -> Self { let backend = Arc::new(Backend::new_test(std::u32::MAX, std::u64::MAX)); @@ -91,7 +95,7 @@ impl TestClientBuilder TestClientBuilder { +impl TestClientBuilder { /// Create a new instance of the test client builder. pub fn with_backend(backend: Arc) -> Self { TestClientBuilder { @@ -101,6 +105,8 @@ impl TestClientBuilder genesis_init: Default::default(), _executor: Default::default(), keystore: None, + fork_blocks: None, + bad_blocks: None, } } @@ -152,8 +158,18 @@ impl TestClientBuilder self } + /// Sets custom block rules. + pub fn set_block_rules(mut self, + fork_blocks: ForkBlocks, + bad_blocks: BadBlocks, + ) -> Self { + self.fork_blocks = fork_blocks; + self.bad_blocks = bad_blocks; + self + } + /// Build the test client with the given native executor. - pub fn build_with_executor( + pub fn build_with_executor( self, executor: Executor, ) -> ( @@ -167,9 +183,7 @@ impl TestClientBuilder ) where Executor: sc_client::CallExecutor + 'static, Backend: sc_client_api::backend::Backend, - Block: BlockT, { - let storage = { let mut storage = self.genesis_init.genesis_storage(); @@ -191,8 +205,8 @@ impl TestClientBuilder self.backend.clone(), executor, &storage, - Default::default(), - Default::default(), + self.fork_blocks, + self.bad_blocks, ExecutionExtensions::new( self.execution_strategies, self.keystore.clone(), @@ -205,13 +219,14 @@ impl TestClientBuilder } } -impl TestClientBuilder< +impl TestClientBuilder< + Block, sc_client::LocalCallExecutor>, Backend, G, > { /// Build the test client with the given native executor. - pub fn build_with_native_executor( + pub fn build_with_native_executor( self, executor: I, ) -> ( @@ -226,7 +241,6 @@ impl TestClientBuilder< I: Into>>, E: sc_executor::NativeExecutionDispatch + 'static, Backend: sc_client_api::backend::Backend + 'static, - Block: BlockT, { let executor = executor.into().unwrap_or_else(|| NativeExecutor::new(WasmExecutionMethod::Interpreted, None) diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index b827163a1b0f757687578252b3627ef8b0bf7dcf..01244d8c6fb7f6bbacff428ad10aade243861f0e 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -4,6 +4,7 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" +license = "GPL-3.0" [dependencies] sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } @@ -16,7 +17,7 @@ frame-executive = { version = "2.0.0", default-features = false, path = "../../f sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } log = { version = "0.4.8", optional = true } -memory-db = { version = "0.18.0", default-features = false } +memory-db = { version = "0.19.0", default-features = false } sp-offchain = { path = "../../primitives/offchain", default-features = false} sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } @@ -35,7 +36,8 @@ pallet-timestamp = { version = "2.0.0", default-features = false, path = "../../ sc-client = { version = "0.8", optional = true, path = "../../client" } sp-trie = { version = "2.0.0", default-features = false, path = "../../primitives/trie" } sp-transaction-pool = { version = "2.0.0", default-features = false, path = "../../primitives/transaction-pool" } -trie-db = { version = "0.19.2", default-features = false } +trie-db = { version = "0.20.0", default-features = false } +parity-util-mem = { version = "0.5.1", default-features = false, features = ["primitive-types"] } [dev-dependencies] sc-executor = { version = "0.8", path = "../../client/executor" } diff --git a/test-utils/runtime/build.rs b/test-utils/runtime/build.rs index 8a9ee642c4fee62b7423991b1f0ab0286a71b235..1fd3d52b2f860b0d3e73676b5fc1a707964e101d 100644 --- a/test-utils/runtime/build.rs +++ b/test-utils/runtime/build.rs @@ -14,22 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; +use wasm_builder_runner::WasmBuilder; fn main() { - build_current_project_with_rustflags( - "wasm_binary.rs", - WasmBuilderSource::CratesOrPath { - path: "../../utils/wasm-builder", - version: "1.0.9", - }, + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_from_crates_or_path("1.0.9", "../../utils/wasm-builder") + .export_heap_base() // Note that we set the stack-size to 1MB explicitly even though it is set // to this value by default. This is because some of our tests (`restoration_of_globals`) // depend on the stack-size. - // - // The --export=__heap_base instructs LLD to export __heap_base as a global variable, which - // is used by the external memory allocator. - "-Clink-arg=-zstack-size=1048576 \ - -Clink-arg=--export=__heap_base", - ); + .append_to_rust_flags("-Clink-arg=-zstack-size=1048576") + .import_memory() + .build() } diff --git a/test-utils/runtime/client/Cargo.toml b/test-utils/runtime/client/Cargo.toml index aa33f550127e86f86f7606b0cd2a0008c16a5ee0..643cde16b3090ea44154448e50012414ac38264b 100644 --- a/test-utils/runtime/client/Cargo.toml +++ b/test-utils/runtime/client/Cargo.toml @@ -3,6 +3,7 @@ name = "substrate-test-runtime-client" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-block-builder = { version = "0.8", path = "../../../client/block-builder" } diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index 7646f4a96023c2dcbf94d29099587fa44e9ab567..21cf94dfa673ade329c1615fd5df2c4e9b7519b6 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -140,7 +140,12 @@ impl substrate_test_client::GenesisInit for GenesisParameters { } /// A `TestClient` with `test-runtime` builder. -pub type TestClientBuilder = substrate_test_client::TestClientBuilder; +pub type TestClientBuilder = substrate_test_client::TestClientBuilder< + substrate_test_runtime::Block, + E, + B, + GenesisParameters, +>; /// Test client type with `LocalExecutor` and generic Backend. pub type Client = sc_client::Client< diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index e68ed5f6fc17e919bdd6040ad0e3a5863d99dc21..c8dff15ef20ce7b81edf59cace30eca84746e0c8 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -114,6 +114,8 @@ pub enum Extrinsic { ChangesTrieConfigUpdate(Option), } +parity_util_mem::malloc_size_of_is_0!(Extrinsic); // non-opaque extrinisic does not need this + #[cfg(feature = "std")] impl serde::Serialize for Extrinsic { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { @@ -255,8 +257,6 @@ cfg_if! { fn use_trie() -> u64; fn benchmark_indirect_call() -> u64; fn benchmark_direct_call() -> u64; - fn returns_mutable_static() -> u64; - fn allocates_huge_stack_array(trap: bool) -> Vec; fn vec_with_capacity(size: u32) -> Vec; /// Returns the initialized block number. fn get_block_number() -> u64; @@ -299,8 +299,6 @@ cfg_if! { fn use_trie() -> u64; fn benchmark_indirect_call() -> u64; fn benchmark_direct_call() -> u64; - fn returns_mutable_static() -> u64; - fn allocates_huge_stack_array(trap: bool) -> Vec; fn vec_with_capacity(size: u32) -> Vec; /// Returns the initialized block number. fn get_block_number() -> u64; @@ -342,8 +340,8 @@ impl_outer_origin!{ #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] pub struct Event; -impl From for Event { - fn from(_evt: frame_system::Event) -> Self { +impl From> for Event { + fn from(_evt: frame_system::Event) -> Self { unimplemented!("Not required in tests!") } } @@ -373,6 +371,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl pallet_timestamp::Trait for Runtime { @@ -448,11 +449,6 @@ impl_opaque_keys! { } } -#[cfg(not(feature = "std"))] -/// Mutable static variables should be always observed to have -/// the initialized value at the start of a runtime call. -static mut MUTABLE_STATIC: u64 = 32; - cfg_if! { if #[cfg(feature = "std")] { impl_runtime_apis! { @@ -558,14 +554,6 @@ cfg_if! { (0..1000).fold(0, |p, i| p + benchmark_add_one(i)) } - fn returns_mutable_static() -> u64 { - unimplemented!("is not expected to be invoked from non-wasm builds"); - } - - fn allocates_huge_stack_array(_trap: bool) -> Vec { - unimplemented!("is not expected to be invoked from non-wasm builds"); - } - fn vec_with_capacity(_size: u32) -> Vec { unimplemented!("is not expected to be invoked from non-wasm builds"); } @@ -631,6 +619,12 @@ cfg_if! { fn generate_session_keys(_: Option>) -> Vec { SessionKeys::generate(None) } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } } impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { @@ -747,41 +741,6 @@ cfg_if! { (0..10000).fold(0, |p, i| p + benchmark_add_one(i)) } - fn returns_mutable_static() -> u64 { - unsafe { - MUTABLE_STATIC += 1; - MUTABLE_STATIC - } - } - - fn allocates_huge_stack_array(trap: bool) -> Vec { - // Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB). - // This will just decrease (stacks in wasm32-u-u grow downwards) the stack - // pointer. This won't trap on the current compilers. - let mut data = [0u8; 1024 * 768]; - - // Then make sure we actually write something to it. - // - // If: - // 1. the stack area is placed at the beginning of the linear memory space, and - // 2. the stack pointer points to out-of-bounds area, and - // 3. a write is performed around the current stack pointer. - // - // then a trap should happen. - // - for (i, v) in data.iter_mut().enumerate() { - *v = i as u8; // deliberate truncation - } - - if trap { - // There is a small chance of this to be pulled up in theory. In practice - // the probability of that is rather low. - panic!() - } - - data.to_vec() - } - fn vec_with_capacity(size: u32) -> Vec { Vec::with_capacity(size as usize) } @@ -847,6 +806,12 @@ cfg_if! { fn generate_session_keys(_: Option>) -> Vec { SessionKeys::generate(None) } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } } impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { @@ -958,54 +923,6 @@ mod tests { use sp_state_machine::ExecutionStrategy; use codec::Encode; - #[test] - fn returns_mutable_static() { - let client = TestClientBuilder::new() - .set_execution_strategy(ExecutionStrategy::AlwaysWasm) - .build(); - let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - - let ret = runtime_api.returns_mutable_static(&block_id).unwrap(); - assert_eq!(ret, 33); - - // We expect that every invocation will need to return the initial - // value plus one. If the value increases more than that then it is - // a sign that the wasm runtime preserves the memory content. - let ret = runtime_api.returns_mutable_static(&block_id).unwrap(); - assert_eq!(ret, 33); - } - - // If we didn't restore the wasm instance properly, on a trap the stack pointer would not be - // returned to its initial value and thus the stack space is going to be leaked. - // - // See https://github.com/paritytech/substrate/issues/2967 for details - #[test] - fn restoration_of_globals() { - // Allocate 32 pages (of 65536 bytes) which gives the runtime 2048KB of heap to operate on - // (plus some additional space unused from the initial pages requested by the wasm runtime - // module). - // - // The fixture performs 2 allocations of 768KB and this theoretically gives 1536KB, however, due - // to our allocator algorithm there are inefficiencies. - const REQUIRED_MEMORY_PAGES: u64 = 32; - - let client = TestClientBuilder::new() - .set_execution_strategy(ExecutionStrategy::AlwaysWasm) - .set_heap_pages(REQUIRED_MEMORY_PAGES) - .build(); - let runtime_api = client.runtime_api(); - let block_id = BlockId::Number(client.chain_info().best_number); - - // On the first invocation we allocate approx. 768KB (75%) of stack and then trap. - let ret = runtime_api.allocates_huge_stack_array(&block_id, true); - assert!(ret.is_err()); - - // On the second invocation we allocate yet another 768KB (75%) of stack - let ret = runtime_api.allocates_huge_stack_array(&block_id, false); - assert!(ret.is_ok()); - } - #[test] fn heap_pages_is_respected() { // This tests that the on-chain HEAP_PAGES parameter is respected. diff --git a/test-utils/runtime/src/system.rs b/test-utils/runtime/src/system.rs index 4d12ab8437d5b32e2d1ba2a84327eeba99a047a6..d0a38c7c7788248b933580bd5ebe89519803fcaf 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -46,7 +46,7 @@ decl_module! { decl_storage! { trait Store for Module as TestRuntime { - ExtrinsicData: map u32 => Vec; + ExtrinsicData: map hasher(blake2_256) u32 => Vec; // The current block number being processed. Set by `execute_block`. Number get(fn number): Option; ParentHash get(fn parent_hash): Hash; diff --git a/test-utils/runtime/transaction-pool/Cargo.toml b/test-utils/runtime/transaction-pool/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..615886d987ffd8f7df5c5a880693b22fd9156355 --- /dev/null +++ b/test-utils/runtime/transaction-pool/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "substrate-test-runtime-transaction-pool" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +substrate-test-runtime-client = { version = "2.0.0", path = "../client" } +parking_lot = "0.10.0" +codec = { package = "parity-scale-codec", version = "1.0.0" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +sc-transaction-graph = { version = "2.0.0", path = "../../../client/transaction-pool/graph" } +futures = { version = "0.3.1", features = ["compat"] } +derive_more = "0.99.2" diff --git a/test-utils/runtime/transaction-pool/src/lib.rs b/test-utils/runtime/transaction-pool/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..58c801d8d66d7a4b77f0d6a34673a5fe153df350 --- /dev/null +++ b/test-utils/runtime/transaction-pool/src/lib.rs @@ -0,0 +1,240 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utils for the transaction pool together with the test runtime. +//! +//! See [`TestApi`] for more information. + +use codec::Encode; +use parking_lot::RwLock; +use sp_runtime::{ + generic::{self, BlockId}, + traits::{BlakeTwo256, Hash as HashT}, + transaction_validity::{ + TransactionValidity, ValidTransaction, TransactionValidityError, InvalidTransaction, + }, +}; +use std::collections::{HashSet, HashMap}; +use substrate_test_runtime_client::{ + runtime::{Index, AccountId, Block, BlockNumber, Extrinsic, Hash, Header, Transfer}, + AccountKeyring::{self, *}, +}; + +/// Error type used by [`TestApi`]. +#[derive(Debug, derive_more::From, derive_more::Display)] +pub struct Error(sp_transaction_pool::error::Error); + +impl sp_transaction_pool::error::IntoPoolError for Error { + fn into_pool_error(self) -> Result { + Ok(self.0) + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.0) + } +} + +#[derive(Default)] +struct ChainState { + pub block_by_number: HashMap>, + pub block_by_hash: HashMap>, + pub header_by_number: HashMap, + pub nonces: HashMap, + pub invalid_hashes: HashSet, +} + +/// Test Api for transaction pool. +pub struct TestApi { + valid_modifier: RwLock>, + chain: RwLock, + validation_requests: RwLock>, +} + +impl TestApi { + /// Test Api with Alice nonce set initially. + pub fn with_alice_nonce(nonce: u64) -> Self { + let api = TestApi { + valid_modifier: RwLock::new(Box::new(|_| {})), + chain: Default::default(), + validation_requests: RwLock::new(Default::default()), + }; + + api.chain.write().nonces.insert(Alice.into(), nonce); + + api + } + + /// Default Test Api + pub fn empty() -> Self { + let api = TestApi { + valid_modifier: RwLock::new(Box::new(|_| {})), + chain: Default::default(), + validation_requests: RwLock::new(Default::default()), + }; + + api + } + + /// Set hook on modify valid result of transaction. + pub fn set_valid_modifier(&self, modifier: Box) { + *self.valid_modifier.write() = modifier; + } + + /// Push block as a part of canonical chain under given number. + pub fn push_block(&self, block_number: BlockNumber, xts: Vec) { + let mut chain = self.chain.write(); + chain.block_by_number.insert(block_number, xts); + chain.header_by_number.insert(block_number, Header { + number: block_number, + digest: Default::default(), + extrinsics_root: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + }); + } + + /// Push a block without a number. + /// + /// As a part of non-canonical chain. + pub fn push_fork_block(&self, block_hash: Hash, xts: Vec) { + let mut chain = self.chain.write(); + chain.block_by_hash.insert(block_hash, xts); + } + + fn hash_and_length_inner(ex: &Extrinsic) -> (Hash, usize) { + let encoded = ex.encode(); + (BlakeTwo256::hash(&encoded), encoded.len()) + } + + /// Mark some transaction is invalid. + /// + /// Next time transaction pool will try to validate this + /// extrinsic, api will return invalid result. + pub fn add_invalid(&self, xts: &Extrinsic) { + self.chain.write().invalid_hashes.insert( + Self::hash_and_length_inner(xts).0 + ); + } + + /// Query validation requests received. + pub fn validation_requests(&self) -> Vec { + self.validation_requests.read().clone() + } + + /// Increment nonce in the inner state. + pub fn increment_nonce(&self, account: AccountId) { + let mut chain = self.chain.write(); + chain.nonces.entry(account).and_modify(|n| *n += 1).or_insert(1); + } +} + +impl sc_transaction_graph::ChainApi for TestApi { + type Block = Block; + type Hash = Hash; + type Error = Error; + type ValidationFuture = futures::future::Ready>; + type BodyFuture = futures::future::Ready>, Error>>; + + fn validate_transaction( + &self, + _at: &BlockId, + uxt: sc_transaction_graph::ExtrinsicFor, + ) -> Self::ValidationFuture { + self.validation_requests.write().push(uxt.clone()); + + let chain_nonce = self.chain.read().nonces.get(&uxt.transfer().from).cloned().unwrap_or(0); + let requires = if chain_nonce == uxt.transfer().nonce { + vec![] + } else { + vec![vec![chain_nonce as u8]] + }; + let provides = vec![vec![uxt.transfer().nonce as u8]]; + + if self.chain.read().invalid_hashes.contains(&self.hash_and_length(&uxt).0) { + return futures::future::ready(Ok( + Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0)).into()) + )) + } + + let mut validity = ValidTransaction { + priority: 1, + requires, + provides, + longevity: 64, + propagate: true, + }; + + (self.valid_modifier.read())(&mut validity); + + futures::future::ready(Ok(Ok(validity))) + } + + fn block_id_to_number( + &self, + at: &BlockId, + ) -> Result>, Error> { + Ok(Some(number_of(at))) + } + + fn block_id_to_hash( + &self, + at: &BlockId, + ) -> Result>, Error> { + Ok(match at { + generic::BlockId::Hash(x) => Some(x.clone()), + _ => Some(Default::default()), + }) + } + + fn hash_and_length( + &self, + ex: &sc_transaction_graph::ExtrinsicFor, + ) -> (Self::Hash, usize) { + Self::hash_and_length_inner(ex) + } + + fn block_body(&self, id: &BlockId) -> Self::BodyFuture { + futures::future::ready(Ok(if let BlockId::Number(num) = id { + self.chain.read().block_by_number.get(num).cloned() + } else { + None + })) + } +} + +fn number_of(at: &BlockId) -> u64 { + match at { + generic::BlockId::Number(n) => *n as u64, + _ => 0, + } +} + +/// Generate transfer extrinsic with a given nonce. +/// +/// Part of the test api. +pub fn uxt(who: AccountKeyring, nonce: Index) -> Extrinsic { + let transfer = Transfer { + from: who.into(), + to: AccountId::default(), + nonce, + amount: 1, + }; + let signature = transfer.using_encoded(|e| who.sign(e)); + Extrinsic::Transfer(transfer, signature.into()) +} + diff --git a/utils/browser/Cargo.toml b/utils/browser/Cargo.toml index cfceaa143c7be261b3f5cc47fdf77fbba0d848ce..e52f630cb506e0253d7b167278428b3940475141 100644 --- a/utils/browser/Cargo.toml +++ b/utils/browser/Cargo.toml @@ -4,24 +4,28 @@ version = "0.8.0" authors = ["Parity Technologies "] description = "Utilities for creating a browser light-client." edition = "2018" +license = "GPL-3.0" [dependencies] futures = "0.3" futures01 = { package = "futures", version = "0.1.29" } log = "0.4.8" -libp2p = { version = "0.14.0-alpha.1", default-features = false } +libp2p = { version = "0.15.0", default-features = false } console_error_panic_hook = "0.1.6" console_log = "0.1.2" js-sys = "0.3.34" wasm-bindgen = "0.2.57" wasm-bindgen-futures = "0.4.7" -kvdb-web = "0.3" -service = { version = "0.8", package = "sc-service", path = "../../client/service", default-features = false } -network = { package = "sc-network", path = "../../client/network" } -chain-spec = { package = "sc-chain-spec", path = "../../client/chain-spec" } +kvdb-web = "0.4" +sc-informant = { version = "0.8", path = "../../client/informant" } +sc-service = { version = "0.8", path = "../../client/service", default-features = false } +sc-network = { path = "../../client/network" } +sc-chain-spec = { path = "../../client/chain-spec" } # Imported just for the `no_cc` feature clear_on_drop = { version = "0.2.3", features = ["no_cc"] } # Imported just for the `wasm-bindgen` feature rand6 = { package = "rand", version = "0.6", features = ["wasm-bindgen"] } rand = { version = "0.7", features = ["wasm-bindgen"] } +futures-timer = { version = "3.0.1", features = ["wasm-bindgen"]} +chrono = { version = "0.4", features = ["wasmbind"] } diff --git a/utils/browser/src/lib.rs b/utils/browser/src/lib.rs index 7b7fda45839ee33f09a6546827c204374afc1c5f..d82f982e14b650bde020b0847c027846960bbfda 100644 --- a/utils/browser/src/lib.rs +++ b/utils/browser/src/lib.rs @@ -17,7 +17,7 @@ use futures01::sync::mpsc as mpsc01; use log::{debug, info}; use std::sync::Arc; -use service::{ +use sc_service::{ AbstractService, RpcSession, Roles, Configuration, config::{DatabaseConfig, KeystoreConfig}, ChainSpec, RuntimeGenesis }; @@ -25,7 +25,7 @@ use wasm_bindgen::prelude::*; use futures::{prelude::*, channel::{oneshot, mpsc}, future::{poll_fn, ok}, compat::*}; use std::task::Poll; use std::pin::Pin; -use chain_spec::Extension; +use sc_chain_spec::Extension; pub use libp2p::wasm_ext::{ExtTransport, ffi::Transport}; pub use console_error_panic_hook::set_once as set_console_error_panic_hook; @@ -34,36 +34,39 @@ pub use console_log::init_with_level as init_console_log; /// Create a service configuration from a chain spec and the websocket transport. /// /// This configuration contains good defaults for a browser light client. -pub async fn browser_configuration( +pub async fn browser_configuration( transport: Transport, chain_spec: ChainSpec, -) -> Result, Box> +) -> Result, Box> where - C: Default, G: RuntimeGenesis, E: Extension, { let name = chain_spec.name().to_string(); let transport = ExtTransport::new(transport); - let mut config = Configuration::default_with_spec_and_base_path(chain_spec, None); - config.network.transport = network::config::TransportConfig::Normal { + let mut config = Configuration::default(); + config.network.boot_nodes = chain_spec.boot_nodes().to_vec(); + config.telemetry_endpoints = chain_spec.telemetry_endpoints().clone(); + config.chain_spec = Some(chain_spec); + config.network.transport = sc_network::config::TransportConfig::Normal { wasm_external_transport: Some(transport.clone()), allow_private_ipv4: true, enable_mdns: false, + use_yamux_flow_control: true, }; - config.tasks_executor = Some(Box::new(move |fut| { + config.task_executor = Some(Arc::new(move |fut| { wasm_bindgen_futures::spawn_local(fut) })); config.telemetry_external_transport = Some(transport); config.roles = Roles::LIGHT; config.name = format!("{} (Browser)", name); - config.database = { + config.database = Some({ info!("Opening Indexed DB database '{}'...", name); let db = kvdb_web::Database::open(name, 10) .await?; DatabaseConfig::Custom(Arc::new(db)) - }; + }); config.keystore = KeystoreConfig::InMemory; Ok(config) @@ -83,6 +86,11 @@ struct RpcMessage { /// Create a Client object that connects to a service. pub fn start_client(mut service: impl AbstractService) -> Client { + // Spawn informant + wasm_bindgen_futures::spawn_local( + sc_informant::build(&service, sc_informant::OutputFormat::Plain).map(drop) + ); + // We dispatch a background task responsible for processing the service. // // The main action performed by the code below consists in polling the service with diff --git a/utils/build-script-utils/Cargo.toml b/utils/build-script-utils/Cargo.toml index 36703ab838f065f3b1e0b0c37bed1bc39cc05c17..8c94edd527be18057894f3d30bea934dd82b12d5 100644 --- a/utils/build-script-utils/Cargo.toml +++ b/utils/build-script-utils/Cargo.toml @@ -3,5 +3,6 @@ name = "substrate-build-script-utils" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] diff --git a/utils/fork-tree/Cargo.toml b/utils/fork-tree/Cargo.toml index fa37161dc2495b1a7dd72210f6390285903105ba..0ac0fb65220106d98e9cb085e5a3f27e6618e565 100644 --- a/utils/fork-tree/Cargo.toml +++ b/utils/fork-tree/Cargo.toml @@ -3,6 +3,7 @@ name = "fork-tree" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } diff --git a/utils/fork-tree/src/lib.rs b/utils/fork-tree/src/lib.rs index 1aa085c3da4855ab8019e54dacc30f033300a8ba..8ce0b729c67c9848533bedf49684dc1acb7e663d 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -200,7 +200,7 @@ impl ForkTree where data, hash: hash, number: number, - children: Vec::new(), + children: Vec::new(), }); self.rebalance(); @@ -232,10 +232,10 @@ impl ForkTree where number: &N, is_descendent_of: &F, predicate: &P, - ) -> Result>, Error> - where E: std::error::Error, - F: Fn(&H, &H) -> Result, - P: Fn(&V) -> bool, + ) -> Result>, Error> where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, { // search for node starting from all roots for root in self.roots.iter() { @@ -250,6 +250,31 @@ impl ForkTree where Ok(None) } + /// Same as [`find_node_where`](Self::find_node_where), but returns mutable reference. + pub fn find_node_where_mut( + &mut self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + // search for node starting from all roots + for root in self.roots.iter_mut() { + let node = root.find_node_where_mut(hash, number, is_descendent_of, predicate)?; + + // found the node, early exit + if let FindOutcome::Found(node) = node { + return Ok(Some(node)); + } + } + + Ok(None) + } + /// Finalize a root in the tree and return it, return `None` in case no root /// with the given hash exists. All other roots are pruned, and the children /// of the finalized node become the new roots. @@ -609,16 +634,17 @@ mod node_implementation { /// when the predicate fails. /// The given function `is_descendent_of` should return `true` if the second hash (target) /// is a descendent of the first hash (base). - // FIXME: it would be useful if this returned a mutable reference but - // rustc can't deal with lifetimes properly. an option would be to try - // an iterative definition instead. - pub fn find_node_where( + /// + /// The returned indices are from last to first. The earliest index in the traverse path + /// goes last, and the final index in the traverse path goes first. An empty list means + /// that the current node is the result. + pub fn find_node_index_where( &self, hash: &H, number: &N, is_descendent_of: &F, predicate: &P, - ) -> Result>, Error> + ) -> Result>, Error> where E: std::error::Error, F: Fn(&H, &H) -> Result, P: Fn(&V) -> bool, @@ -631,11 +657,14 @@ mod node_implementation { let mut known_descendent_of = false; // continue depth-first search through all children - for node in self.children.iter() { + for (i, node) in self.children.iter().enumerate() { // found node, early exit - match node.find_node_where(hash, number, is_descendent_of, predicate)? { + match node.find_node_index_where(hash, number, is_descendent_of, predicate)? { FindOutcome::Abort => return Ok(FindOutcome::Abort), - FindOutcome::Found(x) => return Ok(FindOutcome::Found(x)), + FindOutcome::Found(mut x) => { + x.push(i); + return Ok(FindOutcome::Found(x)) + }, FindOutcome::Failure(true) => { // if the block was a descendent of this child, // then it cannot be a descendent of any others, @@ -655,7 +684,7 @@ mod node_implementation { if is_descendent_of { // if the predicate passes we return the node if predicate(&self.data) { - return Ok(FindOutcome::Found(self)); + return Ok(FindOutcome::Found(Vec::new())); } } @@ -663,6 +692,70 @@ mod node_implementation { // the block was a descendent. Ok(FindOutcome::Failure(is_descendent_of)) } + + /// Find a node in the tree that is the deepest ancestor of the given + /// block hash which also passes the given predicate, backtracking + /// when the predicate fails. + /// The given function `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + pub fn find_node_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let outcome = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + + match outcome { + FindOutcome::Abort => Ok(FindOutcome::Abort), + FindOutcome::Failure(f) => Ok(FindOutcome::Failure(f)), + FindOutcome::Found(mut indexes) => { + let mut cur = self; + + while let Some(i) = indexes.pop() { + cur = &cur.children[i]; + } + Ok(FindOutcome::Found(cur)) + }, + } + } + + /// Find a node in the tree that is the deepest ancestor of the given + /// block hash which also passes the given predicate, backtracking + /// when the predicate fails. + /// The given function `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + pub fn find_node_where_mut( + &mut self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let outcome = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + + match outcome { + FindOutcome::Abort => Ok(FindOutcome::Abort), + FindOutcome::Failure(f) => Ok(FindOutcome::Failure(f)), + FindOutcome::Found(mut indexes) => { + let mut cur = self; + + while let Some(i) = indexes.pop() { + cur = &mut cur.children[i]; + } + Ok(FindOutcome::Found(cur)) + }, + } + } } } diff --git a/utils/frame/rpc/support/Cargo.toml b/utils/frame/rpc/support/Cargo.toml index 19451743cd26841d0e411d7d2a73a05313aafc05..3b4339496a99192ad5e34d672680e375634c86f3 100644 --- a/utils/frame/rpc/support/Cargo.toml +++ b/utils/frame/rpc/support/Cargo.toml @@ -3,6 +3,7 @@ name = "substrate-frame-rpc-support" version = "2.0.0" authors = ["Parity Technologies ", "Andrew Dirksen "] edition = "2018" +license = "GPL-3.0" [dependencies] futures = { version = "0.3.0", features = ["compat"] } diff --git a/utils/frame/rpc/support/src/lib.rs b/utils/frame/rpc/support/src/lib.rs index 1e29c4e91b49555a5e63bc5f089237876786f980..a9982945e7f73eefbc0977b0bbc228e8a56ada69 100644 --- a/utils/frame/rpc/support/src/lib.rs +++ b/utils/frame/rpc/support/src/lib.rs @@ -65,9 +65,9 @@ use sc_rpc_api::state::StateClient; /// decl_storage! { /// trait Store for Module as TestRuntime { /// pub LastActionId: u64; -/// pub Voxels: map Loc => Block; -/// pub Actions: linked_map u64 => Loc; -/// pub Prefab: double_map u128, (i8, i8, i8) => Block; +/// pub Voxels: map hasher(blake2_256) Loc => Block; +/// pub Actions: linked_map hasher(blake2_256) u64 => Loc; +/// pub Prefab: double_map hasher(blake2_256) u128, hasher(blake2_256) (i8, i8, i8) => Block; /// } /// } /// diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 39f7735eed682b528ad65f7fb724b2a255847fcf..818794f8469fc23e822f99ea6b9360d58634e415 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -3,6 +3,7 @@ name = "substrate-frame-rpc-system" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +license = "GPL-3.0" [dependencies] sc-client = { version = "0.8", path = "../../../../client/" } diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index f909e66fd6e2ecf0d462546b7abd26f329f8a4ce..9cc01fb6ec0c3b83cffb09a7ce76b03eead803d8 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -87,7 +87,7 @@ where P: TransactionPool + 'static, Block: traits::Block, AccountId: Clone + std::fmt::Display + Codec, - Index: Clone + std::fmt::Display + Codec + Send + traits::SimpleArithmetic + 'static, + Index: Clone + std::fmt::Display + Codec + Send + traits::AtLeast32Bit + 'static, { fn nonce(&self, account: AccountId) -> FutureResult { let get_nonce = || { @@ -141,7 +141,7 @@ where F: Fetcher + 'static, Block: traits::Block, AccountId: Clone + std::fmt::Display + Codec + Send + 'static, - Index: Clone + std::fmt::Display + Codec + Send + traits::SimpleArithmetic + 'static, + Index: Clone + std::fmt::Display + Codec + Send + traits::AtLeast32Bit + 'static, { fn nonce(&self, account: AccountId) -> FutureResult { let best_hash = self.client.info().best_hash; @@ -189,7 +189,7 @@ fn adjust_nonce( ) -> Index where P: TransactionPool, AccountId: Clone + std::fmt::Display + Encode, - Index: Clone + std::fmt::Display + Encode + traits::SimpleArithmetic + 'static, + Index: Clone + std::fmt::Display + Encode + traits::AtLeast32Bit + 'static, { log::debug!(target: "rpc", "State nonce for {}: {}", account, nonce); // Now we need to query the transaction pool @@ -235,7 +235,9 @@ mod tests { // given let _ = env_logger::try_init(); let client = Arc::new(substrate_test_runtime_client::new()); - let pool = Arc::new(BasicPool::new(Default::default(), FullChainApi::new(client.clone()))); + let pool = Arc::new( + BasicPool::new(Default::default(), Arc::new(FullChainApi::new(client.clone()))) + ); let new_transaction = |nonce: u64| { let t = Transfer { diff --git a/utils/wasm-builder-runner/Cargo.toml b/utils/wasm-builder-runner/Cargo.toml index ab8a539054820445d744711a99192b3adf6f524f..1380d64fb30055e6f3cb4ac1073560e21a32552d 100644 --- a/utils/wasm-builder-runner/Cargo.toml +++ b/utils/wasm-builder-runner/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-wasm-builder-runner" -version = "1.0.4" +version = "1.0.5" authors = ["Parity Technologies "] description = "Runner for substrate-wasm-builder" edition = "2018" diff --git a/utils/wasm-builder-runner/src/lib.rs b/utils/wasm-builder-runner/src/lib.rs index 40016a015972cedddd5361d4963e5b7ad087b17a..6366c1ce3750d98d33663995e7cf0a9d245200cc 100644 --- a/utils/wasm-builder-runner/src/lib.rs +++ b/utils/wasm-builder-runner/src/lib.rs @@ -25,7 +25,10 @@ //! //! For more information see -use std::{env, process::{Command, self}, fs, path::{PathBuf, Path}}; +use std::{ + env, process::{Command, self}, fs, path::{PathBuf, Path}, hash::{Hash, Hasher}, + collections::hash_map::DefaultHasher, +}; /// Environment variable that tells us to skip building the WASM binary. const SKIP_BUILD_ENV: &str = "SKIP_WASM_BUILD"; @@ -47,6 +50,225 @@ fn replace_back_slashes(path: T) -> String { path.to_string().replace("\\", "/") } +/// Returns the manifest dir from the `CARGO_MANIFEST_DIR` env. +fn get_manifest_dir() -> PathBuf { + env::var("CARGO_MANIFEST_DIR") + .expect("`CARGO_MANIFEST_DIR` is always set for `build.rs` files; qed") + .into() +} + +/// First step of the [`WasmBuilder`] to select the project to build. +pub struct WasmBuilderSelectProject { + /// This parameter just exists to make it impossible to construct + /// this type outside of this crate. + _ignore: (), +} + +impl WasmBuilderSelectProject { + /// Use the current project as project for building the WASM binary. + /// + /// # Panics + /// + /// Panics if the `CARGO_MANIFEST_DIR` variable is not set. This variable + /// is always set by `Cargo` in `build.rs` files. + pub fn with_current_project(self) -> WasmBuilderSelectSource { + WasmBuilderSelectSource(get_manifest_dir().join("Cargo.toml")) + } + + /// Use the given `path` as project for building the WASM binary. + /// + /// Returns an error if the given `path` does not points to a `Cargo.toml`. + pub fn with_project( + self, + path: impl Into, + ) -> Result { + let path = path.into(); + + if path.ends_with("Cargo.toml") { + Ok(WasmBuilderSelectSource(path)) + } else { + Err("Project path must point to the `Cargo.toml` of the project") + } + } +} + +/// Second step of the [`WasmBuilder`] to set the source of the `wasm-builder`. +pub struct WasmBuilderSelectSource(PathBuf); + +impl WasmBuilderSelectSource { + /// Use the given `path` as source for `wasm-builder`. + /// + /// The `path` must be relative and point to the directory that contains the `Cargo.toml` for + /// `wasm-builder`. + pub fn with_wasm_builder_from_path(self, path: &'static str) -> WasmBuilder { + WasmBuilder { + source: WasmBuilderSource::Path(path), + rust_flags: Vec::new(), + file_name: None, + project_cargo_toml: self.0, + } + } + + /// Use the given `repo` and `rev` as source for `wasm-builder`. + pub fn with_wasm_builder_from_git(self, repo: &'static str, rev: &'static str) -> WasmBuilder { + WasmBuilder { + source: WasmBuilderSource::Git { repo, rev }, + rust_flags: Vec::new(), + file_name: None, + project_cargo_toml: self.0, + } + } + + /// Use the given `version` to fetch `wasm-builder` source from crates.io. + pub fn with_wasm_builder_from_crates(self, version: &'static str) -> WasmBuilder { + WasmBuilder { + source: WasmBuilderSource::Crates(version), + rust_flags: Vec::new(), + file_name: None, + project_cargo_toml: self.0, + } + } + + /// Use the given `version` to fetch `wasm-builder` source from crates.io or use + /// the given `path` as source. + /// + /// The `path` must be relative and point to the directory that contains the `Cargo.toml` for + /// `wasm-builder`. + pub fn with_wasm_builder_from_crates_or_path( + self, + version: &'static str, + path: &'static str, + ) -> WasmBuilder { + WasmBuilder { + source: WasmBuilderSource::CratesOrPath { version, path }, + rust_flags: Vec::new(), + file_name: None, + project_cargo_toml: self.0, + } + } + + /// Use the given `source` as source for `wasm-builder`. + pub fn with_wasm_builder_source(self, source: WasmBuilderSource) -> WasmBuilder { + WasmBuilder { + source, + rust_flags: Vec::new(), + file_name: None, + project_cargo_toml: self.0, + } + } +} + +/// The builder for building a wasm binary. +/// +/// The builder itself is seperated into multiple structs to make the setup type safe. +/// +/// Building a wasm binary: +/// +/// 1. Call [`WasmBuilder::new`] to create a new builder. +/// 2. Select the project to build using the methods of [`WasmBuilderSelectProject`]. +/// 3. Select the source of the `wasm-builder` crate using the methods of +/// [`WasmBuilderSelectSource`]. +/// 4. Set additional `RUST_FLAGS` or a different name for the file containing the WASM code +/// using methods of [`Self`]. +/// 5. Build the WASM binary using [`Self::build`]. +pub struct WasmBuilder { + /// Where should we pull the `wasm-builder` crate from. + source: WasmBuilderSource, + /// Flags that should be appended to `RUST_FLAGS` env variable. + rust_flags: Vec, + /// The name of the file that is being generated in `OUT_DIR`. + /// + /// Defaults to `wasm_binary.rs`. + file_name: Option, + /// The path to the `Cargo.toml` of the project that should be build + /// for wasm. + project_cargo_toml: PathBuf, +} + +impl WasmBuilder { + /// Create a new instance of the builder. + pub fn new() -> WasmBuilderSelectProject { + WasmBuilderSelectProject { + _ignore: (), + } + } + + /// Enable exporting `__heap_base` as global variable in the WASM binary. + /// + /// This adds `-Clink-arg=--export=__heap_base` to `RUST_FLAGS`. + pub fn export_heap_base(mut self) -> Self { + self.rust_flags.push("-Clink-arg=--export=__heap_base".into()); + self + } + + /// Set the name of the file that will be generated in `OUT_DIR`. + /// + /// This file needs to be included to get access to the build WASM binary. + /// + /// If this function is not called, `file_name` defaults to `wasm_binary.rs` + pub fn set_file_name(mut self, file_name: impl Into) -> Self { + self.file_name = Some(file_name.into()); + self + } + + /// Instruct the linker to import the memory into the WASM binary. + /// + /// This adds `-C link-arg=--import-memory` to `RUST_FLAGS`. + pub fn import_memory(mut self) -> Self { + self.rust_flags.push("-C link-arg=--import-memory".into()); + self + } + + /// Append the given `flag` to `RUST_FLAGS`. + /// + /// `flag` is appended as is, so it needs to be a valid flag. + pub fn append_to_rust_flags(mut self, flag: impl Into) -> Self { + self.rust_flags.push(flag.into()); + self + } + + /// Build the WASM binary. + pub fn build(self) { + if check_skip_build() { + // If we skip the build, we still want to make sure to be called when an env variable + // changes + generate_rerun_if_changed_instructions(); + return; + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is set by cargo!")); + let file_path = out_dir.join(self.file_name.unwrap_or_else(|| "wasm_binary.rs".into())); + + // Hash the path to the project cargo toml. + let mut hasher = DefaultHasher::new(); + self.project_cargo_toml.hash(&mut hasher); + + let project_name = env::var("CARGO_PKG_NAME").expect("`CARGO_PKG_NAME` is set by cargo!"); + // Make sure the `wasm-builder-runner` path is unique by concatenating the name of the + // project that is compiling the WASM binary with the hash of the path to the project that + // should be compiled as WASM binary. + let project_folder = get_workspace_root() + .join(format!("{}{}", project_name, hasher.finish())); + + if check_provide_dummy_wasm_binary() { + provide_dummy_wasm_binary(&file_path); + } else { + create_project( + &project_folder, + &file_path, + self.source, + &self.project_cargo_toml, + &self.rust_flags.into_iter().map(|f| format!("{} ", f)).collect::(), + ); + run_project(&project_folder); + } + + // As last step we need to generate our `rerun-if-changed` stuff. If a build fails, we don't + // want to spam the output! + generate_rerun_if_changed_instructions(); + } +} + /// The `wasm-builder` dependency source. pub enum WasmBuilderSource { /// The relative path to the source code from the current manifest dir. @@ -96,46 +318,21 @@ impl WasmBuilderSource { /// Build the currently built project as WASM binary and extend `RUSTFLAGS` with the given rustflags. /// /// For more information, see [`build_current_project`]. +#[deprecated( + since = "1.0.5", + note = "Please switch to [`WasmBuilder`]", +)] pub fn build_current_project_with_rustflags( file_name: &str, wasm_builder_source: WasmBuilderSource, - default_rustflags: &str, + default_rust_flags: &str, ) { - if check_skip_build() { - // If we skip the build, we still want to make sure to be called when an env variable changes - generate_rerun_if_changed_instructions(); - return; - } - - let manifest_dir = PathBuf::from( - env::var("CARGO_MANIFEST_DIR").expect( - "`CARGO_MANIFEST_DIR` is always set for `build.rs` files; qed" - ) - ); - - let cargo_toml_path = manifest_dir.join("Cargo.toml"); - let out_dir = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is set by cargo!")); - let file_path = out_dir.join(file_name); - let project_name = env::var("CARGO_PKG_NAME").expect("`CARGO_PKG_NAME` is set by cargo!"); - let project_folder = get_workspace_root().join(project_name); - - if check_provide_dummy_wasm_binary() { - provide_dummy_wasm_binary(&file_path); - } else { - create_project( - &project_folder, - &file_path, - &manifest_dir, - wasm_builder_source, - &cargo_toml_path, - default_rustflags, - ); - run_project(&project_folder); - } - - // As last step we need to generate our `rerun-if-changed` stuff. If a build fails, we don't - // want to spam the output! - generate_rerun_if_changed_instructions(); + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_source(wasm_builder_source) + .append_to_rust_flags(default_rust_flags) + .set_file_name(file_name) + .build() } /// Build the currently built project as WASM binary. @@ -145,7 +342,12 @@ pub fn build_current_project_with_rustflags( /// `file_name` - The name of the file being generated in the `OUT_DIR`. The file contains the /// constant `WASM_BINARY` which contains the build wasm binary. /// `wasm_builder_path` - Path to the wasm-builder project, relative to `CARGO_MANIFEST_DIR`. +#[deprecated( + since = "1.0.5", + note = "Please switch to [`WasmBuilder`]", +)] pub fn build_current_project(file_name: &str, wasm_builder_source: WasmBuilderSource) { + #[allow(deprecated)] build_current_project_with_rustflags(file_name, wasm_builder_source, ""); } @@ -171,7 +373,6 @@ fn get_workspace_root() -> PathBuf { fn create_project( project_folder: &Path, file_path: &Path, - manifest_dir: &Path, wasm_builder_source: WasmBuilderSource, cargo_toml_path: &Path, default_rustflags: &str, @@ -193,7 +394,7 @@ fn create_project( [workspace] "#, - wasm_builder_source = wasm_builder_source.to_cargo_source(manifest_dir), + wasm_builder_source = wasm_builder_source.to_cargo_source(&get_manifest_dir()), ) ).expect("WASM build runner `Cargo.toml` writing can not fail; qed");