diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 828c830948a13841269a1fa6479d744f65856663..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 @@ -524,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/kubernetes/templates/service.yaml b/.maintain/kubernetes/templates/service.yaml index 01ba9d5a567c57045b585aa7e1fa894d404cbf90..b14bb74c10a1abdeae784a51189500e46bce131d 100644 --- a/.maintain/kubernetes/templates/service.yaml +++ b/.maintain/kubernetes/templates/service.yaml @@ -33,7 +33,7 @@ spec: app: {{ .Values.GitlabEnvSlug | default .Values.app }} sessionAffinity: None type: NodePort - # don't route exteral traffic to non-local pods + # don't route external traffic to non-local pods externalTrafficPolicy: Local {{- else if .Values.validator.keys }} {{- $root := . -}} diff --git a/.maintain/kubernetes/values.yaml b/.maintain/kubernetes/values.yaml index 89a6445e00a2f59c6290ef8e4eafdc9fdd925832..4c3cb5c7d702d6c4a8a3ce6b428012dcd634e3db 100644 --- a/.maintain/kubernetes/values.yaml +++ b/.maintain/kubernetes/values.yaml @@ -48,7 +48,7 @@ validator: {} # substrate-1-node-key # # pod names are canonical. changing these or providing different amount of - # keys than the replicas count will lead to behavior noone ever has + # keys than the replicas count will lead to behavior no one ever has # experienced before. 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 eadf251f759eedeb120fda3e022b56970031d93f..b557048f9d36a3878df336e13402265a5e03025d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6656 +4,7342 @@ 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", + "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 = "chacha20-poly1305-aead" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d2058ba29594f69c75e8a9018e0485e3914ca5084e3613cd64529042f5423b" +dependencies = [ + "constant_time_eq", +] [[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)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time", + "wasm-bindgen", ] [[package]] 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)", + "autocfg 0.1.7", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", ] [[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)", -] - -[[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", + "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 (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)", + "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 = [ + "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", + "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 = [ - "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)", + "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 = "grafana-data-source" version = "0.8.0" dependencies = [ - "async-std 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.13.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)", - "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)", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", + "chrono", + "derive_more", + "futures-timer 3.0.1", + "futures-util", + "hyper 0.13.2", + "lazy_static", + "log 0.4.8", + "parking_lot 0.10.0", + "serde", + "serde_json", + "tokio 0.2.11", ] [[package]] name = "grafana-data-source-test" version = "2.0.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)", - "grafana-data-source 0.8.0", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4", + "futures-timer 3.0.1", + "grafana-data-source", + "rand 0.7.3", ] [[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)", -] - -[[package]] -name = "hashbrown" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -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)", + "crunchy", ] [[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", + "net2", + "pin-project", + "time", + "tokio 0.2.11", + "tower-service", + "want 0.3.0", ] [[package]] name = "hyper-rustls" -version = "0.17.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ea6215c7314d450ee45970ab8b3851ab447a0e6bafdd19e31b20a42dbb7faf" 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.5.4", + "ct-logs", + "futures-util", + "hyper 0.13.2", + "rustls", + "rustls-native-certs", + "tokio 0.2.11", + "tokio-rustls", + "webpki", ] [[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.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58becf0b9585fcfbb8215bbe6e6ac187fcc180fd1026925ca180c845aa5a6e8" +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-pnet", + "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.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b874594c4b29de1a29f27871feba8e6cd13aa54a8a1e8f8c7cf3dfac5ca287c" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "fnv", + "futures 0.3.4", + "futures-timer 3.0.1", + "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", + "void", + "zeroize 1.1.0", ] [[package]] name = "libp2p-core-derive" -version = "0.14.0-alpha.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d472e9d522f588805c77801de10b957be84e10f019ca5f869fa1825b15ea9b" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e25004d4d9837b44b22c5f1a69be1724a5168fef6cff1716b5176a972c3aa62" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99e552f9939b606eb4b59f7f64d9b01e3f96752f47e350fc3c5fc646ed3f649" 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.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3234f12e44f9a50351a9807b97fe7de11eb9ae4482370392ba10da6dc90722" +dependencies = [ + "cuckoofilter", + "fnv", + "futures 0.3.4", + "libp2p-core", + "libp2p-swarm", + "prost", + "prost-build", + "rand 0.7.3", + "smallvec 1.2.0", +] + +[[package]] +name = "libp2p-gossipsub" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d46cb3e0841bd951cbf4feae56cdc081e6347836a644fb260c3ec554149b4006" 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)", + "base64 0.11.0", + "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-identify" -version = "0.14.0-alpha.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfeb935a9bd41263e4f3a24b988e9f4a044f3ae89ac284e83c17fe2f84e0d66b" 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)", + "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.14.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -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)", +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2efcff2af085e8181c421f68fe9c2b0a067379d146731925b3ac8f8e605c458" +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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881fcfb360c2822db9f0e6bb6f89529621556ed9a8b038313414eda5107334de" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8507b37ad0eed275efcde67a023c3d85af6c80768b193845b9288e848e1af95" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac7d33809afdf6794f09fdb2f9f94e1550ae230be5bae6430a078eb96fc9e5a6" 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", + "sha2", + "snow", + "static_assertions", + "x25519-dalek 0.5.2", + "zeroize 1.1.0", ] [[package]] name = "libp2p-ping" -version = "0.14.0-alpha.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d22f2f228b3a828dca1cb8aa9fa331e0bc9c36510cb2c1916956e20dc85e8c" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56126a204d7b3382bac163143ff4125a14570b3ba76ba979103d1ae1abed1923" 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-pnet" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b916938a8868f75180aeeffcc6a516a922d165e8fa2a90b57bad989d1ccbb57a" +dependencies = [ + "futures 0.3.4", + "log 0.4.8", + "pin-project", + "rand 0.7.3", + "salsa20", + "sha3", ] [[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.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec00eb9a3404ed76a0e14f637edcaa7f2b4a27a16884da4a56f2f21e166c2843" +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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e9f4fb84a4bfe3d3a361c1fbcd4af017ba68f0a46a77bfbcc48bf8a456d6ef" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e80ad4e3535345f3d666554ce347d3100453775611c05c60786bf9a1747a10" 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 3.0.1", + "get_if_addrs", + "ipnet", + "libp2p-core", + "log 0.4.8", ] [[package]] name = "libp2p-uds" -version = "0.14.0-alpha.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d329564a43da9d0e055a5b938633c4a8ceab1f59cec133fbc4647917c07341" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39703653caa36f4afd0def39cc49a3ac0fa1d4289ca1802e417af03e4f5ef950" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5351ca9eea122081c1c0f9323164d2918cac29b5a6bfe5054d4ba8ec9447cf42" 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.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f72aa5a7273c29c6eaea09108a49feaefc7456164863f64f86a193f9e78a4b7f" 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", + "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)", -] - -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map", ] [[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", ] [[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-inspect", + "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-inspect" +version = "0.8.0" +dependencies = [ + "derive_more", + "log 0.4.8", + "parity-scale-codec", + "sc-cli", + "sc-client-api", + "sc-service", + "sp-blockchain", + "sp-core", + "sp-runtime", + "structopt", ] [[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", + "sc-consensus-babe", + "sc-consensus-babe-rpc", + "sc-consensus-epochs", + "sc-keystore", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "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", + "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.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[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" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0", ] [[package]] -name = "once_cell" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "once_cell" -version = "1.2.0" +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.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26df883298bc3f4e92528b4c5cc9f806b791955b136da3e5e939ed9de0fd958b" 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.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1cd2ba02391b81367bec529fb209019d718684fdc8ad6a712c2b536e46f775" 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" - -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -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)", -] +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" [[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", + "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)", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -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)", + "lock_api", + "parking_lot_core 0.7.0", ] [[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" - -[[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)", -] +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" [[package]] name = "proc-macro2" -version = "1.0.6" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0", ] [[package]] -name = "prost" -version = "0.5.0" +name = "proc-macro2" +version = "1.0.8" 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-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[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", + "log 0.4.8", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ffebdbb48c14f84eba0b715197d673aff1dd22cc1007ca647e28483bbcc307" 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)", + "openssl-probe", + "rustls", + "schannel", + "security-framework", ] [[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 = "salsa20" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2324b0e8c3bb9a586a571fdb3136f70e7e2c748de00a78043f86e0cff91f91fe" +dependencies = [ + "byteorder 1.3.4", + "salsa20-core", + "stream-cipher", +] + +[[package]] +name = "salsa20-core" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe6cc1b9f5a5867853ade63099de70f042f7679e408d1ffe52821c9248e6e69" +dependencies = [ + "stream-cipher", +] [[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]] name = "sc-cli" version = "0.8.0" dependencies = [ - "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", - "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.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)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "names 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-client-api 2.0.0", - "sc-network 0.8.0", - "sc-service 0.8.0", - "sc-telemetry 2.0.0", - "sc-tracing 2.0.0", - "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-blockchain 2.0.0", - "sp-core 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", - "structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (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)", + "ansi_term 0.12.1", + "app_dirs", + "atty", + "chrono", + "clap", + "derive_more", + "env_logger 0.7.1", + "fdlimit", + "futures 0.3.4", + "lazy_static", + "log 0.4.8", + "names", + "parity-util-mem", + "regex", + "rpassword", + "sc-client-api", + "sc-informant", + "sc-network", + "sc-service", + "sc-telemetry", + "sc-tracing", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-panic-handler", + "sp-runtime", + "sp-state-machine", + "structopt", + "tempfile", + "time", + "tokio 0.2.11", ] [[package]] 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", + "serde", + "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-babe-rpc" +version = "0.8.0" +dependencies = [ + "derive_more", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-keystore", + "serde", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-keyring", + "sp-runtime", + "substrate-test-runtime-client", + "tempfile", +] + +[[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", + "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.5.4", + "env_logger 0.7.1", + "fnv", + "futures 0.3.4", + "futures-timer 3.0.1", + "hyper 0.13.2", + "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.2.11", ] [[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-runtime", + "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]] name = "sc-service" version = "0.8.0" dependencies = [ - "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", - "exit-future 0.2.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)", - "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "grafana-data-source 0.8.0", - "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)", - "parity-multiaddr 0.5.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)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sc-chain-spec 2.0.0", - "sc-client 0.8.0", - "sc-client-api 2.0.0", - "sc-client-db 0.8.0", - "sc-executor 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-rpc-server 2.0.0", - "sc-telemetry 2.0.0", - "sc-tracing 2.0.0", - "sc-transaction-pool 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)", - "sp-api 2.0.0", - "sp-application-crypto 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-io 2.0.0", - "sp-runtime 2.0.0", - "sp-session 2.0.0", - "sp-transaction-pool 2.0.0", - "substrate-test-runtime-client 2.0.0", - "sysinfo 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", - "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more", + "exit-future", + "futures 0.1.29", + "futures 0.3.4", + "futures-diagnose", + "futures-timer 3.0.1", + "grafana-data-source", + "lazy_static", + "log 0.4.8", + "parity-multiaddr", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "sc-chain-spec", + "sc-client", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sc-finality-grandpa", + "sc-keystore", + "sc-network", + "sc-offchain", + "sc-rpc", + "sc-rpc-server", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "serde", + "serde_json", + "slog", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-session", + "sp-transaction-pool", + "substrate-test-runtime-client", + "sysinfo", + "target_info", + "tokio 0.2.11", + "tracing", + "wasm-timer", ] [[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)", + "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]] name = "sc-tracing" version = "2.0.0" dependencies = [ - "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "grafana-data-source 0.8.0", - "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)", - "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)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "erased-serde", + "grafana-data-source", + "log 0.4.8", + "parking_lot 0.10.0", + "sc-telemetry", + "serde", + "serde_json", + "slog", + "tracing", + "tracing-core", ] [[package]] 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", + "linked-hash-map", + "log 0.4.8", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.0", + "serde", + "sp-blockchain", + "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", + "futures-timer 2.0.2", + "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" - [[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", + "blake2-rfc", + "chacha20-poly1305-aead", + "rand 0.7.3", + "rand_core 0.5.1", + "ring", + "rustc_version", + "sha2", + "subtle 2.2.2", + "x25519-dalek 0.6.0", ] [[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.3.0", + "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]] @@ -6664,205 +7350,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]] @@ -6873,115 +7578,131 @@ 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", + "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-blockchain", + "sp-runtime", + "sp-transaction-pool", + "substrate-test-runtime-client", ] [[package]] @@ -6992,1848 +7713,1448 @@ 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" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" 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", + "hmac", + "once_cell", + "pbkdf2", + "rand 0.7.3", + "rustc-hash", + "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", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "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", + "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 = [ - "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)", + "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.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141afec0978abae6573065a48882c6bae44c5cc61db9b511ac4abf6a09bfd9cc" 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)", + "futures-core", + "rustls", + "tokio 0.2.11", + "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", ] [[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 = [ + "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 = [ - "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)", + "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", + "libc", +] + +[[package]] +name = "which" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5475d47078209a02e60614f7ba5e645ef3ed60f771920ac1906d7c1cc65024c8" 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)", + "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", + "curve25519-dalek 1.2.3", + "rand_core 0.3.1", +] + +[[package]] +name = "x25519-dalek" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" 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)", + "curve25519-dalek 2.0.0", + "rand_core 0.5.1", + "zeroize 1.1.0", ] [[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.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d73295bc9d9acf89dd9336b3b5f5b57731ee72b587857dd4312721a0196b48e5" 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)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" -"checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" -"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum ahash 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" -"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14" -"checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" -"checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" -"checksum asn1_der 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6fce6b6a0ffdafebd82c87e79e3f40e8d2c523e5fea5566ff6b90509bf98d638" -"checksum asn1_der_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d0864d84b8e07b145449be9a8537db86bf9de5ce03b913214694643b4743502" -"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" -"checksum async-macros 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "644a5a8de80f2085a1e7e57cd1544a2a7438f6e003c0790999bd43b92a77cdb2" -"checksum async-std 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "513ee3c49800679a319912340f5601afda9e72848d7dea3a48bab489e8c1a46f" -"checksum async-task 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de6bd58f7b9cc49032559422595c81cbfcf04db2f2133592f70af19e258a1ced" -"checksum async-tls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce6977f57fa68da77ffe5542950d47e9c23d65f5bc7cb0a9f8700996913eec7" -"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" -"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" -"checksum bindgen 0.49.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4c07087f3d5731bf3fb375a81841b99597e25dc11bd3bc72d16d43adf6624a6e" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" -"checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" -"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b83b7baab1e671718d78204225800d6b170e648188ac7dc992e9d6bddf87d0c0" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum broadcaster 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "07a1446420a56f1030271649ba0da46d23239b3a68c73591cea5247f15a788a0" -"checksum bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c95ee6bba9d950218b6cc910cf62bc9e0a171d0f4537e3627b0f54d08549b188" -"checksum bs58 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b170cd256a3f9fa6b9edae3e44a7dfdfc77e8124dbc3e2612d75f9c3e2396dae" -"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" -"checksum build-helper 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" -"checksum bumpalo 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fe2567a8d8a3aedb4e39aa39e186d5673acfd56393c6ac83b2bc5bd82f4369c" -"checksum byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f6209f3b2c1edea170002e016d5ead6903d3bb0a846477f53bbeb614967a52a9" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10004c15deb332055f7a4a208190aed362cf9a7c2f6ab70a305fba50e1105f38" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" -"checksum cargo_metadata 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" -"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" -"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" -"checksum cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" -"checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" -"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" -"checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" -"checksum console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e7871d2947441b0fdd8e2bd1ce2a2f75304f896582c0d572162d48290683c48" -"checksum const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b641a8c9867e341f3295564203b1c250eb8ce6cb6126e007941f78c4d2ed7fe" -"checksum const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" -"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" -"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum cranelift-bforest 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd05aac8cefcde54ce26178df8f36cb1f518ac691db650e7d2440c2b6b41c4dc" -"checksum cranelift-codegen 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63d9b6ff8a94f98deabab21880d7fd54996e0e16be687b6f80a3b6bdd9c188d" -"checksum cranelift-codegen-meta 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7cb3df51c2c07d719d02869bfac6cabd8d82ee308d5b29ca62e6528723cc33a4" -"checksum cranelift-codegen-shared 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "758f9426b2e22bf83fc1a6b231a9d53cd4830751883c7f0e196ebb3c210467b3" -"checksum cranelift-entity 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff064733df8b98f453060264a8790393d1e807aca6942706b42f79a4f7aae9ed" -"checksum cranelift-frontend 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1eaafb5fa623dcbe19a28084a8226d7a1b17184a949c1a1f29a46b479867998d" -"checksum cranelift-native 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "90033dbd7293f6fad4cf9dcd769cd621d60df22b1c5a11799e86359b7447a51d" -"checksum cranelift-wasm 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54cb82a1071f88822763a583ec1a8688ffe5e2cda02c111d4483dd4376ed14d8" -"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" -"checksum criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" -"checksum criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" -"checksum criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" -"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" -"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" -"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" -"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" -"checksum ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" -"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f" -"checksum cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f" -"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" -"checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" -"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" -"checksum derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8" -"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" -"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" -"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" -"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" -"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -"checksum enumflags2 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "33121c8782ba948ba332dab29311b026a8716dc65a1599e5b88f392d38496af8" -"checksum enumflags2_derive 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ecf634c5213044b8d54a46dd282cf5dd1f86bb5cb53e92c409cb4680a7fb9894" -"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" -"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -"checksum environmental 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" -"checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" -"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" -"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" -"checksum evm 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32a2c6961fdc9952371fc5f0416f03a9d90378a9dfb6862f6a7a9a3b8986b8dd" -"checksum evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bcde5af3d542874ddeb53de0919302d57586ea04b3f76f54d865f8a6cdc70ae" -"checksum evm-gasometer 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b82bc9f275cb59d2bcc05d85c98736ddfaba003a7ef7b73893fa7c1c1fab29dc" -"checksum evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbc89d29618c3722c17ba78ddf432d40ace8ee27e3f8b28b52a85921112e4b" -"checksum exit-future 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" -"checksum faerie 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f902f2af041f6c7177a2a04f805687cdc71e69c7cbef059a2755d8923f4cd7a8" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" -"checksum file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9" -"checksum finality-grandpa 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d9ad6bb0e42865b2d79fc9c8a08f22c39127310ed3334f2a1119ca25ed69dfb" -"checksum fixed-hash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72fe7539e2c5692c6989f2f9c0457e42f1e5768f96b85c87d273574670ae459f" -"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" -"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -"checksum fs-swap 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" -"checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" -"checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" -"checksum futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" -"checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" -"checksum futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" -"checksum futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" -"checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" -"checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" -"checksum futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" -"checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" -"checksum futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "878f1d2fc31355fa02ed2372e741b0c17e58373341e6a122569b4623a14a7d33" -"checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" -"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" -"checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" -"checksum futures_codec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a0a73299e4718f5452e45980fc1d6957a070abe308d3700b63b8673f47e1c2b3" -"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum get_if_addrs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7" -"checksum get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" -"checksum gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "162d18ae5f2e3b90a993d202f1ba17a5633c2484426f8bcae201f86194bacd00" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" -"checksum goblin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88a79ef1f0dad46fd78075b6f80f92d97710eddf87b3e18a15a66761e8942672" -"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" -"checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" -"checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" -"checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" -"checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" -"checksum hashbrown 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" -"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" -"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" -"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" -"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" -"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -"checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" -"checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" -"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" -"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" -"checksum http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" -"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -"checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" -"checksum hyper 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8bf49cfb32edee45d890537d9057d1b02ed55f53b7b6a30bae83a38c9231749e" -"checksum hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "719d85c7df4a7f309a77d145340a063ea929dcb2e025bae46a80345cffec2952" -"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum impl-codec 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" -"checksum impl-rlp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" -"checksum impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" -"checksum impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" -"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" -"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903" -"checksum interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" -"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum ipnet 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f4b06b21db0228860c8dfd17d2106c49c7c6bd07477a4036985347d84def04" -"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b1d42ef453b30b7387e113da1c83ab1605d90c5b4e0eb8e96d016ed3b8c160" -"checksum js-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "c40e7a4fafb6cf0be06d25662fc99aacb25f526eb6e1bc0c24100bde5d6a834e" -"checksum jsonrpc-client-transports 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9ae166c4d1f702d297cd76d4b55758ace80272ffc6dbb139fdc1bf810de40b" -"checksum jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fe3b688648f1ef5d5072229e2d672ecb92cbff7d1c79bcf3fd5898f3f3df0970" -"checksum jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "080dc110be17701097df238fad3c816d4a478a1899dfbcf8ec8957dd40ec7304" -"checksum jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8609af8f63b626e8e211f52441fcdb6ec54f1a446606b10d5c89ae9bf8a20058" -"checksum jsonrpc-http-server 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2d83d348120edee487c560b7cdd2565055d61cda053aa0d0ef0f8b6a18429048" -"checksum jsonrpc-pubsub 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3453625f0f0f5cd6d6776d389d73b7d70fcc98620b7cbb1cbbb1f6a36e95f39a" -"checksum jsonrpc-server-utils 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "95b7635e618a0edbbe0d2a2bbbc69874277c49383fcf6c3c0414491cfb517d22" -"checksum jsonrpc-ws-server 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b34faa167c3ac9705aeecb986c0da6056529f348425dbe0441db60a2c4cc41d1" -"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -"checksum keccak-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3468207deea1359a0e921591ae9b4c928733d94eb9d6a2eeda994cfd59f42cf8" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" -"checksum kvdb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8396be0e5561ccd1bf7ff0b2007487cdd7a87a056873fe6ea906b35d4dbf7ed8" -"checksum kvdb-memorydb 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d25ef14155e418515c4839e9144c839de3506e68946f255a32b7f166095493d" -"checksum kvdb-rocksdb 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "af488cc16c3801705c8d681c3a32c8faa8fafc7fb5309dee0f573f3c6a19d395" -"checksum kvdb-web 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37a0e36637fb86454de401e7cb88f40eb0ad1b9bcee837d46785e7c451f1ebf4" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" -"checksum libp2p 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0c85f850649e7533db125207d86bc2180faf7a962ea07686011031b71cbb3540" -"checksum libp2p-core 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "187ac3f588a74d48f163a8899fddafef4dc9796cad7b73ae754d9e928cebbbe7" -"checksum libp2p-core-derive 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b674da01855701636371b3d5f0e6b38bf5a74d283de705c1f66fab9980da8943" -"checksum libp2p-deflate 0.6.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f83cefe9f01a4a7012c1285e20fca4f193c46526a350679465150035afc5a97" -"checksum libp2p-dns 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67d5a6d0905479ee4c6bcfaeab597d506f3447e5eb09b84aff1ed5e6d834b221" -"checksum libp2p-floodsub 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "462f310a152433a94d4e9766aa8366e4770084f67b9a17289a9667a7b0780714" -"checksum libp2p-identify 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8269e7b673cf0fbce1cbfedf61708e860aeb4b2ad3ca2df54586f1e810b9fbc9" -"checksum libp2p-kad 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90fdc0027f76b50b0822f489f5b8a83664b71c6087e67e8e9e53e6c255b069b3" -"checksum libp2p-mdns 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be0b7cdeb0ad2c009099eaed258ef18b308c1e3f56d8f57668a868305f1c4caa" -"checksum libp2p-mplex 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "54c2ca42c7511fddc566af2259b81749d36011fefab1e99d9bf77bdc6210a5bd" -"checksum libp2p-noise 0.12.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8ff485bebd3181fc7f0c8ca7d11dde6231c132bbfb5d26c0e3a568e1a58b450" -"checksum libp2p-ping 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9061919a24a74760610c860e67cdb44ae4fd3cffe34c3b3a916f21ec68d9d50a" -"checksum libp2p-plaintext 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e9bf4b832590e0bf7efc09ff91c2d4cda0737f29b44356ef0cc3aecc08ed54a" -"checksum libp2p-secio 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2aa7599c5413cbc3de7138d64b932a26e86f8897ef3722f18840ed12ad54ea43" -"checksum libp2p-swarm 0.4.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea29f98b08c7a5113a787bce1a44d33fca6cf9f680f99812b50555e50baf408" -"checksum libp2p-tcp 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4e0d1f322101d81c26782e76c523043f96fe53fa41962fd2c2193a709985186" -"checksum libp2p-uds 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d5620aca9d9f85158b934be33a25a6310f1ca6a038392498eb7f35d35063aee9" -"checksum libp2p-wasm-ext 0.7.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dfe80dd0d2cb6412b57fffeb3db2dc36f7ea23566ed94011e0e07ff948f9624" -"checksum libp2p-websocket 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ac2a4974254a91f5291ef8493e6377380473334ba166ea0487e898364931dd61" -"checksum libp2p-yamux 0.14.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef3277eaffc7c3a0ad0c115dcbe3cbeb0e3a953a18b959d05081835fc2cf2ff9" -"checksum librocksdb-sys 6.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0785e816e1e11e7599388a492c61ef80ddc2afc91e313e61662cce537809be" -"checksum libsecp256k1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "df6edf84fd62aad1c93932b39324eaeda3912c1d26bc18dfaee6293848e49a50" -"checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" -"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum linked_hash_set 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7c91c4c7bbeb4f2f7c4e5be11e6a05bd6830bc37249c47ce1ad86ad453ff9c" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" -"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum lru 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8f669d42c72d18514dfca8115689c5f6370a17d980cb5bd777a67f404594c8" -"checksum lru 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0609345ddee5badacf857d4f547e0e5a2e987db77085c24cd887f73573a04237" -"checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" -"checksum malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" -"checksum memory-db 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "881736a0f68a6fae1b596bb066c5bd16d7b3ed645a4dd8ffaefd02f585abaf71" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum merlin 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" -"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" -"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" -"checksum mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum more-asserts 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" -"checksum multimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb04b9f127583ed176e163fb9ec6f3e793b87e21deedd5734a69386a18a0151" -"checksum multistream-select 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f938ffe420493e77c8b6cbcc3f282283f68fc889c5dcbc8e51668d5f3a01ad94" -"checksum names 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef320dab323286b50fb5cdda23f61c796a72a89998ab565ca32525c5c556f2da" -"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum nohash-hasher 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4e657a6ec97f9a3ba46f6f7034ea6db9fcd5b71d25ef1074b7bc03da49be0e8e" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"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-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" -"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" -"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" -"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" -"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" -"checksum once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d584f08c2d717d5c23a6414fc2822b71c651560713e54fa7eace675f758a355e" -"checksum once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" -"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" -"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" -"checksum parity-bytes 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0c276d76c5333b8c2579e02d49a06733a55b8282d2d9b13e8d53b6406bd7e30a" -"checksum parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "045b3c7af871285146300da35b1932bb6e4639b66c7c98e85d06a32cbc4e8fa7" -"checksum parity-multiaddr 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6de20a133b50f5120c6b8284ee88c5017fb167149208b3ee2e95f8719a434dc4" -"checksum parity-multihash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df3a17dc27848fd99e4f87eb0f8c9baba6ede0a6d555400c850ca45254ef4ce3" -"checksum parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70a4d7b05e51bff5ae2c29c7b8c3d889985bbd8f4e15b3542fcc1f6f9666d292" -"checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507" -"checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476" -"checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" -"checksum parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8174d85e62c4d615fddd1ef67966bdc5757528891d0742f15b131ad04667b3f9" -"checksum parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "900dd84654b048e5bad420bb341658fc2c4d7fea628c22bcf4621733e54859b4" -"checksum parity-util-mem-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" -"checksum parity-wasm 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16ad52817c4d343339b3bc2e26861bd21478eda0b7509acf83505727000512ac" -"checksum parity-wasm 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" -"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" -"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" -"checksum pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "94b90146c7216e4cb534069fb91366de4ea0ea353105ee45ed297e2d1619e469" -"checksum pin-project-internal 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "44ca92f893f0656d3cba8158dd0f2b99b94de256a4a54e870bd6922fcc6c8355" -"checksum pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f0af6cbca0e6e3ce8692ee19fb8d734b641899e07b68eb73e9bbbd32f1703991" -"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" -"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" -"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" -"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-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" -"checksum proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"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 prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96d14b1c185652833d24aaad41c5832b0be5616a590227c1fbff57c616754b23" -"checksum prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" -"checksum prost-build 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb788126ea840817128183f8f603dce02cb7aea25c2a0b764359d8e20010702e" -"checksum prost-derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e7dc378b94ac374644181a2247cebf59a6ec1c88b49ac77f3a94b86b79d0e11" -"checksum prost-derive 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" -"checksum prost-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1de482a366941c8d56d19b650fac09ca08508f2a696119ee7513ad590c8bac6f" -"checksum protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40361836defdd5871ff7e84096c6f6444af7fc157f8ef1789f54f147687caa20" -"checksum pwasm-utils 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5ca504a2fdaa08d3517f442fbbba91ac24d1ec4c51ea68688a038765e3b2662" -"checksum quicksink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8461ef7445f61fd72d8dcd0629ce724b9131b3c2eb36e83a5d3d4161c127530" -"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.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" -"checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" -"checksum raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" -"checksum rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43739f8831493b276363637423d3622d4bd6394ab6f0a9c4a552e208aeb7fddd" -"checksum rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8bf17de6f23b05473c437eb958b9c850bfc8af0961fe17b4cc92d5a627b4791" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" -"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" -"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" -"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" -"checksum region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "448e868c6e4cfddfa49b6a72c95906c04e8547465e9536575b95c70a4044f856" -"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac" -"checksum rlp 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3a44d5ae8afcb238af8b75640907edc6c931efcfab2c854e81ed35fa080f84cd" -"checksum rocksdb 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12069b106981c6103d3eab7dd1c86751482d0779a520b7c14954c8b586c1e643" -"checksum rpassword 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d59f0e97173c514b9036cd450c195a6483ba81055c6fa0f1bff3ab563f47d44a" -"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" -"checksum rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c48f91977f4ef3be5358c15d131d3f663f6b4d7a112555bf3bf52ad23b6659e5" -"checksum rw-stream-sink 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "761d4727649dc004ee5555a0779afd53963efafd2218c969a2c5e323cdf73a09" -"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" -"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" -"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" -"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" -"checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" -"checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" -"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" -"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" -"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" -"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" -"checksum send_wrapper 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "686ef91cf020ad8d4aca9a7047641fd6add626b7b89e14546c2b6a76781cf822" -"checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" -"checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" -"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" -"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" -"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" -"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" -"checksum slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" -"checksum slog-scope 4.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c44c89dd8b0ae4537d1ae318353eaf7840b4869c536e31c41e963d1ea523ee6" -"checksum slog_derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a945ec7f7ce853e89ffa36be1e27dce9a43e82ff9093bf3461c30d5da74ed11b" -"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" -"checksum snow 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afb767eee7d257ba202f0b9b08673bc13b22281632ef45267b19f13100accd2f" -"checksum soketto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3caa0ad6b765419f21e4cfa490ec7514a9fae4af986adef168a69477ba528671" -"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" -"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -"checksum string-interner 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "884ae79d6aad1e738f4a70dff314203fd498490a63ebc4d03ea83323c40b7b72" -"checksum structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a97f829a34a0a9d5b353a881025a23b8c9fd09d46be6045df6b22920dbd7a93" -"checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" -"checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" -"checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" -"checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" -"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -"checksum sysinfo 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4b2468c629cffba39c0a4425849ab3cdb03d9dfacba69684609aea04d08ff9" -"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" -"checksum target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4c118a7a38378f305a9e111fcb2f7f838c0be324bfb31a77ea04f7f6e684b4" -"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" -"checksum test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a605baa797821796a751f4a959e1206079b24a4b7e1ed302b7d785d81a9276c9" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" -"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" -"checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" -"checksum tiny-keccak 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" -"checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bcced6bb623d4bff3739c176c415f13c418f426395c169c9c3cd9a492c715b16" -"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ca6df436c42b0c3330a82d855d2ef017cd793090ad550a6bc2184f4b933532ab" -"checksum tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee9ceecf69145923834ea73f32ba40c790fd877b74a7817dd0b089f1eb9c7c8" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146" -"checksum tokio-rustls 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1df2fa53ac211c136832f530ccb081af9af891af22d685a9493e232c7a359bc2" -"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" -"checksum tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f1aaeb685540f7407ea0e27f1c9757d258c7c6bf4e3eb19da6fc59b747239d2" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c32ffea4827978e9aa392d2f743d973c1dfa3730a2ed3f22ce1e6984da848c" -"checksum tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1739638e364e558128461fc1ad84d997702c8e31c2e6b18fb99842268199e827" -"checksum tokio-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" -"checksum tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" -"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" -"checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" -"checksum tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ff4e4f59e752cb3beb5b61c6d5e11191c7946231ba84faec2902c9efdd8691c5" -"checksum tracing-attributes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a4263b12c3d3c403274493eb805966093b53214124796552d674ca1dd5d27c2b" -"checksum tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bc913647c520c959b6d21e35ed8fa6984971deca9f0a2fcb8c51207e0c56af1d" -"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" -"checksum trie-bench 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26fd042d57ee9c987c562811162a78db78b5340ab674ac76056c85dca49b26bc" -"checksum trie-db 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d747ae5b6f078df7e46477fcc7df66df9eb4f27a031cf4a7c890a8dd03d8e6" -"checksum trie-root 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b779f7c1c8fe9276365d9d5be5c4b5adeacf545117bb3f64c974305789c5c0b" -"checksum trie-standardmap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3161ba520ab28cd8e6b68e1126f1009f6e335339d1a73b978139011703264c8" -"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum trybuild 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "b75e31d624df08744532e935f1d4bfedd319a277d5a162c5b15f6ced59307575" -"checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -"checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" -"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum uint 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" -"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" -"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" -"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 unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f0023a96687fe169081e8adce3f65e3874426b7886e9234d490af2dc077959" -"checksum unsigned-varint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c689459fbaeb50e56c6749275f084decfd02194ac5852e6617d95d0d3cf02eaf" -"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" -"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" -"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693" -"checksum wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08" -"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" -"checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" -"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum wasm-bindgen 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "701bc20794a7f9e8dcd85984a848f951ef6c5083322b6dd17fe880c99390f7cd" -"checksum wasm-bindgen-backend 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "426315280d373e1a828e1c322507d51aa82e03c2fb1d654720b9e2f2368d291d" -"checksum wasm-bindgen-futures 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1de54efe80cb87a8fa1f715d60ab47a5eac6b1447dd68665300773f498c229b1" -"checksum wasm-bindgen-macro 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "d9a8a46373db32c892de910ccca302ecdaf8262abab746324a8a4dac352fc11f" -"checksum wasm-bindgen-macro-support 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "45d1c6c2259c0f4ef3d61ea0976ea075b6f8185497c4a5457793740c2cda6430" -"checksum wasm-bindgen-shared 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "dd8e108ae395aae8017b091c4766101f08c635a5074e6631e0085e8a839e5810" -"checksum wasm-bindgen-webidl 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "7a38859ace1c29c8ed75cd74940d4c96b980837179355de542a2eab3b435bb5c" -"checksum wasm-gc-api 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c32691b6c7e6c14e7f8fd55361a9088b507aa49620fcd06c09b3a1082186b9" -"checksum wasm-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" -"checksum wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bf617d864d25af3587aa745529f7aaa541066c876d57e050c0d0c85c61c92aff" -"checksum wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" -"checksum wasmparser 0.39.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c702914acda5feeeffbc29e4d953e5b9ce79d8b98da4dbf18a77086e116c5470" -"checksum wasmtime-debug 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5008729ad53f75020f28fa0d682269335d6f0eac0b3ffafe31f185b2f33aca74" -"checksum wasmtime-environ 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3947662a0b8e05b1418465e64f16de9114f9fec18cc3f56e0ed5aa7737b89d0" -"checksum wasmtime-jit 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ed7922689461a7b5bd0d9c7350cac526c8a520a23b3ffd7f5b446ac51dfc51f" -"checksum wasmtime-runtime 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "781d6bb8b346efaa3dc39746386957cd79b8d841e8652ed9b02d77bcf64fb514" -"checksum web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ba09295448c0b93bc87d2769614d371a924749e5e6c87e4c1df8b2416b49b775" -"checksum webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7e664e770ac0110e2384769bcc59ed19e329d81f555916a6e072714957b81b4" -"checksum webpki-roots 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a262ae37dd9d60f60dd473d1158f9fbebf110ba7b6a5051c8160460f6043718b" -"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" -"checksum websocket 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "413b37840b9e27b340ce91b319ede10731de8c72f5bc4cb0206ec1ca4ce581d0" -"checksum websocket-base 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e3810f0d00c4dccb54c30a4eee815e703232819dec7b007db115791c42aa374" -"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" -"checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" -"checksum ws 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" -"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" -"checksum yamux 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "809f4388471d280173404e3d4f889e2d36004960a37d822ce5637fbef33a0ce4" -"checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" -"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" -"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" -"checksum zstd 0.5.1+zstd.1.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c5d978b793ae64375b80baf652919b148f6a496ac8802922d9999f5a553194f" -"checksum zstd-safe 2.0.3+zstd.1.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bee25eac9753cfedd48133fa1736cbd23b774e253d89badbeac7d12b23848d3f" -"checksum zstd-sys 1.4.15+zstd.1.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "89719b034dc22d240d5b407fb0a3fe6d29952c181cff9a9f95c0bd40b4f8f7d8" + "cc", + "glob 0.3.0", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index f6e521b9c5dbb6d1b5bb6798e1cafbcf4e9e3e2e..a42a8e24d0f48e5412d18b93a2aa5ade5f5b0737 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,12 @@ members = [ "client/cli", "client/consensus/aura", "client/consensus/babe", + "client/consensus/babe/rpc", + "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 +36,7 @@ members = [ "client/executor/wasmtime", "client/executor/runtime-test", "client/finality-grandpa", + "client/informant", "client/tracing", "client/keystore", "client/network", @@ -98,6 +103,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", @@ -145,6 +152,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..5466f3e919c0baa4bed2b83622fdf35d5987e5b8 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,7 @@ 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) + Ok(sc_transaction_pool::BasicPool::new(config, std::sync::Arc::new(pool_api))) })? .with_import_queue(|_config, client, mut select_chain, transaction_pool| { let select_chain = select_chain.take() @@ -80,7 +76,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 +129,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 +170,11 @@ pub fn new_full(config: Configuration { grandpa::setup_disabled_grandpa( @@ -195,7 +189,7 @@ pub fn new_full(config: Configuration(config: Configuration) +pub fn new_light(config: Configuration) -> Result { let inherent_data_providers = InherentDataProviders::new(); @@ -207,11 +201,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 function, from our runtime functions + 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..ec123a50c7cc984f15c8f80983ed300d95ecdccf --- /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 function `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 5d737f66e894fd16e5d5d76a46849536cadf9ba0..48c5fcb3fd44dcde095ce59cf72d96c2715f598d 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,13 +66,10 @@ 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 -/// to even the core datastructures. +/// to even the core data structures. pub mod opaque { use super::*; @@ -109,7 +109,7 @@ pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; -/// The version infromation used to identify this runtime when compiled natively. +/// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { @@ -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! { @@ -236,7 +237,7 @@ impl transaction_payment::Trait for Runtime { impl sudo::Trait for Runtime { type Event = Event; - type Proposal = Call; + type Call = Call; } /// Used for the module template in `./template.rs` @@ -250,14 +251,14 @@ 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, + Sudo: sudo::{Module, Call, Config, Storage, Event}, // Used for the module template in `./template.rs` TemplateModule: template::{Module, Call, Storage, Event}, RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage}, @@ -316,6 +317,10 @@ impl_runtime_apis! { Executive::apply_extrinsic(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_trusted_extrinsic(extrinsic) + } + fn finalize_block() -> ::Header { Executive::finalize_block() } @@ -362,6 +367,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..079e8d13e2d690eba49701253cfddc7efa29b8fb 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -5,8 +5,14 @@ authors = ["Parity Technologies "] description = "Substrate node implementation in Rust." build = "build.rs" edition = "2018" +license = "GPL-3.0" default-run = "substrate" +[package.metadata.wasm-pack.profile.release] +# `wasm-opt` has some problems on linux, see +# https://github.com/rustwasm/wasm-pack/issues/781 etc. +wasm-opt = false + [badges] travis-ci = { repository = "paritytech/substrate", branch = "master" } maintenance = { status = "actively-developed" } @@ -30,7 +36,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 +66,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,10 +88,9 @@ 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" } +node-inspect = { version = "0.8.0", optional = true, path = "../inspect" } # WASM-specific dependencies wasm-bindgen = { version = "0.2.57", optional = true } @@ -93,15 +100,28 @@ 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" } +node-inspect = { version = "0.8.0", optional = true, path = "../inspect" } + +[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"] @@ -111,12 +131,13 @@ browser = [ "wasm-bindgen-futures", ] cli = [ - "sc-cli", + "node-executor/wasmi-errno", + "node-inspect", "node-transaction-factory", - "tokio", - "ctrlc", + "sc-cli", "sc-service/rocksdb", - "node-executor/wasmi-errno", + "structopt", + "vergen", ] 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..7dcd02699d664c9c44d31cc2152e3f208007a277 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. +/// An overarching CLI command definition. #[derive(Clone, Debug, StructOpt)] -pub enum CustomSubcommands { +#[structopt(settings = &[ + structopt::clap::AppSettings::GlobalVersion, + structopt::clap::AppSettings::ArgsNegateSubcommands, + structopt::clap::AppSettings::SubcommandsNegateReqs, +])] +pub struct Cli { + /// Possible subcommand with parameters. + #[structopt(subcommand)] + pub subcommand: Option, + #[allow(missing_docs)] + #[structopt(flatten)] + pub run: RunCmd, +} + +/// Possible subcommands of the main binary. +#[derive(Clone, Debug, StructOpt)] +pub enum Subcommand { + /// A set of base subcommands handled by `sc_cli`. + #[structopt(flatten)] + Base(sc_cli::Subcommand), /// The custom factory subcommmand for manufacturing transactions. #[structopt( name = "factory", @@ -36,48 +46,26 @@ pub enum CustomSubcommands { Only supported for development or local testnet." )] Factory(FactoryCmd), -} -impl GetSharedParams for CustomSubcommands { - fn shared_params(&self) -> Option<&SharedParams> { - match self { - CustomSubcommands::Factory(cmd) => Some(&cmd.shared_params), - } - } + /// The custom inspect subcommmand for decoding blocks and extrinsics. + #[structopt( + name = "inspect", + about = "Decode given block or extrinsic using current native runtime." + )] + Inspect(node_inspect::cli::InspectCmd), } /// 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 +75,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..ba0f2785c1f00ba25241353cf0f2eb2fc0b92e69 --- /dev/null +++ b/bin/node/cli/src/command.rs @@ -0,0 +1,102 @@ +// 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::Inspect(cmd)) => { + cmd.init(&mut config, load_spec, &version)?; + + let client = sc_service::new_full_client::< + node_runtime::Block,node_runtime::RuntimeApi, node_executor::Executor, _, _, + >(&config)?; + let inspect = node_inspect::Inspector::::new(client); + + cmd.run(inspect) + }, + 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..789d6a6913fc9a4226fa56a81ec773d5c8d32f10 100644 --- a/bin/node/cli/src/lib.rs +++ b/bin/node/cli/src/lib.rs @@ -27,7 +27,6 @@ //! hasn't been tested. #![warn(missing_docs)] -#![warn(unused_extern_crates)] pub mod chain_spec; @@ -39,11 +38,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..a882483a44a3dd8411eb6892a71e0cb50a506fbd 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -63,10 +63,7 @@ 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) + Ok(sc_transaction_pool::BasicPool::new(config, std::sync::Arc::new(pool_api))) })? .with_import_queue(|_config, client, mut select_chain, _transaction_pool| { let select_chain = select_chain.take() @@ -98,8 +95,21 @@ macro_rules! new_full_start { import_setup = Some((block_import, grandpa_link, babe_link)); Ok(import_queue) })? - .with_rpc_extensions(|client, pool, _backend, fetcher, _remote_blockchain| -> Result { - Ok(node_rpc::create(client, pool, node_rpc::LightDeps::none(fetcher))) + .with_rpc_extensions(|builder| -> Result { + let babe_link = import_setup.as_ref().map(|s| &s.2) + .expect("BabeLink is present for full services or set up failed; qed."); + let deps = node_rpc::FullDeps { + client: builder.client().clone(), + pool: builder.pool(), + select_chain: builder.select_chain().cloned() + .expect("SelectChain is present for full services or set up failed; qed."), + babe: node_rpc::BabeDeps { + keystore: builder.keystore(), + babe_config: sc_consensus_babe::BabeLink::config(babe_link).clone(), + shared_epoch_changes: sc_consensus_babe::BabeLink::epoch_changes(babe_link).clone() + } + }; + Ok(node_rpc::create_full(deps)) })?; (builder, import_setup, inherent_data_providers) @@ -112,10 +122,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 +184,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 +199,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 +223,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 +240,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 +278,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 +309,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 +322,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 @@ -365,14 +365,21 @@ pub fn new_light(config: NodeConfiguration) .with_finality_proof_provider(|client, backend| Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _) )? - .with_rpc_extensions(|client, pool, _backend, fetcher, remote_blockchain| -> Result { - let fetcher = fetcher + .with_rpc_extensions(|builder,| -> + Result + { + let fetcher = builder.fetcher() .ok_or_else(|| "Trying to start node RPC without active fetcher")?; - let remote_blockchain = remote_blockchain + let remote_blockchain = builder.remote_backend() .ok_or_else(|| "Trying to start node RPC without active remote blockchain")?; - let light_deps = node_rpc::LightDeps { remote_blockchain, fetcher }; - Ok(node_rpc::create(client, pool, Some(light_deps))) + let light_deps = node_rpc::LightDeps { + remote_blockchain, + fetcher, + client: builder.client().clone(), + pool: builder.pool(), + }; + Ok(node_rpc::create_light(light_deps)) })? .build()?; @@ -381,8 +388,11 @@ pub fn new_light(config: NodeConfiguration) #[cfg(test)] mod tests { - use std::sync::Arc; - use sc_consensus_babe::CompatibleDigestItem; + use std::{sync::Arc, 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 +404,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 +519,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, @@ -556,19 +576,14 @@ mod tests { ); slot_num += 1; - let params = BlockImportParams { - origin: BlockOrigin::File, - header: new_header, - justification: None, - post_digests: vec![item], - body: Some(new_body), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; + let mut params = BlockImportParams::new(BlockOrigin::File, new_header); + params.post_digests.push(item); + params.body = Some(new_body); + params.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + params.fork_choice = Some(ForkChoiceStrategy::LongestChain); block_import.import_block(params, Default::default()) .expect("error importing test block"); 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..4044f69d08168e4dda1d526ff9b1c4e5efbd32b8 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 number 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..dbb57bdd21ab8de8add8f4faf4057aea2157fdaf --- /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 process 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 9b0b49ef815bede8a0b91949ac23011c6ac0a4f8..90e64c41369ee9f110f87978b380f381794ad751 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/inspect/Cargo.toml b/bin/node/inspect/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..cbce7e4589698b75e7c0c0c01cb48c896628cef2 --- /dev/null +++ b/bin/node/inspect/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "node-inspect" +version = "0.8.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0" } +derive_more = "0.99" +log = "0.4.8" +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sc-client-api = { version = "2.0.0", path = "../../../client/api" } +sc-service = { version = "0.8", default-features = false, path = "../../../client/service" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +structopt = "0.3.8" diff --git a/bin/node/inspect/src/cli.rs b/bin/node/inspect/src/cli.rs new file mode 100644 index 0000000000000000000000000000000000000000..27afcfff919ee276220f20871a0140f6c2aa326c --- /dev/null +++ b/bin/node/inspect/src/cli.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 . + +//! Structs to easily compose inspect sub-command for CLI. + +use std::{ + fmt::Debug, + str::FromStr, +}; +use crate::{Inspector, PrettyPrinter}; +use sc_cli::{ImportParams, SharedParams, error}; +use structopt::StructOpt; + +/// The `inspect` command used to print decoded chain data. +#[derive(Debug, StructOpt, Clone)] +pub struct InspectCmd { + #[allow(missing_docs)] + #[structopt(flatten)] + pub command: InspectSubCmd, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub import_params: ImportParams, +} + +/// A possible inspect sub-commands. +#[derive(Debug, StructOpt, Clone)] +pub enum InspectSubCmd { + /// Decode block with native version of runtime and print out the details. + Block { + /// Address of the block to print out. + /// + /// Can be either a block hash (no 0x prefix) or a number to retrieve existing block, + /// or a 0x-prefixed bytes hex string, representing SCALE encoding of + /// a block. + #[structopt(value_name = "HASH or NUMBER or BYTES")] + input: String, + }, + /// Decode extrinsic with native version of runtime and print out the details. + Extrinsic { + /// Address of an extrinsic to print out. + /// + /// Can be either a block hash (no 0x prefix) or number and the index, in the form + /// of `{block}:{index}` or a 0x-prefixed bytes hex string, + /// representing SCALE encoding of an extrinsic. + #[structopt(value_name = "BLOCK:INDEX or BYTES")] + input: String, + }, +} + +impl InspectCmd { + /// Parse CLI arguments and initialize given config. + pub fn init( + &self, + config: &mut sc_service::config::Configuration, + spec_factory: impl FnOnce(&str) -> Result>, String>, + version: &sc_cli::VersionInfo, + ) -> error::Result<()> where + G: sc_service::RuntimeGenesis, + E: sc_service::ChainSpecExtension, + { + sc_cli::init_config(config, &self.shared_params, version, spec_factory)?; + // make sure to configure keystore + sc_cli::fill_config_keystore_in_memory(config)?; + // and all import params (especially pruning that has to match db meta) + sc_cli::fill_import_params( + config, + &self.import_params, + sc_service::Roles::FULL, + self.shared_params.dev, + )?; + Ok(()) + } + + /// Run the inspect command, passing the inspector. + pub fn run( + self, + inspect: Inspector, + ) -> error::Result<()> where + B: sp_runtime::traits::Block, + B::Hash: FromStr, + P: PrettyPrinter, + { + match self.command { + InspectSubCmd::Block { input } => { + let input = input.parse()?; + let res = inspect.block(input) + .map_err(|e| format!("{}", e))?; + println!("{}", res); + Ok(()) + }, + InspectSubCmd::Extrinsic { input } => { + let input = input.parse()?; + let res = inspect.extrinsic(input) + .map_err(|e| format!("{}", e))?; + println!("{}", res); + Ok(()) + }, + } + } +} + + +/// A block to retrieve. +#[derive(Debug, Clone, PartialEq)] +pub enum BlockAddress { + /// Get block by hash. + Hash(Hash), + /// Get block by number. + Number(Number), + /// Raw SCALE-encoded bytes. + Bytes(Vec), +} + +impl FromStr for BlockAddress { + type Err = String; + + fn from_str(s: &str) -> Result { + // try to parse hash first + if let Ok(hash) = s.parse() { + return Ok(Self::Hash(hash)) + } + + // then number + if let Ok(number) = s.parse() { + return Ok(Self::Number(number)) + } + + // then assume it's bytes (hex-encoded) + sp_core::bytes::from_hex(s) + .map(Self::Bytes) + .map_err(|e| format!( + "Given string does not look like hash or number. It could not be parsed as bytes either: {}", + e + )) + } +} + +/// An extrinsic address to decode and print out. +#[derive(Debug, Clone, PartialEq)] +pub enum ExtrinsicAddress { + /// Extrinsic as part of existing block. + Block(BlockAddress, usize), + /// Raw SCALE-encoded extrinsic bytes. + Bytes(Vec), +} + +impl FromStr for ExtrinsicAddress { + type Err = String; + + fn from_str(s: &str) -> Result { + // first try raw bytes + if let Ok(bytes) = sp_core::bytes::from_hex(s).map(Self::Bytes) { + return Ok(bytes) + } + + // split by a bunch of different characters + let mut it = s.split(|c| c == '.' || c == ':' || c == ' '); + let block = it.next() + .expect("First element of split iterator is never empty; qed") + .parse()?; + + let index = it.next() + .ok_or_else(|| format!("Extrinsic index missing: example \"5:0\""))? + .parse() + .map_err(|e| format!("Invalid index format: {}", e))?; + + Ok(Self::Block(block, index)) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::hash::H160 as Hash; + + #[test] + fn should_parse_block_strings() { + type BlockAddress = super::BlockAddress; + + let b0 = BlockAddress::from_str("3BfC20f0B9aFcAcE800D73D2191166FF16540258"); + let b1 = BlockAddress::from_str("1234"); + let b2 = BlockAddress::from_str("0"); + let b3 = BlockAddress::from_str("0x0012345f"); + + + assert_eq!(b0, Ok(BlockAddress::Hash( + "3BfC20f0B9aFcAcE800D73D2191166FF16540258".parse().unwrap() + ))); + assert_eq!(b1, Ok(BlockAddress::Number(1234))); + assert_eq!(b2, Ok(BlockAddress::Number(0))); + assert_eq!(b3, Ok(BlockAddress::Bytes(vec![0, 0x12, 0x34, 0x5f]))); + } + + #[test] + fn should_parse_extrinsic_address() { + type BlockAddress = super::BlockAddress; + type ExtrinsicAddress = super::ExtrinsicAddress; + + let e0 = ExtrinsicAddress::from_str("1234"); + let b0 = ExtrinsicAddress::from_str("3BfC20f0B9aFcAcE800D73D2191166FF16540258:5"); + let b1 = ExtrinsicAddress::from_str("1234:0"); + let b2 = ExtrinsicAddress::from_str("0 0"); + let b3 = ExtrinsicAddress::from_str("0x0012345f"); + + + assert_eq!(e0, Err("Extrinsic index missing: example \"5:0\"".into())); + assert_eq!(b0, Ok(ExtrinsicAddress::Block( + BlockAddress::Hash("3BfC20f0B9aFcAcE800D73D2191166FF16540258".parse().unwrap()), + 5 + ))); + assert_eq!(b1, Ok(ExtrinsicAddress::Block( + BlockAddress::Number(1234), + 0 + ))); + assert_eq!(b2, Ok(ExtrinsicAddress::Block( + BlockAddress::Number(0), + 0 + ))); + assert_eq!(b3, Ok(ExtrinsicAddress::Bytes(vec![0, 0x12, 0x34, 0x5f]))); + } +} diff --git a/bin/node/inspect/src/lib.rs b/bin/node/inspect/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5c4e18c0a74a643b4625b616b61c284281171e74 --- /dev/null +++ b/bin/node/inspect/src/lib.rs @@ -0,0 +1,204 @@ +// 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 CLI extension for substrate node, adding sub-command to pretty print debug info +//! about blocks and extrinsics. +//! +//! The blocks and extrinsics can either be retrieved from the database (on-chain), +//! or a raw SCALE-encoding can be provided. + +#![warn(missing_docs)] + +pub mod cli; + +use std::{ + fmt, + marker::PhantomData +}; +use codec::{Encode, Decode}; +use sc_client_api::BlockBody; +use sp_blockchain::HeaderBackend; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::{ + generic::BlockId, + traits::{Block, HashFor, NumberFor, Hash} +}; + +/// A helper type for a generic block input. +pub type BlockAddressFor = cli::BlockAddress< + as Hash>::Output, + NumberFor +>; + +/// A Pretty formatter implementation. +pub trait PrettyPrinter { + /// Nicely format block. + fn fmt_block(&self, fmt: &mut fmt::Formatter, block: &TBlock) -> fmt::Result; + /// Nicely format extrinsic. + fn fmt_extrinsic(&self, fmt: &mut fmt::Formatter, extrinsic: &TBlock::Extrinsic) -> fmt::Result; +} + +/// Default dummy debug printer. +#[derive(Default)] +pub struct DebugPrinter; +impl PrettyPrinter for DebugPrinter { + fn fmt_block(&self, fmt: &mut fmt::Formatter, block: &TBlock) -> fmt::Result { + writeln!(fmt, "Header:")?; + writeln!(fmt, "{:?}", block.header())?; + writeln!(fmt, "Block bytes: {:?}", HexDisplay::from(&block.encode()))?; + writeln!(fmt, "Extrinsics ({})", block.extrinsics().len())?; + for (idx, ex) in block.extrinsics().iter().enumerate() { + writeln!(fmt, "- {}:", idx)?; + >::fmt_extrinsic(self, fmt, ex)?; + } + Ok(()) + } + + fn fmt_extrinsic(&self, fmt: &mut fmt::Formatter, extrinsic: &TBlock::Extrinsic) -> fmt::Result { + writeln!(fmt, " {:?}", extrinsic)?; + writeln!(fmt, " Bytes: {:?}", HexDisplay::from(&extrinsic.encode()))?; + Ok(()) + } +} + +/// Aggregated error for `Inspector` operations. +#[derive(Debug, derive_more::From, derive_more::Display)] +pub enum Error { + /// Could not decode Block or Extrinsic. + Codec(codec::Error), + /// Error accessing blockchain DB. + Blockchain(sp_blockchain::Error), + /// Given block has not been found. + NotFound(String), +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::Codec(ref e) => Some(e), + Self::Blockchain(ref e) => Some(e), + Self::NotFound(_) => None, + } + } +} + +/// A helper trait to access block headers and bodies. +pub trait ChainAccess: + HeaderBackend + + BlockBody +{} + +impl ChainAccess for T where + TBlock: Block, + T: sp_blockchain::HeaderBackend + sc_client_api::BlockBody, +{} + +/// Blockchain inspector. +pub struct Inspector = DebugPrinter> { + printer: TPrinter, + chain: Box>, + _block: PhantomData, +} + +impl> Inspector { + /// Create new instance of the inspector with default printer. + pub fn new( + chain: impl ChainAccess + 'static, + ) -> Self where TPrinter: Default { + Self::with_printer(chain, Default::default()) + } + + /// Customize pretty-printing of the data. + pub fn with_printer( + chain: impl ChainAccess + 'static, + printer: TPrinter, + ) -> Self { + Inspector { + chain: Box::new(chain) as _, + printer, + _block: Default::default(), + } + } + + /// Get a pretty-printed block. + pub fn block(&self, input: BlockAddressFor) -> Result { + struct BlockPrinter<'a, A, B>(A, &'a B); + impl<'a, A: Block, B: PrettyPrinter> fmt::Display for BlockPrinter<'a, A, B> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.1.fmt_block(fmt, &self.0) + } + } + + let block = self.get_block(input)?; + Ok(format!("{}", BlockPrinter(block, &self.printer))) + } + + fn get_block(&self, input: BlockAddressFor) -> Result { + Ok(match input { + cli::BlockAddress::Bytes(bytes) => { + TBlock::decode(&mut &*bytes)? + }, + cli::BlockAddress::Number(number) => { + let id = BlockId::number(number); + let not_found = format!("Could not find block {:?}", id); + let body = self.chain.block_body(&id)? + .ok_or_else(|| Error::NotFound(not_found.clone()))?; + let header = self.chain.header(id)? + .ok_or_else(|| Error::NotFound(not_found.clone()))?; + TBlock::new(header, body) + }, + cli::BlockAddress::Hash(hash) => { + let id = BlockId::hash(hash); + let not_found = format!("Could not find block {:?}", id); + let body = self.chain.block_body(&id)? + .ok_or_else(|| Error::NotFound(not_found.clone()))?; + let header = self.chain.header(id)? + .ok_or_else(|| Error::NotFound(not_found.clone()))?; + TBlock::new(header, body) + }, + }) + } + + /// Get a pretty-printed extrinsic. + pub fn extrinsic( + &self, + input: cli::ExtrinsicAddress< as Hash>::Output, NumberFor>, + ) -> Result { + struct ExtrinsicPrinter<'a, A: Block, B>(A::Extrinsic, &'a B); + impl<'a, A: Block, B: PrettyPrinter> fmt::Display for ExtrinsicPrinter<'a, A, B> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.1.fmt_extrinsic(fmt, &self.0) + } + } + + let ext = match input { + cli::ExtrinsicAddress::Block(block, index) => { + let block = self.get_block(block)?; + block.extrinsics() + .get(index) + .cloned() + .ok_or_else(|| Error::NotFound(format!( + "Could not find extrinsic {} in block {:?}", index, block + )))? + }, + cli::ExtrinsicAddress::Bytes(bytes) => { + TBlock::Extrinsic::decode(&mut &*bytes)? + } + }; + + Ok(format!("{}", ExtrinsicPrinter(ext, &self.printer))) + } +} 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-client/src/main.rs b/bin/node/rpc-client/src/main.rs index b28c9f8ff63922601934f90569bbd79028aa5b44..c547d30002d795b07ffc9468027bf1c06f17b877 100644 --- a/bin/node/rpc-client/src/main.rs +++ b/bin/node/rpc-client/src/main.rs @@ -19,7 +19,7 @@ //! Example substrate RPC client code. //! //! This module shows how you can write a Rust RPC client that connects to a running -//! substrate node and use staticly typed RPC wrappers. +//! substrate node and use statically typed RPC wrappers. use futures::Future; use hyper::rt; @@ -55,7 +55,7 @@ fn main() { /// 1. Calls the `pending_extrinsics` method to get all extrinsics in the pool. /// 2. Then calls `remove_extrinsic` passing the obtained raw extrinsics. /// -/// As the resul of running the code the entire content of the transaction pool is going +/// As the result of running the code the entire content of the transaction pool is going /// to be removed and the extrinsics are going to be temporarily banned. fn remove_all_extrinsics(client: AuthorClient) -> impl Future { client.pending_extrinsics() diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 174b2875166c2940720844d51a75f9617da26843..aefc9222f781da744c647e0969a337f9950b5d0d 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/" } @@ -11,7 +12,14 @@ node-primitives = { version = "2.0.0", path = "../primitives" } node-runtime = { version = "2.0.0", path = "../runtime" } sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } sp-api = { version = "2.0.0", path = "../../../primitives/api" } -pallet-contracts-rpc = { version = "0.8.0", path = "../../../frame/contracts/rpc/" } +pallet-contracts-rpc = { version = "0.8", path = "../../../frame/contracts/rpc/" } pallet-transaction-payment-rpc = { version = "2.0.0", path = "../../../frame/transaction-payment/rpc/" } substrate-frame-rpc-system = { version = "2.0.0", path = "../../../utils/frame/rpc/system" } sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +sc-consensus-babe = { version = "0.8", path = "../../../client/consensus/babe" } +sc-consensus-babe-rpc = { version = "0.8", path = "../../../client/consensus/babe/rpc" } +sp-consensus-babe = { version = "0.8", path = "../../../primitives/consensus/babe" } +sc-keystore = { version = "2.0.0", path = "../../../client/keystore" } +sc-consensus-epochs = { version = "0.8", path = "../../../client/consensus/epochs" } +sp-consensus = { version = "0.8", path = "../../../primitives/consensus/common" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index a473b43a7f189e8812b0bda939ef4ead6d0fe26f..16e5446bb1591c56f7e0cb66c9e580f4deb06883 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -29,73 +29,130 @@ #![warn(missing_docs)] -use std::sync::Arc; +use std::{sync::Arc, fmt}; -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; +use sp_blockchain::{Error as BlockChainError, HeaderMetadata, HeaderBackend}; +use sp_consensus::SelectChain; +use sc_keystore::KeyStorePtr; +use sp_consensus_babe::BabeApi; +use sc_consensus_epochs::SharedEpochChanges; +use sc_consensus_babe::{Config, Epoch}; +use sc_consensus_babe_rpc::BabeRPCHandler; /// Light client extra dependencies. -pub struct LightDeps { +pub struct LightDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, /// Remote access to the blockchain (async). pub remote_blockchain: Arc>, /// Fetcher instance. pub fetcher: Arc, } -impl LightDeps { - /// Create empty `LightDeps` with given `F` type. - /// - /// This is a convenience method to be used in the service builder, - /// to make sure the type of the `LightDeps` is matching. - pub fn none(_: Option>) -> Option { - None - } +/// Extra dependencies for BABE. +pub struct BabeDeps { + /// BABE protocol config. + pub babe_config: Config, + /// BABE pending epoch changes. + pub shared_epoch_changes: SharedEpochChanges, + /// The keystore that manages the keys of the node. + pub keystore: KeyStorePtr, } -/// Instantiate all RPC extensions. -/// -/// If you provide `LightDeps`, the system is configured for light client. -pub fn create( - client: Arc, - pool: Arc

, - light_deps: Option>, +/// Full client dependencies. +pub struct FullDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// The SelectChain Strategy + pub select_chain: SC, + /// BABE specific dependencies. + pub babe: BabeDeps, +} + +/// Instantiate all Full RPC extensions. +pub fn create_full( + deps: FullDeps, ) -> jsonrpc_core::IoHandler where C: ProvideRuntimeApi, - C: sc_client::blockchain::HeaderBackend, + C: HeaderBackend + HeaderMetadata + 'static, 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, + C::Api: BabeApi, + ::Error: fmt::Debug, P: TransactionPool + 'static, M: jsonrpc_core::Metadata + Default, + SC: SelectChain +'static, { - use substrate_frame_rpc_system::{FullSystem, LightSystem, SystemApi}; + use substrate_frame_rpc_system::{FullSystem, SystemApi}; use pallet_contracts_rpc::{Contracts, ContractsApi}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; let mut io = jsonrpc_core::IoHandler::default(); + let FullDeps { + client, + pool, + select_chain, + babe + } = deps; + let BabeDeps { + keystore, + babe_config, + shared_epoch_changes, + } = babe; + + io.extend_with( + SystemApi::to_delegate(FullSystem::new(client.clone(), pool)) + ); + // Making synchronous calls in light client freezes the browser currently, + // more context: https://github.com/paritytech/substrate/pull/3480 + // These RPCs should use an asynchronous caller instead. + io.extend_with( + ContractsApi::to_delegate(Contracts::new(client.clone())) + ); + io.extend_with( + TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone())) + ); + io.extend_with( + sc_consensus_babe_rpc::BabeApi::to_delegate( + BabeRPCHandler::new(client, shared_epoch_changes, keystore, babe_config, select_chain) + ) + ); + + io +} + +/// Instantiate all Light RPC extensions. +pub fn create_light( + deps: LightDeps, +) -> jsonrpc_core::IoHandler where + C: sc_client::blockchain::HeaderBackend, + C: Send + Sync + 'static, + F: sc_client::light::fetcher::Fetcher + 'static, + P: TransactionPool + 'static, + M: jsonrpc_core::Metadata + Default, +{ + use substrate_frame_rpc_system::{LightSystem, SystemApi}; + + let LightDeps { + client, + pool, + remote_blockchain, + fetcher + } = deps; + let mut io = jsonrpc_core::IoHandler::default(); + io.extend_with( + SystemApi::::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool)) + ); - if let Some(LightDeps { remote_blockchain, fetcher }) = light_deps { - io.extend_with( - SystemApi::::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool)) - ); - } else { - io.extend_with( - SystemApi::to_delegate(FullSystem::new(client.clone(), pool)) - ); - - // Making synchronous calls in light client freezes the browser currently, - // more context: https://github.com/paritytech/substrate/pull/3480 - // These RPCs should use an asynchronous caller instead. - io.extend_with( - ContractsApi::to_delegate(Contracts::new(client.clone())) - ); - io.extend_with( - TransactionPaymentApi::to_delegate(TransactionPayment::new(client)) - ); - } io } 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 d020382cbe367835654388c5fe735105881ec540..e8c0a103e652e912db5d6c98733416c119d4dccf 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: 205, - impl_version: 205, + spec_version: 223, + 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! { @@ -238,13 +240,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; } @@ -306,7 +308,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>; @@ -318,6 +319,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 @@ -346,16 +348,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; @@ -390,22 +392,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; @@ -421,7 +421,7 @@ impl pallet_contracts::Trait for Runtime { type Randomness = RandomnessCollectiveFlip; type Call = Call; type Event = Event; - type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminator; + type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminer; type ComputeDispatchFee = pallet_contracts::DefaultDispatchFeeComputor; type TrieIdGenerator = pallet_contracts::TrieIdFromParentCounter; type GasPayment = (); @@ -432,8 +432,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; @@ -446,7 +444,7 @@ impl pallet_contracts::Trait for Runtime { impl pallet_sudo::Trait for Runtime { type Event = Event; - type Proposal = Call; + type Call = Call; } /// A runtime transaction submitter. @@ -458,11 +456,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 { @@ -483,7 +481,7 @@ parameter_types! { } impl pallet_finality_tracker::Trait for Runtime { - type OnFinalizationStalled = Grandpa; + type OnFinalizationStalled = (); type WindowSize = WindowSize; type ReportLatency = ReportLatency; } @@ -492,19 +490,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 { @@ -590,21 +590,27 @@ 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, + Staking: pallet_staking::{Module, Call, Config, Storage, Event}, Session: pallet_session::{Module, Call, Storage, Event, Config}, Democracy: pallet_democracy::{Module, Call, Storage, Config, Event}, Council: pallet_collective::::{Module, Call, Storage, Origin, Event, Config}, @@ -614,8 +620,8 @@ construct_runtime!( FinalityTracker: pallet_finality_tracker::{Module, Call, Inherent}, Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event}, Treasury: pallet_treasury::{Module, Call, Storage, Config, Event}, - Contracts: pallet_contracts, - Sudo: pallet_sudo, + Contracts: pallet_contracts::{Module, Call, Config, Storage, Event}, + Sudo: pallet_sudo::{Module, Call, Config, Storage, Event}, ImOnline: pallet_im_online::{Module, Call, Storage, Event, ValidateUnsigned, Config}, AuthorityDiscovery: pallet_authority_discovery::{Module, Call, Config}, Offences: pallet_offences::{Module, Call, Storage, Event}, @@ -623,6 +629,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}, } ); @@ -681,6 +688,10 @@ impl_runtime_apis! { Executive::apply_extrinsic(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_trusted_extrinsic(extrinsic) + } + fn finalize_block() -> ::Header { Executive::finalize_block() } @@ -732,6 +743,10 @@ impl_runtime_apis! { secondary_slots: true, } } + + fn current_epoch_start() -> sp_consensus_babe::SlotNumber { + Babe::current_epoch_start() + } } impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { @@ -746,7 +761,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, @@ -754,13 +771,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, @@ -773,16 +785,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) } } @@ -800,6 +810,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..840b2d0fefcd7258815cfd0ccb79c3dabee15946 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,29 @@ 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" +tempdir = "0.3" +fs_extra = "1" + +[dev-dependencies] +criterion = "0.3.0" +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..f8cbbec79d5c2ba11bdccb5d379633f7aae3ea04 --- /dev/null +++ b/bin/node/testing/benches/import.rs @@ -0,0 +1,141 @@ +// 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 node_testing::bench::{BenchDb, Profile}; +use sp_runtime::generic::BlockId; +use criterion::{Criterion, criterion_group, criterion_main}; +use sc_client_api::backend::Backend; + +criterion_group!( + name = benches; + config = Criterion::default().sample_size(50).warm_up_time(std::time::Duration::from_secs(20)); + targets = bench_block_import +); +criterion_group!( + name = profile; + config = Criterion::default().sample_size(10); + targets = profile_block_import +); +criterion_main!(benches, profile); + +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 mut bench_db = BenchDb::new(128); + let block = bench_db.generate_block(100); + + log::trace!( + target: "bench-logistics", + "Seed database directory: {}", + bench_db.path().display(), + ); + + c.bench_function_over_inputs("import block", + move |bencher, profile| { + bencher.iter_batched( + || { + let context = bench_db.create_context(*profile); + + // 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.path().display(), version, + ); + + context + }, + |mut context| { + let start = std::time::Instant::now(); + context.import_block(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 mut bench_db = BenchDb::new(128); + let block = bench_db.generate_block(100); + + c.bench_function("profile block", + move |bencher| { + bencher.iter_batched( + || { + bench_db.create_context(Profile::Native) + }, + |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)); + context.import_block(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, + ); + }, + ); +} diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..5653ba77016462ea9fd17e95c9a982223a5f4a3b --- /dev/null +++ b/bin/node/testing/src/bench.rs @@ -0,0 +1,424 @@ +// 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 . + +//! Benchmarking module. +//! +//! Utilities to do full-scale benchmarks involving database. With `BenchDb` you +//! can pregenerate seed database and `clone` it for every iteration of your benchmarks +//! or tests to get consistent, smooth benchmark experience! + +use std::{sync::Arc, path::Path, collections::BTreeMap}; + +use node_primitives::Block; +use crate::client::{Client, Backend}; +use crate::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::{ + ExecutionStrategy, + execution_extensions::{ExecutionExtensions, ExecutionStrategies}, +}; +use sp_core::{Pair, Public, sr25519}; + +/// Keyring full of accounts for benching. +/// +/// Accounts are ordered: +/// //endowed-user//00 +/// //endowed-user//01 +/// ... +/// //endowed-user//N +#[derive(Clone)] +pub struct BenchKeyring { + accounts: BTreeMap, +} + +/// Pre-initialized benchmarking database. +/// +/// This is prepared database with genesis and keyring +/// that can be cloned and then used for any benchmarking. +pub struct BenchDb { + keyring: BenchKeyring, + directory_guard: Guard, +} + +impl Clone for BenchDb { + fn clone(&self) -> Self { + let keyring = self.keyring.clone(); + let dir = tempdir::TempDir::new("sub-bench").expect("temp dir creation failed"); + + let seed_dir = self.directory_guard.0.path(); + + 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"); + + BenchDb { keyring, directory_guard: Guard(dir) } + } +} + +impl BenchDb { + /// New immutable benchmarking database. + /// + /// This will generate database files in random temporary directory + /// and keep it there until struct is dropped. + /// + /// You can `clone` this database or you can `create_context` from it + /// (which also do `clone`) to run actual operation against new database + /// which will be identical to this. + pub fn new(keyring_length: usize) -> Self { + let keyring = BenchKeyring::new(keyring_length); + + 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) = Self::bench_client(dir.path(), Profile::Native, &keyring); + let directory_guard = Guard(dir); + + BenchDb { keyring, directory_guard } + } + + // 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), + &keyring.generate_genesis(), + None, + None, + ExecutionExtensions::new(profile.into_execution_strategies(), None), + ).expect("Should not fail"); + + (client, backend) + } + + /// Generate new block using this database. + pub fn generate_block(&mut self, transactions: usize) -> Block { + let (client, _backend) = Self::bench_client( + self.directory_guard.path(), + Profile::Wasm, + &self.keyring, + ); + + 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 timestamp 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..transactions { + + let sender = self.keyring.at(iteration); + let receiver = get_account_id_from_seed::( + &format!("random-user//{}", iteration) + ); + + let signed = self.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 + } + + /// Database path. + pub fn path(&self) -> &Path { + self.directory_guard.path() + } + + /// Clone this database and create context for testing/benchmarking. + pub fn create_context(&self, profile: Profile) -> BenchContext { + let BenchDb { directory_guard, keyring } = self.clone(); + let (client, backend) = Self::bench_client(directory_guard.path(), profile, &keyring); + + BenchContext { + client, backend, db_guard: directory_guard, + } + } +} + +impl BenchKeyring { + /// New keyring. + /// + /// `length` is the number of accounts generated. + pub fn new(length: usize) -> Self { + let mut accounts = BTreeMap::new(); + + for n in 0..length { + 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 } + } + + /// Generated account id-s from keyring keypairs. + pub fn collect_account_ids(&self) -> Vec { + self.accounts.keys().cloned().collect() + } + + /// Get account id at position `index` + pub fn at(&self, index: usize) -> AccountId { + self.accounts.keys().nth(index).expect("Failed to get account").clone() + } + + /// Sign transaction with keypair from this keyring. + pub 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, + }, + } + } + + /// Generate genesis with accounts from this keyring endowed with some balance. + pub fn generate_genesis(&self) -> node_runtime::GenesisConfig { + crate::genesis::config_endowed( + false, + Some(node_runtime::WASM_BINARY), + self.collect_account_ids(), + ) + } +} + +/// Profile for exetion strategies. +#[derive(Clone, Copy, Debug)] +pub enum Profile { + /// As native as possible. + Native, + /// As wasm as possible. + 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, + } + } + } +} + +struct Guard(tempdir::TempDir); + +impl Guard { + fn path(&self) -> &Path { + self.0.path() + } +} + +/// Benchmarking/test context holding instantiated client and backend references. +pub struct BenchContext { + /// Node client. + pub client: Client, + /// Node backend. + pub backend: Arc, + + db_guard: Guard, +} + +type AccountPublic = ::Signer; + +fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public> +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +impl BenchContext { + /// Import some block. + pub fn import_block(&mut self, block: Block) { + let mut import_params = BlockImportParams::new(BlockOrigin::NetworkBroadcast, block.header.clone()); + import_params.body = Some(block.extrinsics().to_vec()); + import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + assert_eq!(self.client.chain_info().best_number, 0); + + assert_eq!( + self.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!(self.client.chain_info().best_number, 1); + } + + /// Database path for the current context. + pub fn path(&self) -> &Path { + self.db_guard.path() + } +} diff --git a/bin/node/testing/src/client.rs b/bin/node/testing/src/client.rs index 140d1cbfc33379af90e49a2a238545fa284fd9ab..963bac7041b7d8ac25686e8e872b72d9210fb23b 100644 --- a/bin/node/testing/src/client.rs +++ b/bin/node/testing/src/client.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Utilites to build a `TestClient` for `node-runtime`. +//! Utilities to build a `TestClient` for `node-runtime`. use sp_runtime::BuildStorage; @@ -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/testing/src/lib.rs b/bin/node/testing/src/lib.rs index f4a5e75e492d3dcc294276d48c970e78071dba0d..6a06d318016f290fd96d6a33fbe7c2e2163ce988 100644 --- a/bin/node/testing/src/lib.rs +++ b/bin/node/testing/src/lib.rs @@ -21,4 +21,4 @@ pub mod client; pub mod genesis; pub mod keyring; - +pub mod bench; 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..7dafeed40616eaf74997fef2c10624d8e5275910 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,64 @@ 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()); - } - Ok(()) -} + let mut import = BlockImportParams::new(BlockOrigin::File, block.header().clone()); + import.body = Some(block.extrinsics().to_vec()); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); + client.clone().import_block(import, HashMap::new()).expect("Failed to import block"); -/// 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 ..."); + info!("Imported block at {}", factory_state.block_number()); } - 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"); + Ok(()) } 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..8267366d7fe02d782086fa05674fe191dcf000f8 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" @@ -12,7 +13,7 @@ node-primitives = { version = "*", path = "../../node/primitives" } sp-runtime = { version = "*", path = "../../../primitives/runtime" } rand = "0.7.2" clap = "2.33.0" -tiny-bip39 = "0.6.2" +tiny-bip39 = "0.7" rustc-hex = "2.0.1" substrate-bip39 = "0.3.1" hex = "0.4.0" @@ -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.16.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..33209692caffb87284318bc59962ab56bcb6cbf7 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, \ @@ -209,7 +280,7 @@ fn get_app<'a, 'b>(usage: &'a str) -> App<'a, 'b> { SubCommand::with_name("transfer") .about("Author and sign a Node pallet_balances::Transfer transaction with a given (secret) key") .args_from_usage(" - -g, --genesis 'The genesis hash or a recognised \ + -g, --genesis 'The genesis hash or a recognized \ chain identifier (dev, elm, alex).' 'The signing secret key URI.' 'The destination account public key URI.' @@ -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)?; @@ -483,7 +572,7 @@ fn read_genesis_hash(matches: &ArgMatches) -> Result { "elm" => hex!["10c08714a10c7da78f40a60f6f732cf0dba97acfb5e2035445b032386157d5c3"].into(), "alex" => hex!["dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b"].into(), h => Decode::decode(&mut &decode_hex(h)?[..]) - .expect("Invalid genesis hash or unrecognised chain identifier"), + .expect("Invalid genesis hash or unrecognized chain identifier"), }; println!( "Using a genesis hash of {}", 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/execution_extensions.rs b/client/api/src/execution_extensions.rs index 1a986a23a47dc7a2cf97676e0c39d63fb18ecc5f..10d33c20e679c6eeddb4368da3c18d429d2ade6b 100644 --- a/client/api/src/execution_extensions.rs +++ b/client/api/src/execution_extensions.rs @@ -64,7 +64,7 @@ impl Default for ExecutionStrategies { /// Generate the starting set of ExternalitiesExtensions based upon the given capabilities pub trait ExtensionsFactory: Send + Sync { - /// Make `Extensions` for given Capapbilities + /// Make `Extensions` for given `Capabilities`. fn extensions_for(&self, capabilities: offchain::Capabilities) -> Extensions; } @@ -77,7 +77,7 @@ impl ExtensionsFactory for () { /// A producer of execution extensions for offchain calls. /// /// This crate aggregates extensions available for the offchain calls -/// and is responsbile to produce a right `Extensions` object +/// and is responsible for producing a correct `Extensions` object. /// for each call, based on required `Capabilities`. pub struct ExecutionExtensions { strategies: ExecutionStrategies, @@ -125,7 +125,7 @@ impl ExecutionExtensions { /// To break retain cycle between `Client` and `TransactionPool` we require this /// extension to be a `Weak` reference. /// That's also the reason why it's being registered lazily instead of - /// during initialisation. + /// during initialization. pub fn register_transaction_pool(&self, pool: Weak>) { *self.transaction_pool.write() = Some(pool); } 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..248304d137018b0d0fa6e86490ac1f4e6016db72 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.16.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..a99453544e5f63e3a6a6e5d62845b465d4bc86d8 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -195,7 +195,7 @@ impl ProposerInner, inherent_data )? { - block_builder.push(extrinsic)?; + block_builder.push_trusted(extrinsic)?; } // proceed with transactions @@ -218,7 +218,7 @@ impl ProposerInner, let pending_tx_data = pending_tx.data().clone(); let pending_tx_hash = pending_tx.hash().clone(); trace!("[{:?}] Pushing to the block.", pending_tx_hash); - match sc_block_builder::BlockBuilder::push(&mut block_builder, pending_tx_data) { + match sc_block_builder::BlockBuilder::push_trusted(&mut block_builder, pending_tx_data) { Ok(()) => { debug!("[{:?}] Pushed to the block.", pending_tx_hash); } @@ -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()))).0 + ); 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()))).0 + ); 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..e9087c89e07ed373ec2cb932b37b2b6822f570e3 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()))).0); //! // 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..26bc9ecea8deac6ecd5f3988878555183cc26aa7 100644 --- a/client/block-builder/src/lib.rs +++ b/client/block-builder/src/lib.rs @@ -121,11 +121,23 @@ where backend, }) } - + /// Push onto the block's list of extrinsics. /// - /// This will ensure the extrinsic can be validly executed (by executing it); + /// This will ensure the extrinsic can be validly executed (by executing it). pub fn push(&mut self, xt: ::Extrinsic) -> Result<(), ApiErrorFor> { + self.push_internal(xt, false) + } + + /// Push onto the block's list of extrinsics. + /// + /// This will treat incoming extrinsic `xt` as untrusted and perform additional checks + /// (currenty checking signature). + pub fn push_trusted(&mut self, xt: ::Extrinsic) -> Result<(), ApiErrorFor> { + self.push_internal(xt, true) + } + + fn push_internal(&mut self, xt: ::Extrinsic, skip_signature: bool) -> Result<(), ApiErrorFor> { let block_id = &self.block_id; let extrinsics = &mut self.extrinsics; @@ -152,12 +164,29 @@ where } }) } else { - self.api.map_api_result(|api| { - match api.apply_extrinsic_with_context( + let use_trusted = skip_signature && self + .api + .has_api_with::>, _>( block_id, - ExecutionContext::BlockConstruction, - xt.clone(), - )? { + |version| version >= 5, + )?; + + self.api.map_api_result(|api| { + let apply_result = if use_trusted { + api.apply_trusted_extrinsic_with_context( + block_id, + ExecutionContext::BlockConstruction, + xt.clone(), + )? + } else { + api.apply_extrinsic_with_context( + block_id, + ExecutionContext::BlockConstruction, + xt.clone(), + )? + }; + + match apply_result { Ok(_) => { extrinsics.push(xt); Ok(()) @@ -197,8 +226,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/chain-spec/derive/src/impls.rs b/client/chain-spec/derive/src/impls.rs index 2caf1c5a9c3ebbe148a5567e40695e95b66293dd..43874241f600f6ffdac987c954fdea7573f01d82 100644 --- a/client/chain-spec/derive/src/impls.rs +++ b/client/chain-spec/derive/src/impls.rs @@ -108,7 +108,7 @@ pub fn derive( let err = || { let err = Error::new( Span::call_site(), - "ChainSpecGroup is only avaible for structs with named fields." + "ChainSpecGroup is only available for structs with named fields." ).to_compile_error(); quote!( #err ).into() }; diff --git a/client/chain-spec/src/chain_spec.rs b/client/chain-spec/src/chain_spec.rs index 81cbce5ea731c095f482293f6d19ca39e12a2698..ab9c851bdde5fb862188b871bbf35b063db4188e 100644 --- a/client/chain-spec/src/chain_spec.rs +++ b/client/chain-spec/src/chain_spec.rs @@ -20,7 +20,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; -use std::rc::Rc; +use std::sync::Arc; use serde::{Serialize, Deserialize}; use sp_core::storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild}; use sp_runtime::BuildStorage; @@ -32,7 +32,7 @@ use sc_telemetry::TelemetryEndpoints; enum GenesisSource { File(PathBuf), Binary(Cow<'static, [u8]>), - Factory(Rc G>), + Factory(Arc G + Send + Sync>), } impl Clone for GenesisSource { @@ -215,7 +215,7 @@ impl ChainSpec { } /// Create hardcoded spec. - pub fn from_genesis G + 'static>( + pub fn from_genesis G + 'static + Send + Sync>( name: &str, id: &str, constructor: F, @@ -239,7 +239,7 @@ impl ChainSpec { ChainSpec { client_spec, - genesis: GenesisSource::Factory(Rc::new(constructor)), + genesis: GenesisSource::Factory(Arc::new(constructor)), } } } @@ -336,7 +336,7 @@ mod tests { type TestSpec = ChainSpec; #[test] - fn should_deserailize_example_chain_spec() { + fn should_deserialize_example_chain_spec() { let spec1 = TestSpec::from_json_bytes(Cow::Owned( include_bytes!("../res/chain_spec.json").to_vec() )).unwrap(); diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index b46e3992b2e9f01f7062b9a8391f5fb6ee2551ba..e176894d64c078dff802e49875c6a5321b1c6a18 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" } @@ -31,8 +33,10 @@ sp-state-machine = { version = "0.8", path = "../../primitives/state-machine" } sc-telemetry = { version = "2.0.0", path = "../telemetry" } 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 fc5025952860b3df6f0fb819872f23a98923d680..7495ad8e75690b10d68da914c029c6fd711c25cf 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,24 +605,30 @@ 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 } }); - 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)?; - let grafana_interface: &str = if cli.grafana_external { "0.0.0.0" } else { "127.0.0.1" }; + 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)?); + } - 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)?); - config.grafana_port = Some( - parse_address(&format!("{}:{}", grafana_interface, 9955), cli.grafana_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)? + ); + } config.rpc_ws_max_connections = cli.ws_max_connections; config.rpc_cors = cli.rpc_cors.unwrap_or_else(|| if is_dev { @@ -1095,13 +652,13 @@ where config.telemetry_endpoints = Some(TelemetryEndpoints::new(cli.telemetry_endpoints)); } - config.tracing_targets = cli.tracing_targets.into(); - config.tracing_receiver = cli.tracing_receiver.into(); + config.tracing_targets = cli.import_params.tracing_targets.into(); + config.tracing_receiver = cli.import_params.tracing_receiver.into(); // Imply forced authoring on --dev config.force_authoring = cli.shared_params.dev || cli.force_authoring; - Ok(config) + Ok(()) } fn interface_str( @@ -1125,61 +682,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, @@ -1194,7 +696,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(); @@ -1266,118 +769,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() { @@ -1389,30 +791,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 { @@ -1425,42 +817,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 57b90c2e73d5ccf5cbed863fbfd7423b8cbe6f5b..d6c437f668b2c39ed496b6f9f220f050785058c7 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. @@ -179,7 +207,7 @@ pub struct NetworkConfigurationParams { #[structopt(long = "port", value_name = "PORT")] pub port: Option, - /// Allow connecting to private IPv4 addresses (as specified in + /// Forbid connecting to private IPv4 addresses (as specified in /// [RFC1918](https://tools.ietf.org/html/rfc1918)), unless the address was passed with /// `--reserved-nodes` or `--bootnodes`. #[structopt(long = "no-private-ipv4")] @@ -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, } @@ -408,7 +440,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, @@ -417,7 +449,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. @@ -529,28 +561,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, @@ -579,71 +625,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 } } } @@ -723,7 +718,7 @@ pub struct BuildSpecCmd { pub node_key_params: NodeKeyParams, } -/// Wrapper type of `String` which holds an arbitary sized unsigned integer formatted as decimal. +/// Wrapper type of `String` that holds an unsigned integer of arbitrary size, formatted as a decimal. #[derive(Debug, Clone)] pub struct BlockNumber(String); @@ -854,16 +849,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), @@ -873,7 +908,7 @@ pub enum CoreParams { /// Import blocks from file. ImportBlocks(ImportBlocksCmd), - /// Validte a single block. + /// Validate a single block. CheckBlock(CheckBlockCmd), /// Revert chain to the previous state. @@ -882,111 +917,352 @@ 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..eccf240f20ff3190cfb5f58809a004a2fadc03e6 --- /dev/null +++ b/client/cli/src/runtime.rs @@ -0,0 +1,141 @@ +// 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 + // and drop the runtime first. + let _telemetry = service.telemetry(); + + let f = service.fuse(); + pin_mut!(f); + + runtime.block_on(main(f)).map_err(|e| e.to_string())?; + drop(runtime); + + 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..db872f28c1b445db8d0c49baa81288477e03a712 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,26 +274,21 @@ 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()); let signature_digest_item = as CompatibleDigestItem

>::aura_seal(signature); - BlockImportParams { - origin: BlockOrigin::Own, - header, - justification: None, - post_digests: vec![signature_digest_item], - body: Some(body), - storage_changes: Some(storage_changes), - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - } + let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); + import_block.post_digests.push(signature_digest_item); + import_block.body = Some(body); + import_block.storage_changes = Some(storage_changes); + import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + import_block }) } @@ -635,21 +630,14 @@ impl Verifier for AuraVerifier where _ => None, }); - let block_import_params = BlockImportParams { - origin, - header: pre_header, - post_digests: vec![seal], - body, - storage_changes: None, - finalized: false, - justification, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; - - Ok((block_import_params, maybe_keys)) + let mut import_block = BlockImportParams::new(origin, pre_header); + import_block.post_digests.push(seal); + import_block.body = body; + import_block.justification = justification; + import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain); + import_block.post_hash = Some(hash); + + Ok((import_block, maybe_keys)) } CheckedHeader::Deferred(a, b) => { debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); @@ -787,7 +775,7 @@ impl BlockImport for AuraBlockImport, new_cache: HashMap>, ) -> Result { - let hash = block.post_header().hash(); + let hash = block.post_hash(); let slot_number = find_pre_digest::(&block.header) .expect("valid Aura headers must contain a predigest; \ header has been already verified; qed"); diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 55b0e0cf202177eb471d94887c674a76066bc977..3f5487885fea0e11ba758d421589eeda6a007500 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"] } @@ -13,6 +14,7 @@ sp-application-crypto = { version = "2.0.0", path = "../../../primitives/applica num-bigint = "0.2.3" num-rational = "0.2.2" num-traits = "0.2.8" +serde = { version = "1.0.104", features = ["derive"] } sp-version = { version = "2.0.0", path = "../../../primitives/version" } sp-io = { version = "2.0.0", path = "../../../primitives/io" } sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } @@ -21,6 +23,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 +33,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/rpc/Cargo.toml b/client/consensus/babe/rpc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..3fd0e924af092231d1103471bec8231f40acb872 --- /dev/null +++ b/client/consensus/babe/rpc/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "sc-consensus-babe-rpc" +version = "0.8.0" +authors = ["Parity Technologies "] +description = "RPC extensions for the BABE consensus algorithm" +edition = "2018" +license = "GPL-3.0" + +[dependencies] +sc-consensus-babe = { version = "0.8.0", path = "../" } +jsonrpc-core = "14.0.3" +jsonrpc-core-client = "14.0.3" +jsonrpc-derive = "14.0.3" +sp-consensus-babe = { version = "0.8", path = "../../../../primitives/consensus/babe" } +serde = { version = "1.0.104", features=["derive"] } +sp-blockchain = { version = "2.0.0", path = "../../../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../../../primitives/runtime" } +sc-consensus-epochs = { version = "0.8", path = "../../epochs" } +futures = "0.3.1" +derive_more = "0.99.2" +sp-api = { version = "2.0.0", path = "../../../../primitives/api" } +sp-consensus = { version = "0.8", path = "../../../../primitives/consensus/common" } +sp-core = { version = "2.0.0", path = "../../../../primitives/core" } +sc-keystore = { version = "2.0.0", path = "../../../keystore" } + +[dev-dependencies] +substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" } +sp-application-crypto = { version = "2.0.0", path = "../../../../primitives/application-crypto" } +sp-keyring = { version = "2.0.0", path = "../../../../primitives/keyring" } +tempfile = "3.1.0" diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..033a7d6b98530aa8f55ad4b224931d13188a1cd6 --- /dev/null +++ b/client/consensus/babe/rpc/src/lib.rs @@ -0,0 +1,248 @@ +// 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 . + +//! RPC api for babe. + +use sc_consensus_babe::{Epoch, authorship, Config}; +use futures::{FutureExt as _, TryFutureExt as _}; +use jsonrpc_core::{ + Error as RpcError, + futures::future as rpc_future, +}; +use jsonrpc_derive::rpc; +use sc_consensus_epochs::{descendent_query, Epoch as EpochT, SharedEpochChanges}; +use sp_consensus_babe::{ + AuthorityId, + BabeApi as BabeRuntimeApi, + digests::PreDigest, +}; +use serde::{Deserialize, Serialize}; +use sc_keystore::KeyStorePtr; +use sp_api::{ProvideRuntimeApi, BlockId}; +use sp_core::crypto::Pair; +use sp_runtime::traits::{Block as BlockT, Header as _}; +use sp_consensus::{SelectChain, Error as ConsensusError}; +use sp_blockchain::{HeaderBackend, HeaderMetadata, Error as BlockChainError}; +use std::{collections::HashMap, fmt, sync::Arc}; + +type FutureResult = Box + Send>; + +/// Provides rpc methods for interacting with Babe. +#[rpc] +pub trait BabeApi { + /// Returns data about which slots (primary or secondary) can be claimed in the current epoch + /// with the keys in the keystore. + #[rpc(name = "babe_epochAuthorship")] + fn epoch_authorship(&self) -> FutureResult>; +} + +/// Implements the BabeRPC trait for interacting with Babe. +/// +/// Uses a background thread to calculate epoch_authorship data. +pub struct BabeRPCHandler { + /// shared reference to the client. + client: Arc, + /// shared reference to EpochChanges + shared_epoch_changes: SharedEpochChanges, + /// shared reference to the Keystore + keystore: KeyStorePtr, + /// config (actually holds the slot duration) + babe_config: Config, + /// The SelectChain strategy + select_chain: SC, +} + +impl BabeRPCHandler { + /// Creates a new instance of the BabeRpc handler. + pub fn new( + client: Arc, + shared_epoch_changes: SharedEpochChanges, + keystore: KeyStorePtr, + babe_config: Config, + select_chain: SC, + ) -> Self { + + Self { + client, + shared_epoch_changes, + keystore, + babe_config, + select_chain, + } + } +} + +impl BabeApi for BabeRPCHandler + where + B: BlockT, + C: ProvideRuntimeApi + HeaderBackend + HeaderMetadata + 'static, + C::Api: BabeRuntimeApi, + ::Error: fmt::Debug, + SC: SelectChain + Clone + 'static, +{ + fn epoch_authorship(&self) -> FutureResult> { + let ( + babe_config, + keystore, + shared_epoch, + client, + select_chain, + ) = ( + self.babe_config.clone(), + self.keystore.clone(), + self.shared_epoch_changes.clone(), + self.client.clone(), + self.select_chain.clone(), + ); + let future = async move { + let header = select_chain.best_chain().map_err(Error::Consensus)?; + let epoch_start = client.runtime_api() + .current_epoch_start(&BlockId::Hash(header.hash())) + .map_err(|err| { + Error::StringError(format!("{:?}", err)) + })?; + let epoch = epoch_data(&shared_epoch, &client, &babe_config, epoch_start, &select_chain)?; + let (epoch_start, epoch_end) = (epoch.start_slot(), epoch.end_slot()); + + let mut claims: HashMap = HashMap::new(); + + for slot_number in epoch_start..epoch_end { + let epoch = epoch_data(&shared_epoch, &client, &babe_config, slot_number, &select_chain)?; + if let Some((claim, key)) = authorship::claim_slot(slot_number, &epoch, &babe_config, &keystore) { + match claim { + PreDigest::Primary { .. } => { + claims.entry(key.public()).or_default().primary.push(slot_number); + } + PreDigest::Secondary { .. } => { + claims.entry(key.public()).or_default().secondary.push(slot_number); + } + }; + } + } + + Ok(claims) + }.boxed(); + + Box::new(future.compat()) + } +} + +/// Holds information about the `slot_number`'s that can be claimed by a given key. +#[derive(Default, Debug, Deserialize, Serialize)] +pub struct EpochAuthorship { + /// the array of primary slots that can be claimed + primary: Vec, + /// the array of secondary slots that can be claimed + secondary: Vec, +} + +/// Errors encountered by the RPC +#[derive(Debug, derive_more::Display, derive_more::From)] +pub enum Error { + /// Consensus error + Consensus(ConsensusError), + /// Errors that can be formatted as a String + StringError(String) +} + +impl From for jsonrpc_core::Error { + fn from(error: Error) -> Self { + jsonrpc_core::Error { + message: format!("{}", error).into(), + code: jsonrpc_core::ErrorCode::ServerError(1234), + data: None, + } + } +} + +/// fetches the epoch data for a given slot_number. +fn epoch_data( + epoch_changes: &SharedEpochChanges, + client: &Arc, + babe_config: &Config, + slot_number: u64, + select_chain: &SC, +) -> Result + where + B: BlockT, + C: HeaderBackend + HeaderMetadata + 'static, + SC: SelectChain, +{ + let parent = select_chain.best_chain()?; + epoch_changes.lock().epoch_for_child_of( + descendent_query(&**client), + &parent.hash(), + parent.number().clone(), + slot_number, + |slot| babe_config.genesis_epoch(slot), + ) + .map_err(|e| Error::Consensus(ConsensusError::ChainLookup(format!("{:?}", e))))? + .map(|e| e.into_inner()) + .ok_or(Error::Consensus(ConsensusError::InvalidAuthoritiesSet)) +} + +#[cfg(test)] +mod tests { + use super::*; + use substrate_test_runtime_client::{ + DefaultTestClientBuilderExt, + TestClientBuilderExt, + TestClientBuilder, + }; + use sp_application_crypto::AppPair; + use sp_keyring::Ed25519Keyring; + use sc_keystore::Store; + + use std::sync::Arc; + use sc_consensus_babe::{Config, block_import, AuthorityPair}; + use jsonrpc_core::IoHandler; + + /// creates keystore backed by a temp file + fn create_temp_keystore(authority: Ed25519Keyring) -> (KeyStorePtr, tempfile::TempDir) { + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = Store::open(keystore_path.path(), None).expect("Creates keystore"); + keystore.write().insert_ephemeral_from_seed::

(&authority.to_seed()) + .expect("Creates authority key"); + + (keystore, keystore_path) + } + + #[test] + fn rpc() { + let builder = TestClientBuilder::new(); + let (client, longest_chain) = builder.build_with_longest_chain(); + let client = Arc::new(client); + let config = Config::get_or_compute(&*client).expect("config available"); + let (_, link) = block_import( + config.clone(), + client.clone(), + client.clone(), + client.clone(), + ).expect("can initialize block-import"); + + let epoch_changes = link.epoch_changes().clone(); + let select_chain = longest_chain; + let keystore = create_temp_keystore::(Ed25519Keyring::Alice).0; + let handler = BabeRPCHandler::new(client.clone(), epoch_changes, keystore, config, select_chain); + let mut io = IoHandler::new(); + + io.extend_with(BabeApi::to_delegate(handler)); + let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4]}},"id":1}"#; + + assert_eq!(Some(response.into()), io.handle_request_sync(request)); + } +} diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 62667ef3978c04fa8beb907723656e817a12702d..4654f91b89876a9858751e8771bbb3fd0a69b022 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, }; @@ -140,12 +144,12 @@ fn claim_secondary_slot( /// a primary VRF based slot. If we are not able to claim it, then if we have /// secondary slots enabled for the given epoch, we will fallback to trying to /// claim a secondary slot. -pub(super) fn claim_slot( +pub fn claim_slot( slot_number: SlotNumber, 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..b93619b29c885216fbf39ff2cffc694889dc18b3 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; +pub 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,29 +479,24 @@ 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()); let digest_item = as CompatibleDigestItem>::babe_seal(signature); - BlockImportParams { - origin: BlockOrigin::Own, - header, - justification: None, - post_digests: vec![digest_item], - body: Some(body), - 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, - allow_missing_state: false, - import_existing: false, - } + let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); + import_block.post_digests.push(digest_item); + import_block.body = Some(body); + import_block.storage_changes = Some(storage_changes); + import_block.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + + import_block }) } @@ -535,12 +574,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 +638,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 +765,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,24 +850,17 @@ impl Verifier for BabeVerifier ?pre_header); - let block_import_params = BlockImportParams { - origin, - header: pre_header, - post_digests: vec![verified_info.seal], - body, - storage_changes: None, - finalized: false, - justification, - auxiliary: Vec::new(), - // 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, - allow_missing_state: false, - import_existing: false, - }; - - Ok((block_import_params, Default::default())) + let mut import_block = BlockImportParams::new(origin, pre_header); + import_block.post_digests.push(verified_info.seal); + import_block.body = body; + import_block.justification = justification; + import_block.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + import_block.post_hash = Some(hash); + + Ok((import_block, Default::default())) } CheckedHeader::Deferred(a, b) => { debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); @@ -859,7 +904,7 @@ pub struct BabeBlockImport { inner: I, client: Arc>, api: Arc, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, config: Config, } @@ -879,7 +924,7 @@ impl BabeBlockImport { fn new( client: Arc>, api: Arc, - epoch_changes: SharedEpochChanges, + epoch_changes: SharedEpochChanges, block_import: I, config: Config, ) -> Self { @@ -912,7 +957,7 @@ impl BlockImport for BabeBlockImport, new_cache: HashMap>, ) -> Result { - let hash = block.post_header().hash(); + let hash = block.post_hash(); let number = block.header.number().clone(); // early exit if block already in chain, otherwise the check for @@ -967,20 +1012,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 +1121,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 +1154,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 +1201,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 +1285,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..6c1ffa2c3af05109701a361a5f463cf4b3b71d50 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, @@ -230,7 +229,7 @@ impl Verifier for TestVerifier { ) -> Result<(BlockImportParams, Option)>>), String> { // apply post-sealing mutations (i.e. stripping seal, if desired). (self.mutator)(&mut header, Stage::PostSeal); - Ok(self.inner.verify(origin, header, justification, body).expect("verification failed!")) + self.inner.verify(origin, header, justification, body) } } @@ -317,12 +316,12 @@ impl TestNetFactory for BabeTestNet { } fn peer(&mut self, i: usize) -> &mut Peer { - trace!(target: "babe", "Retreiving a peer"); + trace!(target: "babe", "Retrieving a peer"); &mut self.peers[i] } fn peers(&self) -> &Vec> { - trace!(target: "babe", "Retreiving peers"); + trace!(target: "babe", "Retrieving peers"); &self.peers } @@ -424,7 +423,14 @@ fn run_one_test( } runtime.spawn(futures01::future::poll_fn(move || { - net.lock().poll(); + let mut net = net.lock(); + net.poll(); + for p in net.peers() { + for (h, e) in p.failed_verifications() { + panic!("Verification failed for {:?}: {}", h, e); + } + } + Ok::<_, ()>(futures01::Async::NotReady::<()>) })); @@ -558,7 +564,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 +572,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. @@ -584,22 +600,15 @@ fn propose_and_import_block( h }; - let import_result = block_import.import_block( - BlockImportParams { - origin: BlockOrigin::Own, - header: block.header, - justification: None, - post_digests: vec![seal], - body: Some(block.extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }, - Default::default(), - ).unwrap(); + let mut import = BlockImportParams::new(BlockOrigin::Own, block.header); + import.post_digests.push(seal); + import.body = Some(block.extrinsics); + import.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate { epoch }) as Box, + ); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); + let import_result = block_import.import_block(import, Default::default()).unwrap(); match import_result { ImportResult::Imported(_) => {}, 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..05ee611de137853e32ef4bfc4e22e53f6c17f914 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), }; @@ -122,13 +141,12 @@ impl ViableEpoch { } } -/// The datatype encoded on disk. -// This really shouldn't be public, but the encode/decode derives force it to be. +/// The data type encoded on disk. #[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..c4336485a14acbe3ac1c6d7b9493a80bd577e5a8 --- /dev/null +++ b/client/consensus/manual-seal/src/lib.rs @@ -0,0 +1,458 @@ +// 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 mut import_params = BlockImportParams::new(origin, header); + import_params.justification = justification; + import_params.body = body; + import_params.finalized = true; + import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + 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.validated_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()).0); + 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().validated_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()).0); + 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()).0); + 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..39d73e16ab74fdadde2e3a597fa2ef71615cc9fa --- /dev/null +++ b/client/consensus/manual-seal/src/seal_new_block.rs @@ -0,0 +1,138 @@ +// 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.validated_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 mut params = BlockImportParams::new(BlockOrigin::Own, header.clone()); + params.body = Some(body); + params.finalized = finalize; + params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + 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..49d2e64f6050c92b8603e6a3cf034e19263085a1 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,16 @@ 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 { + /// 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 +148,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 +180,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 +230,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,71 +265,174 @@ 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 import_block = BlockImportParams { - origin, - header: checked_header, - post_digests: vec![seal], - body, - storage_changes: None, - finalized: false, - justification, - auxiliary: vec![(key, Some(aux.encode()))], - fork_choice: ForkChoiceStrategy::Custom( + 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(&block.post_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 - ), - allow_missing_state: false, - import_existing: false, + )); + } + + 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:: { + difficulty: None, + }; + + let mut import_block = BlockImportParams::new(origin, checked_header); + import_block.post_digests.push(seal); + import_block.body = body; + import_block.justification = justification; + import_block.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(intermediate) as Box + ); + import_block.post_hash = Some(hash); + Ok((import_block, None)) } } @@ -330,33 +455,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 +556,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 +599,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,41 +639,29 @@ 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:: { + 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)], - body: Some(body), - storage_changes: Some(proposal.storage_changes), - finalized: false, - auxiliary: vec![(key, Some(aux.encode()))], - fork_choice: ForkChoiceStrategy::Custom(true), - allow_missing_state: false, - import_existing: false, - }; + let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); + import_block.post_digests.push(seal); + import_block.body = Some(body); + import_block.storage_changes = Some(proposal.storage_changes); + import_block.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(intermediate) as Box + ); + import_block.post_hash = Some(hash); block_import.import_block(import_block, HashMap::default()) .map_err(|e| Error::BlockBuiltError(best_hash, e))?; 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..892c8b136436b9fa226f2cbd4617c4729870dede 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,18 +281,19 @@ pub trait SimpleSlotWorker { body, proposal.storage_changes, claim, + epoch_data, ); info!( "Pre-sealed block for proposal at {}. Hash now {:?}, previously {:?}.", header_num, - block_import_params.post_header().hash(), + block_import_params.post_hash(), header_hash, ); telemetry!(CONSENSUS_INFO; "slots.pre_sealed_block"; "header_num" => ?header_num, - "hash_now" => ?block_import_params.post_header().hash(), + "hash_now" => ?block_import_params.post_hash(), "hash_previously" => ?header_hash, ); 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/cache/list_cache.rs b/client/db/src/cache/list_cache.rs index 72278a1e85e6dd7a293f569084d1337689aba9a1..f3a8171342c9158997cb1dd08e3d90aa03e94b8a 100644 --- a/client/db/src/cache/list_cache.rs +++ b/client/db/src/cache/list_cache.rs @@ -357,7 +357,7 @@ impl> ListCache // it is possible that we're inserting extra (but still required) fork here let new_storage_entry = StorageEntry { prev_valid_from: Some(prev_valid_from), - value: value.expect("chcecked abpve that !value.is_none(); qed"), + value: value.expect("checked above that !value.is_none(); qed"), }; tx.insert_storage_entry(&block, &new_storage_entry); diff --git a/client/db/src/cache/list_entry.rs b/client/db/src/cache/list_entry.rs index d3f7dd57693c1906974c66183f1b6faea36ff523..e18434329079b68d1bc4a13af4a06698dd88d9d8 100644 --- a/client/db/src/cache/list_entry.rs +++ b/client/db/src/cache/list_entry.rs @@ -69,7 +69,7 @@ impl Entry { .map(|(entry, next)| (entry.valid_from, next))) } - /// Searches the list, ending with THIS entry for the best entry preceeding (or at) + /// Searches the list, ending with THIS entry for the best entry preceding (or at) /// given block number. /// If the entry is found, result is the entry and the block id of next entry (if exists). /// NOTE that this function does not check that the passed block is actually linked to diff --git a/client/db/src/changes_tries_storage.rs b/client/db/src/changes_tries_storage.rs index 72163a56942132ac73f203d204b6721be46f7f98..99488bbaed0964d4509e38b94c37d68dbe2cfb24 100644 --- a/client/db/src/changes_tries_storage.rs +++ b/client/db/src/changes_tries_storage.rs @@ -48,7 +48,7 @@ pub fn extract_new_configuration(header: &Header) -> Option<&Op /// Opaque configuration cache transaction. During its lifetime, no-one should modify cache. This is currently /// guaranteed because import lock is held during block import/finalization. pub struct DbChangesTrieStorageTransaction { - /// Cache operations that must be performed after db transaction is comitted. + /// Cache operations that must be performed after db transaction is committed. cache_ops: DbCacheTransactionOps, /// New configuration (if changed at current block). new_config: Option>, diff --git a/client/db/src/children.rs b/client/db/src/children.rs index c90af66027fdce5ef4e202ab7f9c94dec3f28f8d..2ef67de6a83e1116d20483982ff284ecbd5adf08 100644 --- a/client/db/src/children.rs +++ b/client/db/src/children.rs @@ -100,7 +100,7 @@ mod tests { children2.push(1_6); write_children(&mut tx, 0, PREFIX, 1_2, children2); - db.write(tx.clone()).expect("(2) Commiting transaction failed"); + db.write(tx.clone()).expect("(2) Committing transaction failed"); let r1: Vec = read_children(&db, 0, PREFIX, 1_1).expect("(1) Getting r1 failed"); let r2: Vec = read_children(&db, 0, PREFIX, 1_2).expect("(1) Getting r2 failed"); @@ -109,7 +109,7 @@ mod tests { assert_eq!(r2, vec![1_4, 1_6]); remove_children(&mut tx, 0, PREFIX, 1_2); - db.write(tx).expect("(2) Commiting transaction failed"); + db.write(tx).expect("(2) Committing transaction failed"); let r1: Vec = read_children(&db, 0, PREFIX, 1_1).expect("(2) Getting r1 failed"); let r2: Vec = read_children(&db, 0, PREFIX, 1_2).expect("(2) Getting r2 failed"); diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index be569194972cc80a1f26b9505a9d256f02381769..5173497509cf9ad909813d0baa17df72fe406979 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,33 +715,33 @@ 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() } } } /// Disk backend. /// -/// Disk backend keps data in a key-value store. In archive mode, trie nodes are kept from all blocks. +/// Disk backend keeps data in a key-value store. In archive mode, trie nodes are kept from all blocks. /// Otherwise, trie nodes are kept only from some recent blocks. pub struct Backend { storage: Arc>, @@ -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(), }) } @@ -872,7 +878,7 @@ impl Backend { inmem } - /// Returns total numbet of blocks (headers) in the block DB. + /// Returns total number of blocks (headers) in the block DB. #[cfg(feature = "test-helpers")] pub fn blocks_count(&self) -> u64 { self.blockchain.db.iter(columns::HEADER).count() as u64 @@ -994,7 +1000,7 @@ impl Backend { Ok((*hash, number, false, true)) } - // performs forced canonicaliziation with a delay after importing a non-finalized block. + // performs forced canonicalization with a delay after importing a non-finalized block. fn force_delayed_canonicalize( &self, transaction: &mut DBTransaction, @@ -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..14ce6ac0f9a05d5511698b169a117d8fbd888d9a 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, } }) } @@ -814,7 +815,7 @@ pub(crate) mod tests { } #[test] - fn get_cht_fails_for_non_existant_cht() { + fn get_cht_fails_for_non_existent_cht() { let cht_size: u64 = cht::size(); assert!(LightStorage::::new_test().header_cht_root(cht_size, cht_size / 2).unwrap().is_none()); } diff --git a/client/db/src/storage_cache.rs b/client/db/src/storage_cache.rs index fd85a899b628ef78b377e3035e4f7dc370319a12..6ef29f47b8c42c1b5c5702cc9724da611d87d2f2 100644 --- a/client/db/src/storage_cache.rs +++ b/client/db/src/storage_cache.rs @@ -338,7 +338,7 @@ impl CacheChanges { is_best, ); let cache = &mut *cache; - // Filter out commiting block if any. + // Filter out committing block if any. let enacted: Vec<_> = enacted .iter() .filter(|h| commit_hash.as_ref().map_or(true, |p| *h != p)) @@ -1453,7 +1453,7 @@ mod qc { self.head_state( self.canon.last() - .expect("wasn't forking to emptiness so there shoud be one!") + .expect("wasn't forking to emptiness so there should be one!") .hash ) }, diff --git a/client/db/src/upgrade.rs b/client/db/src/upgrade.rs index ab2d4bbf799b25cbf276db4b2b9a4faeb908d948..971acf8456b6463fe315768f16e2f8cf3af8254b 100644 --- a/client/db/src/upgrade.rs +++ b/client/db/src/upgrade.rs @@ -118,7 +118,7 @@ fn current_version(path: &Path) -> sp_blockchain::Result { } } -/// Opens database of givent type with given number of columns. +/// Opens database of given type with given number of columns. fn open_database(db_path: &Path, db_type: DatabaseType, db_columns: u32) -> sp_blockchain::Result { let db_path = db_path.to_str() .ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?; @@ -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..66d520e942411d01c540bd64f7bde4147be28c74 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -27,7 +27,7 @@ pub type Result = std::result::Result; pub enum Error { /// Unserializable Data InvalidData(sp_serializer::Error), - /// Trap occured during execution + /// Trap occurred during execution Trap(wasmi::Trap), /// Wasmi loading/instantiating error Wasmi(wasmi::Error), @@ -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..ccfdc2f3e0e28c2e8b6a1b1734ee8be450b088ff 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,18 +491,14 @@ 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. /// /// This is generic over a supervisor function reference type. pub struct Store { - // Memories and instances are `Some` untill torndown. + // Memories and instances are `Some` until torn down. instances: Vec>>>, memories: Vec>, } 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..a0e11dfcf8b93f4799624688e84c8e109f98ca9d 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 + /// resolving. 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..159746801a52aa68e45c63b4f060a1804415a2f8 --- /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 `instance`. + // + // 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("Unknown 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..050a3c8642fde81056af71f6076fd5534f9c8a6d 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; @@ -80,7 +74,7 @@ mod cost { pub(super) const MALFORMED_CATCH_UP: Rep = Rep::new(-1000, "Grandpa: Malformed cath-up"); pub(super) const MALFORMED_COMMIT: Rep = Rep::new(-1000, "Grandpa: Malformed commit"); pub(super) const FUTURE_MESSAGE: Rep = Rep::new(-500, "Grandpa: Future message"); - pub(super) const UNKNOWN_VOTER: Rep = Rep::new(-150, "Grandpa: Uknown voter"); + pub(super) const UNKNOWN_VOTER: Rep = Rep::new(-150, "Grandpa: Unknown voter"); pub(super) const INVALID_VIEW_CHANGE: Rep = Rep::new(-500, "Grandpa: Invalid view change"); pub(super) const PER_UNDECODABLE_BYTE: i32 = -5; @@ -89,7 +83,7 @@ mod cost { pub(super) const INVALID_CATCH_UP: Rep = Rep::new(-5000, "Grandpa: Invalid catch-up"); pub(super) const INVALID_COMMIT: Rep = Rep::new(-5000, "Grandpa: Invalid commit"); pub(super) const OUT_OF_SCOPE_MESSAGE: Rep = Rep::new(-500, "Grandpa: Out-of-scope message"); - pub(super) const CATCH_UP_REQUEST_TIMEOUT: Rep = Rep::new(-200, "Grandpa: Catch-up reqeust timeout"); + pub(super) const CATCH_UP_REQUEST_TIMEOUT: Rep = Rep::new(-200, "Grandpa: Catch-up request timeout"); // cost of answering a catch up request pub(super) const CATCH_UP_REPLY: Rep = Rep::new(-200, "Grandpa: Catch-up reply"); @@ -159,18 +153,18 @@ pub(crate) struct NetworkBridge> { /// `NeighborPacketWorker` processing packets sent through the `NeighborPacketSender`. // - // `NetworkBridge` is required to be clonable, thus one needs to be able to clone its children, - // thus one has to wrap neighor_packet_worker with an `Arc` `Mutex`. + // `NetworkBridge` is required to be cloneable, thus one needs to be able to clone its children, + // thus one has to wrap `neighbor_packet_worker` with an `Arc` `Mutex`. neighbor_packet_worker: Arc>>, /// Receiver side of the peer report stream populated by the gossip validator, forwarded to the /// gossip engine. // - // `NetworkBridge` is required to be clonable, thus one needs to be able to clone its children, + // `NetworkBridge` is required to be cloneable, thus one needs to be able to clone its children, // 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/finality_proof.rs b/client/finality-grandpa/src/finality_proof.rs index fb0f7fd4a9bab66e91447209c7457d1e7ff3aca8..9da99ab531ae59044c71b10794e0ced3b1546632 100644 --- a/client/finality-grandpa/src/finality_proof.rs +++ b/client/finality-grandpa/src/finality_proof.rs @@ -920,7 +920,7 @@ pub(crate) mod tests { } #[test] - fn finality_proof_check_fails_when_intemediate_fragment_has_unknown_headers() { + fn finality_proof_check_fails_when_intermediate_fragment_has_unknown_headers() { let blockchain = test_blockchain(); // when intermediate (#0) fragment has non-empty unknown headers @@ -945,7 +945,7 @@ pub(crate) mod tests { } #[test] - fn finality_proof_check_fails_when_intemediate_fragment_has_no_authorities_proof() { + fn finality_proof_check_fails_when_intermediate_fragment_has_no_authorities_proof() { let blockchain = test_blockchain(); // when intermediate (#0) fragment has empty authorities proof @@ -1004,7 +1004,7 @@ pub(crate) mod tests { #[test] fn finality_proof_is_none_if_first_justification_is_generated_by_unknown_set() { // this is the case for forced change: set_id has been forcibly increased on full node - // and ligh node missed that + // and light node missed that // => justification verification will fail on light node anyways, so we do not return // finality proof at all let blockchain = test_blockchain(); diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index ad1b2b1a87fb6ad1b2995116baffdf9a0befb142..2eb1b4a7c889c3929c57ac3149e373a6c86c736f 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}; @@ -398,7 +398,7 @@ impl BlockImport mut block: BlockImportParams, new_cache: HashMap>, ) -> Result { - let hash = block.post_header().hash(); + let hash = block.post_hash(); let number = block.header.number().clone(); // early exit if block already in chain, otherwise the check for diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 071214961f9a14917b1d0e8e421da3a6ec843baa..e931271df91001b79276f12fd5acb1d36642ff33 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,77 +820,43 @@ 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 - Block::Hash: Ord, - B: Backend + 'static, - E: CallExecutor + Send + Sync + 'static, - N: NetworkT + Send + Sync + Clone + 'static, - SC: SelectChain + 'static, - NumberFor: BlockNumberOps, - DigestFor: Encode, - RA: Send + Sync + 'static, - VR: VotingRule> + Clone + 'static, - X: futures03::Future + Clone + Send + Unpin + 'static, - Client: AuxStore, - Sp: futures03::task::Spawn + 'static, -{ - run_grandpa_voter(grandpa_params) -} - /// When GRANDPA is not initialized we still need to register the finality /// tracker inherent provider which might be expected by the runtime for block /// authoring. Additionally, we register a gossip message validator that diff --git a/client/finality-grandpa/src/light_import.rs b/client/finality-grandpa/src/light_import.rs index 0da22487789220c03ef201dfcf0c05be35d75d52..0181a93f1088cb35c46715a9db4f32b937950f21 100644 --- a/client/finality-grandpa/src/light_import.rs +++ b/client/finality-grandpa/src/light_import.rs @@ -257,7 +257,7 @@ fn do_import_block( DigestFor: Encode, J: ProvableJustification, { - let hash = block.post_header().hash(); + let hash = block.post_hash(); let number = block.header.number().clone(); // we don't want to finalize on `inner.import_block` @@ -682,25 +682,19 @@ pub mod tests { authority_set: LightAuthoritySet::genesis(vec![(AuthorityId::from_slice(&[1; 32]), 1)]), consensus_changes: ConsensusChanges::empty(), }; - let block = BlockImportParams { - origin: BlockOrigin::Own, - header: Header { + let mut block = BlockImportParams::new( + BlockOrigin::Own, + Header { number: 1, parent_hash: client.chain_info().best_hash, state_root: Default::default(), digest: Default::default(), extrinsics_root: Default::default(), }, - justification, - post_digests: Vec::new(), - body: None, - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: true, - import_existing: false, - }; + ); + block.justification = justification; + block.fork_choice = Some(ForkChoiceStrategy::LongestChain); + do_import_block::<_, _, _, TestJustification>( &client, &mut import_data, diff --git a/client/finality-grandpa/src/observer.rs b/client/finality-grandpa/src/observer.rs index 989a1e1655e8d1109724aa0fb013864bdeec5a99..77227909dc856b0ec0a99d04fefeacd20ad31ce3 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; + + /// 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..9b9063f2c17e973246d58a968dc286120a665224 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::{ @@ -144,7 +147,7 @@ impl TestNetFactory for GrandpaTestNet { use crate::light_import::tests::light_block_import_without_justifications; let authorities_provider = Arc::new(self.test_config.clone()); - // forbid direct finalization using justification that cames with the block + // forbid direct finalization using justification that came with the block // => light clients will try to fetch finality proofs let import = light_block_import_without_justifications( client.clone(), @@ -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, @@ -994,8 +982,8 @@ 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); + // the link-halves have the wrong AuthoritySet + run_to_completion(&mut runtime, 25, net, peers_a); } #[test] @@ -1023,19 +1011,11 @@ fn allows_reimporting_change_blocks() { let block = || { let block = block.clone(); - BlockImportParams { - origin: BlockOrigin::File, - header: block.header, - justification: None, - post_digests: Vec::new(), - body: Some(block.extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - } + let mut import = BlockImportParams::new(BlockOrigin::File, block.header); + import.body = Some(block.extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + import }; assert_eq!( @@ -1082,19 +1062,12 @@ fn test_bad_justification() { let block = || { let block = block.clone(); - BlockImportParams { - origin: BlockOrigin::File, - header: block.header, - justification: Some(Vec::new()), - post_digests: Vec::new(), - body: Some(block.extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - } + let mut import = BlockImportParams::new(BlockOrigin::File, block.header); + import.justification = Some(Vec::new()); + import.body = Some(block.extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + import }; assert_eq!( @@ -1119,11 +1092,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 +1129,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 +1184,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 +1238,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 +1248,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 +1369,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 +1388,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 +1399,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 +1408,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 +1430,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 +1477,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 +1494,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 +1505,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 +1521,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 +1541,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 +1550,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 +1564,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 +1576,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 +1597,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 +1612,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 +1642,6 @@ fn grandpa_environment_respects_voting_rules() { network_service.clone(), config.clone(), set_state.clone(), - &threads_pool, ); Environment { @@ -1820,22 +1788,13 @@ fn imports_justification_for_regular_blocks_on_import() { }; // we import the block with justification attached - let block = BlockImportParams { - origin: BlockOrigin::File, - header: block.header, - justification: Some(justification.encode()), - post_digests: Vec::new(), - body: Some(block.extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(BlockOrigin::File, block.header); + import.justification = Some(justification.encode()); + import.body = Some(block.extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); assert_eq!( - block_import.import_block(block, HashMap::new()).unwrap(), + block_import.import_block(import, HashMap::new()).unwrap(), ImportResult::Imported(ImportedAux { needs_justification: false, clear_justification_requests: 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..087ddf326de0141cc011cf4b046779193b4ba2a1 100644 --- a/client/keystore/src/lib.rs +++ b/client/keystore/src/lib.rs @@ -19,13 +19,10 @@ #![warn(missing_docs)] use std::{collections::HashMap, path::PathBuf, fs::{self, File}, io::{self, Write}, sync::Arc}; - use sp_core::{ crypto::{KeyTypeId, Pair as PairT, Public, IsWrappedBy, Protected}, traits::BareCryptoStore, }; - use sp_application_crypto::{AppKey, AppPublic, AppPair, ed25519, sr25519}; - use parking_lot::RwLock; /// Keystore pointer @@ -72,7 +69,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 +95,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 +174,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 +185,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), @@ -258,7 +257,7 @@ impl Store { /// Get public keys of all stored keys that match the key type. /// /// This will just use the type of the public key (a list of which to be returned) in order - /// to determine the key type. Unless you use a specialised application-type public key, then + /// to determine the key type. Unless you use a specialized application-type public key, then /// this only give you keys registered under generic cryptography, and will not return keys /// registered under the application type. pub fn public_keys(&self) -> Result> { @@ -328,6 +327,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..98b2bd0590af94aeea4a8f87b0c14b47a7bca38b 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.16.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..26433e63ec3ea7e09a6185628f4a5a3ec894e484 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. @@ -42,7 +45,7 @@ mod rep { pub const DUPLICATE_GOSSIP: Rep = Rep::new(-(1 << 2), "Duplicate gossip"); /// Reputation change when a peer sends us a gossip message for an unknown engine, whatever that /// means. - pub const UNKNOWN_GOSSIP: Rep = Rep::new(-(1 << 6), "Unknown gossup message engine id"); + pub const UNKNOWN_GOSSIP: Rep = Rep::new(-(1 << 6), "Unknown gossip message engine id"); /// Reputation change when a peer sends a message from a topic it isn't registered on. pub const UNREGISTERED_TOPIC: Rep = Rep::new(-(1 << 10), "Unregistered gossip message topic"); } @@ -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..e53ba13c8d2e5861a0be48d3a6d76f472134b406 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -6,54 +6,65 @@ 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.16.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.2.0" +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 = ["futures-codec"] } void = "1.0.2" zeroize = "1.0.0" -sp-consensus-babe = { version = "0.8", path = "../../primitives/consensus/babe" } [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..c8c5e59fe62cc3bc34d00aa441194b5993f6cd69 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -19,13 +19,12 @@ 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 libp2p::swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters}; +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}; @@ -39,10 +38,13 @@ pub struct Behaviour, H: ExHashT> { substrate: Protocol, /// Periodically pings and identifies the nodes we are connected to, and store information in a /// cache. - debug_info: debug_info::DebugInfoBehaviour>, + debug_info: debug_info::DebugInfoBehaviour, /// Discovers nodes of the network. - discovery: DiscoveryBehaviour>, - + discovery: DiscoveryBehaviour, + /// Block request handling. + block_requests: protocol::BlockRequests, + /// Light client request handling. + light_client_handler: protocol::LightClientHandler, /// Queue of events to produce for the outside. #[behaviour(ignore)] events: Vec>, @@ -65,6 +67,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, + light_client_handler: protocol::LightClientHandler, ) -> Self { Behaviour { substrate, @@ -73,9 +78,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 +125,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 +158,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 +178,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, @@ -211,7 +222,7 @@ impl, H: ExHashT> NetworkBehaviourEventPr } impl, H: ExHashT> Behaviour { - fn poll(&mut self, _: &mut Context) -> Poll>> { + fn poll(&mut self, _: &mut Context, _: &mut impl PollParameters) -> Poll>> { if !self.events.is_empty() { return Poll::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))) } diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 6a2062ddbf253da685e8473e4008332ef1ee03a0..2f920c6663955aad80c8525543c9fca490887b79 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, } @@ -332,8 +338,9 @@ pub enum TransportConfig { enable_mdns: bool, /// If true, allow connecting to private IPv4 addresses (as defined in - /// [RFC1918](https://tools.ietf.org/html/rfc1918)), unless the address has been passed in - /// [`NetworkConfiguration::reserved_nodes`] or [`NetworkConfiguration::boot_nodes`]. + /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Irrelevant for addresses that have + /// been passed in [`NetworkConfiguration::reserved_nodes`] or + /// [`NetworkConfiguration::boot_nodes`]. allow_private_ipv4: bool, /// Optional external implementation of a libp2p transport. Used in WASM contexts where we @@ -343,6 +350,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..17fb622f7cd3cab44f3b17ab508d7f1a3b258882 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. @@ -36,11 +39,11 @@ const GARBAGE_COLLECT_INTERVAL: Duration = Duration::from_secs(2 * 60); /// Implementation of `NetworkBehaviour` that holds information about nodes in cache for diagnostic /// purposes. -pub struct DebugInfoBehaviour { +pub struct DebugInfoBehaviour { /// Periodically ping nodes, and close the connection if it's unresponsive. - ping: Ping, + ping: Ping, /// Periodically identifies the remote and responds to incoming requests. - identify: Identify, + identify: Identify, /// Information that we know about all nodes. nodes_info: FnvHashMap, /// Interval at which we perform garbage collection in `nodes_info`. @@ -61,7 +64,7 @@ struct NodeInfo { latest_ping: Option, } -impl DebugInfoBehaviour { +impl DebugInfoBehaviour { /// Builds a new `DebugInfoBehaviour`. pub fn new( user_agent: String, @@ -148,11 +151,10 @@ pub enum DebugInfoEvent { }, } -impl NetworkBehaviour for DebugInfoBehaviour -where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static { +impl NetworkBehaviour for DebugInfoBehaviour { type ProtocolsHandler = IntoProtocolsHandlerSelect< - as NetworkBehaviour>::ProtocolsHandler, - as NetworkBehaviour>::ProtocolsHandler + ::ProtocolsHandler, + ::ProtocolsHandler >; type OutEvent = DebugInfoEvent; @@ -251,6 +253,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..a236ceb1a8b6d8ba4e3981f83e7577fb06224d4e 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; @@ -55,25 +55,23 @@ use libp2p::kad::record::{self, store::MemoryStore}; #[cfg(not(target_os = "unknown"))] use libp2p::{swarm::toggle::Toggle}; #[cfg(not(target_os = "unknown"))] -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; /// Implementation of `NetworkBehaviour` that discovers the nodes on the network. -pub struct DiscoveryBehaviour { +pub struct DiscoveryBehaviour { /// User-defined list of nodes and their addresses. Typically includes bootstrap nodes and /// reserved nodes. user_defined: Vec<(PeerId, Multiaddr)>, /// Kademlia requests and answers. - kademlia: Kademlia, + kademlia: Kademlia, /// Discovers nodes on the local network. #[cfg(not(target_os = "unknown"))] - mdns: Toggle>>, + mdns: Toggle, /// Stream that fires when we need to perform the next random Kademlia query. next_kad_random_query: Delay, /// After `next_kad_random_query` triggers, the next one triggers after this duration. @@ -87,9 +85,11 @@ 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 { +impl DiscoveryBehaviour { /// Builds a new `DiscoveryBehaviour`. /// /// `user_defined` is a list of known address for nodes that never expire. @@ -98,6 +98,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 +121,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); @@ -190,7 +192,7 @@ pub enum DiscoveryOut { /// e.g. obtained through the `identify` protocol. UnroutablePeer(PeerId), - /// The DHT yeided results for the record request, grouped in (key, value) pairs. + /// The DHT yielded results for the record request, grouped in (key, value) pairs. ValueFound(Vec<(record::Key, Vec)>), /// The record requested was not found in the DHT. @@ -203,11 +205,8 @@ pub enum DiscoveryOut { ValuePutFailed(record::Key), } -impl NetworkBehaviour for DiscoveryBehaviour -where - TSubstream: AsyncRead + AsyncWrite + Unpin, -{ - type ProtocolsHandler = as NetworkBehaviour>::ProtocolsHandler; +impl NetworkBehaviour for DiscoveryBehaviour { + type ProtocolsHandler = as NetworkBehaviour>::ProtocolsHandler; type OutEvent = DiscoveryOut; fn new_handler(&mut self) -> Self::ProtocolsHandler { @@ -266,6 +265,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 +286,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 +330,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 +442,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 +513,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..d5e7ae6252ca147e59da50be6c22b8acb7d772f0 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}; use libp2p::swarm::{ProtocolsHandler, IntoProtocolsHandler}; use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use sp_core::storage::{StorageKey, ChildInfo}; @@ -52,20 +52,36 @@ 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); -/// Interval at which we propagate exstrinsics; +/// Interval at which we propagate extrinsics; const PROPAGATE_TIMEOUT: time::Duration = time::Duration::from_millis(2900); /// Maximim number of known block hashes to keep for a peer. @@ -81,7 +97,7 @@ pub(crate) const MIN_VERSION: u32 = 3; // Maximum allowed entries in `BlockResponse` const MAX_BLOCK_DATA_RESPONSE: u32 = 128; /// When light node connects to the full node and the full node is behind light node -/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it unuseful +/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful /// and disconnect to free connection slot. const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192; @@ -142,7 +158,7 @@ pub struct Protocol, H: ExHashT> { /// When asked for a proof of finality, we use this struct to build one. finality_proof_provider: Option>>, /// Handles opening the unique substream and sending and receiving raw messages. - behaviour: LegacyProto>, + behaviour: LegacyProto, /// List of notification protocols that have been registered. registered_notif_protocols: HashSet, } @@ -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)>, - /// Requests we are no longer insterested in. - obsolete_requests: HashMap, + block_request: Option<(Instant, message::BlockRequest)>, + /// Requests we are no longer interested in. + obsolete_requests: HashMap, /// Holds a set of transactions known to this peer. known_extrinsics: LruHashSet, /// Holds a set of blocks known to this peer. @@ -191,7 +207,7 @@ pub struct PeerInfo { } struct LightDispatchIn<'a> { - behaviour: &'a mut LegacyProto>, + behaviour: &'a mut LegacyProto, peerset: sc_peerset::PeersetHandle, } @@ -331,7 +347,7 @@ pub trait Context { /// Protocol context. struct ProtocolContext<'a, B: 'a + BlockT, H: 'a + ExHashT> { - behaviour: &'a mut LegacyProto>, + behaviour: &'a mut LegacyProto, context_data: &'a mut ContextData, peerset_handle: &'a sc_peerset::PeersetHandle, } @@ -339,7 +355,7 @@ struct ProtocolContext<'a, B: 'a + BlockT, H: 'a + ExHashT> { impl<'a, B: BlockT + 'a, H: 'a + ExHashT> ProtocolContext<'a, B, H> { fn new( context_data: &'a mut ContextData, - behaviour: &'a mut LegacyProto>, + behaviour: &'a mut LegacyProto, peerset_handle: &'a sc_peerset::PeersetHandle, ) -> Self { ProtocolContext { context_data, peerset_handle, behaviour } @@ -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); } @@ -848,8 +864,6 @@ impl, H: ExHashT> Protocol { blocks_range ); - // TODO [andre]: move this logic to the import queue so that - // justifications are imported asynchronously (#1482) if request.fields == message::BlockAttributes::JUSTIFICATION { match self.sync.on_block_justification(peer, response) { Ok(sync::OnBlockJustification::Nothing) => CustomMessageOutcome::None, @@ -890,21 +904,21 @@ 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() { if peer.block_request.as_ref().map_or(false, |(t, _)| (tick - *t).as_secs() > REQUEST_TIMEOUT_SEC) { log!( target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Trace }, + if self.important_peers.contains(who) { Level::Warn } else { Level::Trace }, "Request timeout {}", who ); aborting.push(who.clone()); } else if peer.obsolete_requests.values().any(|t| (tick - *t).as_secs() > REQUEST_TIMEOUT_SEC) { log!( target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Trace }, + if self.important_peers.contains(who) { Level::Warn } else { Level::Trace }, "Obsolete timeout {}", who ); aborting.push(who.clone()); @@ -915,7 +929,7 @@ impl, H: ExHashT> Protocol { { log!( target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Trace }, + if self.important_peers.contains(who) { Level::Warn } else { Level::Trace }, "Handshake timeout {}", who ); aborting.push(who.clone()); @@ -1135,18 +1149,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 +1199,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,14 +1826,14 @@ 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, } fn send_request( - behaviour: &mut LegacyProto>, + behaviour: &mut LegacyProto, stats: &mut HashMap<&'static str, PacketStats>, peers: &mut HashMap>, who: &PeerId, @@ -1813,14 +1847,14 @@ 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) } fn send_message( - behaviour: &mut LegacyProto>, + behaviour: &mut LegacyProto, stats: &mut HashMap<&'static str, PacketStats>, who: &PeerId, message: Message, @@ -1834,7 +1868,7 @@ fn send_message( impl, H: ExHashT> NetworkBehaviour for Protocol { - type ProtocolsHandler = > as NetworkBehaviour>::ProtocolsHandler; + type ProtocolsHandler = ::ProtocolsHandler; type OutEvent = CustomMessageOutcome; fn new_handler(&mut self) -> Self::ProtocolsHandler { @@ -1931,7 +1965,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 +2018,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..ef970657c5ff9477bc939b6dd98963b4e41387e9 --- /dev/null +++ b/client/network/src/protocol/block_requests.rs @@ -0,0 +1,356 @@ +// 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::{ + NegotiatedSubstream, + 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>, +} + +impl BlockRequests +where + B: Block, +{ + pub fn new(cfg: Config, chain: Arc>) -> Self { + BlockRequests { + config: cfg, + chain, + outgoing: FuturesUnordered::new(), + } + } + + /// 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 + 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..78490863be9c250ad78605f634c95670ab8bf9ce 100644 --- a/client/network/src/protocol/event.rs +++ b/client/network/src/protocol/event.rs @@ -36,7 +36,7 @@ pub enum DhtEvent { /// The record has been successfully inserted into the DHT. ValuePut(Key), - /// An error has occured while putting a record into the DHT. + /// An error has occurred while putting a record into the DHT. ValuePutFailed(Key), } @@ -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..69c89be9a36957f808773a76d54e8b43ea195251 100644 --- a/client/network/src/protocol/legacy_proto/behaviour.rs +++ b/client/network/src/protocol/legacy_proto/behaviour.rs @@ -25,8 +25,9 @@ use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters}; 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::{borrow::Cow, collections::hash_map::Entry, cmp, error, mem, pin::Pin}; +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. @@ -59,7 +60,7 @@ use std::task::{Context, Poll}; /// Note that this "banning" system is not an actual ban. If a "banned" node tries to connect to /// us, we accept the connection. The "banning" system is only about delaying dialing attempts. /// -pub struct LegacyProto< TSubstream> { +pub struct LegacyProto { /// List of protocols to open with peers. Never modified. protocol: RegisteredProtocol, @@ -79,9 +80,6 @@ pub struct LegacyProto< TSubstream> { /// Events to produce from `poll()`. events: SmallVec<[NetworkBehaviourAction; 4]>, - - /// Marker to pin the generics. - marker: PhantomData, } /// State of a peer we're connected to. @@ -224,7 +222,7 @@ pub enum LegacyProtoOut { }, } -impl LegacyProto { +impl LegacyProto { /// Creates a `CustomProtos`. pub fn new( protocol: impl Into, @@ -240,7 +238,6 @@ impl LegacyProto { incoming: SmallVec::new(), next_incoming_index: sc_peerset::IncomingIndex(0), events: SmallVec::new(), - marker: PhantomData, } } @@ -382,12 +379,14 @@ impl LegacyProto { } }; + let now = Instant::now(); + match mem::replace(occ_entry.get_mut(), PeerState::Poisoned) { - PeerState::Banned { ref until } if *until > Instant::now() => { + PeerState::Banned { ref until } if *until > now => { 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() - now), timer_deadline: until.clone(), }; }, @@ -400,13 +399,13 @@ impl LegacyProto { }, PeerState::Disabled { open, ref connected_point, banned_until: Some(ref banned) } - if *banned > Instant::now() => { + if *banned > now => { debug!(target: "sub-libp2p", "PSM => Connect({:?}): Has idle connection through \ {:?} but node is banned until {:?}", occ_entry.key(), connected_point, banned); *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() - now), timer_deadline: banned.clone(), }; }, @@ -604,7 +603,7 @@ impl LegacyProto { } } -impl DiscoveryNetBehaviour for LegacyProto { +impl DiscoveryNetBehaviour for LegacyProto { fn add_discovered_nodes(&mut self, peer_ids: impl Iterator) { self.peerset.discovered(peer_ids.into_iter().map(|peer_id| { debug!(target: "sub-libp2p", "PSM <= Discovered({:?})", peer_id); @@ -613,11 +612,8 @@ impl DiscoveryNetBehaviour for LegacyProto { } } -impl NetworkBehaviour for LegacyProto -where - TSubstream: AsyncRead + AsyncWrite + Unpin, -{ - type ProtocolsHandler = CustomProtoHandlerProto; +impl NetworkBehaviour for LegacyProto { + type ProtocolsHandler = CustomProtoHandlerProto; type OutEvent = LegacyProtoOut; fn new_handler(&mut self) -> Self::ProtocolsHandler { diff --git a/client/network/src/protocol/legacy_proto/handler.rs b/client/network/src/protocol/legacy_proto/handler.rs index f042d1b88925f409981146995995884304849d5e..e3490993dd46d05549e656b124883f1976a4d062 100644 --- a/client/network/src/protocol/legacy_proto/handler.rs +++ b/client/network/src/protocol/legacy_proto/handler.rs @@ -26,10 +26,11 @@ use libp2p::swarm::{ KeepAlive, ProtocolsHandlerUpgrErr, SubstreamProtocol, + NegotiatedSubstream, }; use log::{debug, error}; use smallvec::{smallvec, SmallVec}; -use std::{borrow::Cow, error, fmt, io, marker::PhantomData, mem, time::Duration}; +use std::{borrow::Cow, error, fmt, io, mem, time::Duration}; use std::{pin::Pin, task::{Context, Poll}}; /// Implements the `IntoProtocolsHandler` trait of libp2p. @@ -86,32 +87,22 @@ use std::{pin::Pin, task::{Context, Poll}}; /// We consider that we are now "closed" if the remote closes all the existing substreams. /// Re-opening it can then be performed by closing all active substream and re-opening one. /// -pub struct CustomProtoHandlerProto { +pub struct CustomProtoHandlerProto { /// Configuration for the protocol upgrade to negotiate. protocol: RegisteredProtocol, - - /// Marker to pin the generic type. - marker: PhantomData, } -impl CustomProtoHandlerProto -where - TSubstream: AsyncRead + AsyncWrite + Unpin, -{ +impl CustomProtoHandlerProto { /// Builds a new `CustomProtoHandlerProto`. pub fn new(protocol: RegisteredProtocol) -> Self { CustomProtoHandlerProto { protocol, - marker: PhantomData, } } } -impl IntoProtocolsHandler for CustomProtoHandlerProto -where - TSubstream: AsyncRead + AsyncWrite + Unpin, -{ - type Handler = CustomProtoHandler; +impl IntoProtocolsHandler for CustomProtoHandlerProto { + type Handler = CustomProtoHandler; fn inbound_protocol(&self) -> RegisteredProtocol { self.protocol.clone() @@ -132,12 +123,12 @@ where } /// The actual handler once the connection has been established. -pub struct CustomProtoHandler { +pub struct CustomProtoHandler { /// Configuration for the protocol upgrade to negotiate. protocol: RegisteredProtocol, /// State of the communications with the remote. - state: ProtocolState, + state: ProtocolState, /// Identifier of the node we're talking to. Used only for logging purposes and shouldn't have /// any influence on the behaviour. @@ -155,11 +146,11 @@ pub struct CustomProtoHandler { } /// State of the handler. -enum ProtocolState { +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 +166,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 +176,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. @@ -257,10 +248,7 @@ pub enum CustomProtoHandlerOut { }, } -impl CustomProtoHandler -where - TSubstream: AsyncRead + AsyncWrite + Unpin, -{ +impl CustomProtoHandler { /// Enables the handler. fn enable(&mut self) { self.state = match mem::replace(&mut self.state, ProtocolState::Poisoned) { @@ -348,13 +336,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 +350,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 +363,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 +447,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 => { @@ -522,11 +503,9 @@ where } } -impl ProtocolsHandler for CustomProtoHandler -where TSubstream: AsyncRead + AsyncWrite + Unpin { +impl ProtocolsHandler for CustomProtoHandler { type InEvent = CustomProtoHandlerIn; type OutEvent = CustomProtoHandlerOut; - type Substream = TSubstream; type Error = ConnectionKillError; type InboundProtocol = RegisteredProtocol; type OutboundProtocol = RegisteredProtocol; @@ -538,14 +517,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); @@ -608,10 +587,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin { } } -impl fmt::Debug for CustomProtoHandler -where - TSubstream: AsyncRead + AsyncWrite + Unpin, -{ +impl fmt::Debug for CustomProtoHandler { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { f.debug_struct("CustomProtoHandler") .finish() @@ -620,10 +596,10 @@ 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>>, +fn shutdown_list + (list: &mut SmallVec>>, cx: &mut Context) -where TSubstream: AsyncRead + AsyncWrite + Unpin { +{ 'outer: for n in (0..list.len()).rev() { let mut substream = list.swap_remove(n); loop { diff --git a/client/network/src/protocol/legacy_proto/tests.rs b/client/network/src/protocol/legacy_proto/tests.rs index 6a2174f30c9370bc3b26410a86a9ae264fb2b797..89b0854d9081f82c80e8f0dab8da0e138ebb4fca 100644 --- a/client/network/src/protocol/legacy_proto/tests.rs +++ b/client/network/src/protocol/legacy_proto/tests.rs @@ -18,24 +18,20 @@ use futures::{prelude::*, ready}; use codec::{Encode, Decode}; -use libp2p::core::nodes::Substream; -use libp2p::core::{ConnectedPoint, transport::boxed::Boxed, muxing::StreamMuxerBox}; +use libp2p::core::nodes::listeners::ListenerId; +use libp2p::core::ConnectedPoint; 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; /// Builds two nodes that have each other as bootstrap nodes. /// This is to be used only for testing, and a panic will happen if something goes wrong. -fn build_nodes() --> ( - Swarm, CustomProtoWithAddr>, - Swarm, CustomProtoWithAddr> -) { +fn build_nodes() -> (Swarm, Swarm) { let mut out = Vec::with_capacity(2); let keypairs: Vec<_> = (0..2).map(|_| libp2p::identity::Keypair::generate_ed25519()).collect(); @@ -115,12 +111,12 @@ fn build_nodes() /// Wraps around the `CustomBehaviour` network behaviour, and adds hardcoded node addresses to it. struct CustomProtoWithAddr { - inner: LegacyProto>, + inner: LegacyProto, addrs: Vec<(PeerId, Multiaddr)>, } impl std::ops::Deref for CustomProtoWithAddr { - type Target = LegacyProto>; + type Target = LegacyProto; fn deref(&self) -> &Self::Target { &self.inner @@ -134,9 +130,8 @@ impl std::ops::DerefMut for CustomProtoWithAddr { } impl NetworkBehaviour for CustomProtoWithAddr { - type ProtocolsHandler = - > as NetworkBehaviour>::ProtocolsHandler; - type OutEvent = > as NetworkBehaviour>::OutEvent; + type ProtocolsHandler = ::ProtocolsHandler; + type OutEvent = ::OutEvent; fn new_handler(&mut self) -> Self::ProtocolsHandler { self.inner.new_handler() @@ -204,6 +199,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 +404,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..77cf71408d6c49f78511cb6c6936820eaa126c1e --- /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::{ + NegotiatedSubstream, + 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, + task::{Context, Poll} +}; +use void::Void; +use wasm_timer::Instant; + +/// 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, +} + +impl LightClientHandler +where + 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, + } + } + + /// 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 + 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 = serialize_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 serialize 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 serialize_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 serialized 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; + type Swarm = libp2p::swarm::Swarm; + + 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 + { + 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> { + 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..738847dbaf3cf2229503e53855f2d18318ffad5b 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 { @@ -226,7 +227,7 @@ impl FetchChecker for AlwaysBadChecker { impl LightDispatch where B::Header: HeaderT, { - /// Creates new light client requests processer. + /// Creates new light client requests processor. pub fn new(checker: Arc>) -> Self { LightDispatch { checker, @@ -503,7 +504,7 @@ impl LightDispatch where } pub fn is_light_response(&self, peer: &PeerId, request_id: message::RequestId) -> bool { - self.active_peers.get(&peer).map_or(false, |r| r.id == request_id) + self.active_peers.get(peer).map_or(false, |r| r.id == request_id) } fn remove(&mut self, peer: PeerId, id: u64) -> Option> { @@ -566,7 +567,7 @@ impl LightDispatch where // return peer to the back of the queue self.idle_peers.push_back(peer.clone()); - // we have enumerated all peers and noone can handle request + // we have enumerated all peers and no one can handle request if Some(peer) == last_peer { let request = self.pending_requests.pop_front().expect("checked in loop condition; qed"); unhandled_requests.push_back(request); @@ -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/message.rs b/client/network/src/protocol/message.rs index ef7d550de6cbeac8f0250aae5ebe419d04209ab9..a2261b2059110af0049e520a093f0acf262512c1 100644 --- a/client/network/src/protocol/message.rs +++ b/client/network/src/protocol/message.rs @@ -209,13 +209,13 @@ pub mod generic { RemoteHeaderResponse(RemoteHeaderResponse
), /// Remote changes request. RemoteChangesRequest(RemoteChangesRequest), - /// Remote changes reponse. + /// Remote changes response. RemoteChangesResponse(RemoteChangesResponse), /// Remote child storage read request. RemoteReadChildRequest(RemoteReadChildRequest), /// Finality proof request. FinalityProofRequest(FinalityProofRequest), - /// Finality proof reponse. + /// Finality proof response. FinalityProofResponse(FinalityProofResponse), /// Batch of consensus protocol messages. ConsensusBatch(Vec), 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..73128c53de46635a1af74cd78c6f19087028af7a --- /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 descending 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/specialization.rs b/client/network/src/protocol/specialization.rs index af6d5f7a239cd8f384d53955e3efbfb3372f28eb..6ffa335c8c330fb1689acf7176c8442dd31d4e8a 100644 --- a/client/network/src/protocol/specialization.rs +++ b/client/network/src/protocol/specialization.rs @@ -41,14 +41,6 @@ pub trait NetworkSpecialization: Send + Sync + 'static { message: Vec ); - /// Called when a network-specific event arrives. - #[deprecated(note = "This method is never called; please use `with_dht_event_tx` when building the service")] - fn on_event(&mut self, _event: Event) {} - - /// Called on abort. - #[deprecated(note = "This method is never called; aborting corresponds to dropping the object")] - fn on_abort(&mut self) { } - /// Called periodically to maintain peers and handle timeouts. fn maintain_peers(&mut self, _ctx: &mut dyn Context) { } @@ -162,17 +154,6 @@ macro_rules! construct_simple_protocol { $( self.$sub_protocol_name.on_message(_ctx, _who, _message); )* } - fn on_event( - &mut self, - _event: $crate::specialization::Event - ) { - $( self.$sub_protocol_name.on_event(_event); )* - } - - fn on_abort(&mut self) { - $( self.$sub_protocol_name.on_abort(); )* - } - fn maintain_peers(&mut self, _ctx: &mut $crate::Context<$block>) { $( self.$sub_protocol_name.maintain_peers(_ctx); )* } diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index a513d47ca419862db6598ba4e421a890b0ff3864..88e663c904bb1175704593db5427442c4a2ee7b8 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -751,7 +751,7 @@ impl ChainSync { | PeerSyncState::DownloadingFinalityProof(..) => Vec::new() } } else { - // When request.is_none() just acccept blocks + // When request.is_none() just accept blocks blocks.into_iter().map(|b| { IncomingBlock { hash: b.hash, @@ -813,7 +813,7 @@ impl ChainSync { peer.state = PeerSyncState::Available; // We only request one justification at a time - if let Some(block) = response.blocks.into_iter().next() { + let justification = if let Some(block) = response.blocks.into_iter().next() { if hash != block.hash { info!( target: "sync", @@ -821,13 +821,22 @@ impl ChainSync { ); return Err(BadPeer(who, rep::BAD_JUSTIFICATION)); } - if let Some((peer, hash, number, j)) = self.extra_justifications.on_response(who, block.justification) { - return Ok(OnBlockJustification::Import { peer, hash, number, justification: j }) - } + + block.justification } else { - // we might have asked the peer for a justification on a block that we thought it had - // (regardless of whether it had a justification for it or not). - trace!(target: "sync", "Peer {:?} provided empty response for justification request {:?}", who, hash) + // we might have asked the peer for a justification on a block that we assumed it + // had but didn't (regardless of whether it had a justification for it or not). + trace!(target: "sync", + "Peer {:?} provided empty response for justification request {:?}", + who, + hash, + ); + + None + }; + + if let Some((peer, hash, number, j)) = self.extra_justifications.on_response(who, justification) { + return Ok(OnBlockJustification::Import { peer, hash, number, justification: j }) } } @@ -955,7 +964,7 @@ impl ChainSync { }, Err(BlockImportError::MissingState) => { // This may happen if the chain we were requesting upon has been discarded - // in the meantime becasue other chain has been finalized. + // in the meantime because other chain has been finalized. // Don't mark it as bad as it still may be synced if explicitly requested. trace!(target: "sync", "Obsolete block"); }, @@ -1353,3 +1362,93 @@ fn fork_sync_request( } None } + +#[cfg(test)] +mod test { + use super::*; + use super::message::FromBlock; + use substrate_test_runtime_client::{ + runtime::Block, + DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt, + }; + use sp_blockchain::HeaderBackend; + use sp_consensus::block_validation::DefaultBlockAnnounceValidator; + + #[test] + fn processes_empty_response_on_justification_request_for_unknown_block() { + // if we ask for a justification for a given block to a peer that doesn't know that block + // (different from not having a justification), the peer will reply with an empty response. + // internally we should process the response as the justification not being available. + + let client = Arc::new(TestClientBuilder::new().build()); + let info = client.info(); + let block_announce_validator = Box::new(DefaultBlockAnnounceValidator::new(client.clone())); + let peer_id = PeerId::random(); + + let mut sync = ChainSync::new( + Roles::AUTHORITY, + client.clone(), + &info, + None, + block_announce_validator, + 1, + ); + + let (a1_hash, a1_number) = { + let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + (a1.hash(), *a1.header.number()) + }; + + // add a new peer with the same best block + sync.new_peer(peer_id.clone(), a1_hash, a1_number).unwrap(); + + // and request a justification for the block + sync.request_justification(&a1_hash, a1_number); + + // the justification request should be scheduled to that peer + assert!( + sync.justification_requests().any(|(who, request)| { + who == peer_id && request.from == FromBlock::Hash(a1_hash) + }) + ); + + // there are no extra pending requests + assert_eq!( + sync.extra_justifications.pending_requests().count(), + 0, + ); + + // there's one in-flight extra request to the expected peer + assert!( + sync.extra_justifications.active_requests().any(|(who, (hash, number))| { + *who == peer_id && *hash == a1_hash && *number == a1_number + }) + ); + + // if the peer replies with an empty response (i.e. it doesn't know the block), + // the active request should be cleared. + assert_eq!( + sync.on_block_justification( + peer_id.clone(), + BlockResponse:: { + id: 0, + blocks: vec![], + } + ), + Ok(OnBlockJustification::Nothing), + ); + + // there should be no in-flight requests + assert_eq!( + sync.extra_justifications.active_requests().count(), + 0, + ); + + // and the request should now be pending again, waiting for reschedule + assert!( + sync.extra_justifications.pending_requests().any(|(hash, number)| { + *hash == a1_hash && *number == a1_number + }) + ); + } +} diff --git a/client/network/src/protocol/sync/extra_requests.rs b/client/network/src/protocol/sync/extra_requests.rs index 44b42b154fff2724d78abf36b0d6d5247023f49d..38c250cddf26d920d9e2f2e2e08eed19ac53a3e4 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); @@ -227,6 +228,18 @@ impl ExtraRequests { true } + + /// Returns an iterator over all active (in-flight) requests and associated peer id. + #[cfg(test)] + pub(crate) fn active_requests(&self) -> impl Iterator)> { + self.active_requests.iter() + } + + /// Returns an iterator over all scheduled pending requests. + #[cfg(test)] + pub(crate) fn pending_requests(&self) -> impl Iterator> { + self.pending_requests.iter() + } } /// Matches peers with pending extra requests. @@ -468,7 +481,7 @@ mod tests { } #[test] - fn anecstor_roots_are_finalized_when_finality_notification_is_missed() { + fn ancestor_roots_are_finalized_when_finality_notification_is_missed() { let mut finality_proofs = ExtraRequests::::new("test"); let hash4 = [4; 32].into(); diff --git a/client/network/src/service.rs b/client/network/src/service.rs index 9c9a87bb67e017ea0be8274622d9557ca6ba5ce3..8d990282981bb795ce65db10ef636de943847156 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -35,8 +35,7 @@ use sp_consensus::import_queue::{BlockImportResult, BlockImportError}; 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 +76,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 +116,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 +192,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 +203,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 +242,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. @@ -348,7 +367,7 @@ impl, H: ExHashT> NetworkWorker /// Get network state. /// - /// **Note**: Use this only for debugging. This API is unstable. There are warnings literaly + /// **Note**: Use this only for debugging. This API is unstable. There are warnings literally /// everywhere about this. Please don't use this function to retrieve actual information. pub fn network_state(&mut self) -> NetworkState { let swarm = &mut self.network_service; @@ -477,14 +496,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 +692,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 +732,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 +775,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) => @@ -823,7 +853,6 @@ impl, H: ExHashT> Unpin for Net /// The libp2p swarm, customized for our needs. type Swarm = libp2p::swarm::Swarm< - Boxed<(PeerId, StreamMuxerBox), io::Error>, Behaviour >; diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index d632f9b75c0c9d23d143c83c6e0eee975998fbcf..75ee2d5db89b9c64569c23a70c7384080819ef36 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, noise }; #[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::{tcp, dns, websocket}; use libp2p::core::{self, upgrade, transport::boxed::Boxed, transport::OptionalTransport, muxing::StreamMuxerBox}; use std::{io, sync::Arc, time::Duration, usize}; @@ -38,10 +36,10 @@ 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"))] let noise_config = { let noise_keypair = noise::Keypair::new().into_authentic(&keypair) // For more information about this panic, see in "On the Importance of Checking @@ -52,13 +50,20 @@ 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 mut yamux_config = libp2p::yamux::Config::default(); + yamux_config.set_lazy_open(true); // Only set SYN flag on first data frame sent to the remote. + + if use_yamux_flow_control { + // Enable proper flow-control: window updates are only sent when + // buffered data has been consumed. + yamux_config.set_window_update_mode(libp2p::yamux::WindowUpdateMode::OnRead); + } // Build the base layer of the transport. let transport = if let Some(t) = wasm_external_transport { @@ -89,34 +94,17 @@ pub fn build_transport( let (transport, sinks) = bandwidth::BandwidthLogging::new(transport, Duration::from_secs(5)); // Encryption - - // 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. - #[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))) - }); - // Multiplexing let transport = transport.and_then(move |(stream, peer_id), endpoint| { let peer_id2 = peer_id.clone(); @@ -126,11 +114,12 @@ 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 = transport + .timeout(Duration::from_secs(20)) + .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..339279c704828376d460e17f6480005a1849f023 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.16.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..c15bd3365503f733801e6c3e3e066e8be1c5149a 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; @@ -85,20 +85,13 @@ impl Verifier for PassThroughVerifier { .or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe"))) ) .map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]); + let mut import = BlockImportParams::new(origin, header); + import.body = body; + import.finalized = self.0; + import.justification = justification; + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); - Ok((BlockImportParams { - origin, - header, - body, - storage_changes: None, - finalized: self.0, - justification, - post_digests: vec![], - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }, maybe_keys)) + Ok((import, maybe_keys)) } } @@ -190,7 +183,7 @@ pub struct Peer> { client: PeersClient, /// We keep a copy of the verifier so that we can invoke it for locally-generated blocks, /// instead of going through the import queue. - verifier: VerifierAdapter>, + verifier: VerifierAdapter, /// We keep a copy of the block_import so that we can invoke it for locally-generated blocks, /// instead of going through the import queue. block_import: BlockImportAdapter<()>, @@ -367,6 +360,11 @@ impl> Peer { |backend| backend.blocks_count() ).unwrap_or(0) } + + /// Return a collection of block hashes that failed verification + pub fn failed_verifications(&self) -> HashMap<::Hash, String> { + self.verifier.failed_verifications.lock().clone() + } } pub struct EmptyTransactionPool; @@ -390,6 +388,8 @@ impl TransactionPool for EmptyTransactionPool { ) {} fn on_broadcasted(&self, _: HashMap>) {} + + fn transaction(&self, _h: &Hash) -> Option { None } } pub trait SpecializationFactory { @@ -490,15 +490,13 @@ impl BlockImport for BlockImportAdapter { } /// Implements `Verifier` on an `Arc>`. Used internally. -struct VerifierAdapter(Arc>>); - -impl Clone for VerifierAdapter { - fn clone(&self) -> Self { - VerifierAdapter(self.0.clone()) - } +#[derive(Clone)] +struct VerifierAdapter { + verifier: Arc>>>, + failed_verifications: Arc>>, } -impl> Verifier for VerifierAdapter { +impl Verifier for VerifierAdapter { fn verify( &mut self, origin: BlockOrigin, @@ -506,7 +504,20 @@ impl> Verifier for VerifierAdapter { justification: Option, body: Option> ) -> Result<(BlockImportParams, Option)>>), String> { - self.0.lock().verify(origin, header, justification, body) + let hash = header.hash(); + self.verifier.lock().verify(origin, header, justification, body).map_err(|e| { + self.failed_verifications.lock().insert(hash, e.clone()); + e + }) + } +} + +impl VerifierAdapter { + fn new(verifier: Arc>>>) -> VerifierAdapter { + VerifierAdapter { + verifier, + failed_verifications: Default::default(), + } } } @@ -597,7 +608,7 @@ pub trait TestNetFactory: Sized { config, &data, ); - let verifier = VerifierAdapter(Arc::new(Mutex::new(Box::new(verifier) as Box<_>))); + let verifier = VerifierAdapter::new(Arc::new(Mutex::new(Box::new(verifier) as Box<_>))); let import_queue = Box::new(BasicQueue::new( verifier.clone(), @@ -610,6 +621,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, @@ -672,7 +684,7 @@ pub trait TestNetFactory: Sized { &config, &data, ); - let verifier = VerifierAdapter(Arc::new(Mutex::new(Box::new(verifier) as Box<_>))); + let verifier = VerifierAdapter::new(Arc::new(Mutex::new(Box::new(verifier) as Box<_>))); let import_queue = Box::new(BasicQueue::new( verifier.clone(), @@ -685,6 +697,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, @@ -753,7 +766,7 @@ pub trait TestNetFactory: Sized { /// Blocks the current thread until we are sync'ed. /// - /// Calls `poll_until_sync` repeatidely with the runtime passed as parameter. + /// Calls `poll_until_sync` repeatedly with the runtime passed as parameter. fn block_until_sync(&mut self, runtime: &mut tokio::runtime::current_thread::Runtime) { runtime.block_on(futures::future::poll_fn::<(), (), _>(|| Ok(self.poll_until_sync()))).unwrap(); } 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..29e6dca2a7f95bbad8422186586ac4305b9453cd 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -7,19 +7,18 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -bytes = "0.4.12" +bytes = "0.5" sc-client-api = { version = "2.0.0", path = "../api" } 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" } @@ -27,14 +26,14 @@ sc-network = { version = "0.8", path = "../network" } sc-keystore = { version = "2.0.0", path = "../keystore" } [target.'cfg(not(target_os = "unknown"))'.dependencies] -hyper = "0.12.35" -hyper-rustls = "0.17.1" +hyper = "0.13.2" +hyper-rustls = "0.19" [dev-dependencies] sc-client-db = { version = "0.8", default-features = true, path = "../db/" } env_logger = "0.7.0" substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } -tokio = "0.1.22" +tokio = "0.2" sc-transaction-pool = { version = "2.0.0", path = "../../client/transaction-pool" } sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } diff --git a/client/offchain/src/api/http.rs b/client/offchain/src/api/http.rs index 84c3ecd69b37ff7956bbe16e1b23f8383ad356b5..0483f84e2f114f4af17e38533964483ecda5e056 100644 --- a/client/offchain/src/api/http.rs +++ b/client/offchain/src/api/http.rs @@ -18,7 +18,7 @@ //! function returns a pair of [`HttpApi`] and [`HttpWorker`] that share some state. //! //! The [`HttpApi`] is (indirectly) passed to the runtime when calling an offchain worker, while -//! the [`HttpWorker`] must be processed in the background. The [`HttpApi`] mimicks the API of the +//! the [`HttpWorker`] must be processed in the background. The [`HttpApi`] mimics the API of the //! HTTP-related methods available to offchain workers. //! //! The reason for this design is driven by the fact that HTTP requests should continue running @@ -26,9 +26,9 @@ //! actively calling any function. use crate::api::timestamp; -use bytes::Buf as _; +use bytes::buf::ext::{Reader, BufExt}; use fnv::FnvHashMap; -use futures::{prelude::*, channel::mpsc, compat::Compat01As03}; +use futures::{prelude::*, future, channel::mpsc}; use log::error; use sp_core::offchain::{HttpRequestId, Timestamp, HttpRequestStatus, HttpError}; use std::{fmt, io::Read as _, mem, pin::Pin, task::Context, task::Poll}; @@ -50,7 +50,7 @@ pub fn http() -> (HttpApi, HttpWorker) { let engine = HttpWorker { to_api, from_api, - http_client: hyper::Client::builder().build(hyper_rustls::HttpsConnector::new(1)), + http_client: hyper::Client::builder().build(hyper_rustls::HttpsConnector::new()), requests: Vec::new(), }; @@ -103,14 +103,14 @@ struct HttpApiRequestRp { /// Elements extracted from the channel are first put into `current_read_chunk`. /// If the channel produces an error, then that is translated into an `IoError` and the request /// is removed from the list. - body: stream::Fuse>>, + body: stream::Fuse>>, /// Chunk that has been extracted from the channel and that is currently being read. /// Reading data from the response should read from this field in priority. - current_read_chunk: Option>, + current_read_chunk: Option>, } impl HttpApi { - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn request_start( &mut self, method: &str, @@ -122,7 +122,7 @@ impl HttpApi { let (body_sender, body) = hyper::Body::channel(); let mut request = hyper::Request::new(body); *request.method_mut() = hyper::Method::from_bytes(method.as_bytes()).map_err(|_| ())?; - *request.uri_mut() = hyper::Uri::from_shared(From::from(uri)).map_err(|_| ())?; + *request.uri_mut() = hyper::Uri::from_maybe_shared(uri.to_owned()).map_err(|_| ())?; let new_id = self.next_id; debug_assert!(!self.requests.contains_key(&new_id)); @@ -138,7 +138,7 @@ impl HttpApi { Ok(new_id) } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn request_add_header( &mut self, request_id: HttpRequestId, @@ -158,7 +158,7 @@ impl HttpApi { Ok(()) } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn request_write_body( &mut self, request_id: HttpRequestId, @@ -177,9 +177,7 @@ impl HttpApi { // (if the body has been written), or `DeadlineReached`, or `IoError`. // If `IoError` is returned, don't forget to remove the request from the list. let mut poll_sender = move |sender: &mut hyper::body::Sender| -> Result<(), HttpError> { - let mut when_ready = future::maybe_done(Compat01As03::new( - futures01::future::poll_fn(|| sender.poll_ready()) - )); + let mut when_ready = future::maybe_done(future::poll_fn(|cx| sender.poll_ready(cx))); futures::executor::block_on(future::select(&mut when_ready, &mut deadline)); match when_ready { future::MaybeDone::Done(Ok(())) => {} @@ -191,13 +189,11 @@ impl HttpApi { } }; - match sender.send_data(hyper::Chunk::from(chunk.to_owned())) { - Ok(()) => Ok(()), - Err(_chunk) => { + futures::executor::block_on(sender.send_data(hyper::body::Bytes::from(chunk.to_owned()))) + .map_err(|_| { error!("HTTP sender refused data despite being ready"); - Err(HttpError::IoError) - }, - } + HttpError::IoError + }) }; loop { @@ -266,7 +262,7 @@ impl HttpApi { } } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn response_wait( &mut self, ids: &[HttpRequestId], @@ -392,7 +388,7 @@ impl HttpApi { } } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn response_headers( &mut self, request_id: HttpRequestId @@ -411,7 +407,7 @@ impl HttpApi { .collect() } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn response_read_body( &mut self, request_id: HttpRequestId, @@ -538,7 +534,7 @@ enum WorkerToApi { /// the next item. /// Can also be used to send an error, in case an error happend on the HTTP socket. After /// an error is sent, the channel will close. - body: mpsc::Receiver>, + body: mpsc::Receiver>, }, /// A request has failed because of an error. The request is then no longer valid. Fail { @@ -564,13 +560,13 @@ pub struct HttpWorker { /// HTTP request being processed by the worker. enum HttpWorkerRequest { /// Request has been dispatched and is waiting for a response from the Internet. - Dispatched(Compat01As03), + Dispatched(hyper::client::ResponseFuture), /// Progressively reading the body of the response and sending it to the channel. ReadBody { /// Body to read `Chunk`s from. Only used if the channel is ready to accept data. - body: Compat01As03, + body: hyper::Body, /// Channel to the [`HttpApi`] where we send the chunks to. - tx: mpsc::Sender>, + tx: mpsc::Sender>, }, } @@ -608,7 +604,7 @@ impl Future for HttpWorker { // We received a response! Decompose it into its parts. let status_code = response.status(); let headers = mem::replace(response.headers_mut(), hyper::HeaderMap::new()); - let body = Compat01As03::new(response.into_body()); + let body = response.into_body(); let (body_tx, body_rx) = mpsc::channel(3); let _ = me.to_api.unbounded_send(WorkerToApi::Response { @@ -660,7 +656,7 @@ impl Future for HttpWorker { Poll::Pending => {}, Poll::Ready(None) => return Poll::Ready(()), // stops the worker Poll::Ready(Some(ApiToWorker::Dispatch { id, request })) => { - let future = Compat01As03::new(me.http_client.request(request)); + let future = me.http_client.request(request); debug_assert!(me.requests.iter().all(|(i, _)| *i != id)); me.requests.push((id, HttpWorkerRequest::Dispatched(future))); cx.waker().wake_by_ref(); // reschedule the task to poll the request @@ -692,31 +688,36 @@ impl fmt::Debug for HttpWorkerRequest { #[cfg(test)] mod tests { + use core::convert::Infallible; use crate::api::timestamp; use super::http; - use futures::prelude::*; - use futures01::Future as _; use sp_core::offchain::{HttpError, HttpRequestId, HttpRequestStatus, Duration}; // Returns an `HttpApi` whose worker is ran in the background, and a `SocketAddr` to an HTTP // server that runs in the background as well. macro_rules! build_api_server { () => {{ + fn tokio_run(future: impl std::future::Future) { + let _ = tokio::runtime::Runtime::new().unwrap().block_on(future); + } + let (api, worker) = http(); - // Note: we have to use tokio because hyper still uses old futures. - std::thread::spawn(move || { - tokio::run(futures::compat::Compat::new(worker.map(|()| Ok::<(), ()>(())))) - }); + std::thread::spawn(move || tokio_run(worker)); + let (addr_tx, addr_rx) = std::sync::mpsc::channel(); std::thread::spawn(move || { - let server = hyper::Server::bind(&"127.0.0.1:0".parse().unwrap()) - .serve(|| { - hyper::service::service_fn_ok(move |_: hyper::Request| { - hyper::Response::new(hyper::Body::from("Hello World!")) - }) - }); - let _ = addr_tx.send(server.local_addr()); - hyper::rt::run(server.map_err(|e| panic!("{:?}", e))); + tokio_run(async move { + let server = hyper::Server::bind(&"127.0.0.1:0".parse().unwrap()) + .serve(hyper::service::make_service_fn(|_| { async move { + Ok::<_, Infallible>(hyper::service::service_fn(move |_req| async move { + Ok::<_, Infallible>( + hyper::Response::new(hyper::Body::from("Hello World!")) + ) + })) + }})); + let _ = addr_tx.send(server.local_addr()); + server.await + }); }); (api, addr_rx.recv().unwrap()) }}; @@ -947,7 +948,7 @@ mod tests { #[test] fn fuzzing() { - // Uses the API in random ways to try to trigger panicks. + // Uses the API in random ways to try to trigger panics. // Doesn't test some paths, such as waiting for multiple requests. Also doesn't test what // happens if the server force-closes our socket. diff --git a/client/offchain/src/api/http_dummy.rs b/client/offchain/src/api/http_dummy.rs index 8725c8989979582d455b65d3e93d07bcf1eac5e0..5ff77a1068312bbc5da2f71d9c15a9dee7bd8380 100644 --- a/client/offchain/src/api/http_dummy.rs +++ b/client/offchain/src/api/http_dummy.rs @@ -33,7 +33,7 @@ pub struct HttpApi; pub struct HttpWorker; impl HttpApi { - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn request_start( &mut self, _: &str, @@ -43,7 +43,7 @@ impl HttpApi { Err(()) } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn request_add_header( &mut self, _: HttpRequestId, @@ -54,7 +54,7 @@ impl HttpApi { never be called; qed") } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn request_write_body( &mut self, _: HttpRequestId, @@ -65,7 +65,7 @@ impl HttpApi { never be called; qed") } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn response_wait( &mut self, requests: &[HttpRequestId], @@ -79,7 +79,7 @@ impl HttpApi { } } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn response_headers( &mut self, _: HttpRequestId @@ -88,7 +88,7 @@ impl HttpApi { never be called; qed") } - /// Mimicks the corresponding method in the offchain API. + /// Mimics the corresponding method in the offchain API. pub fn response_read_body( &mut self, _: HttpRequestId, diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 78760fb011c1c8c8935770795b80faf4619a4f56..7fc9671fcbcd826c85b69c9414fbaa34e56797da 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())), + ).0)); 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); @@ -219,6 +217,6 @@ mod tests { // then assert_eq!(pool.0.status().ready, 1); - assert_eq!(pool.0.ready().next().unwrap().is_propagateable(), false); + assert_eq!(pool.0.ready().next().unwrap().is_propagable(), false); } } diff --git a/client/peerset/Cargo.toml b/client/peerset/Cargo.toml index 90f44418c72e4c9377f7c154ce3244a99cafa2bb..c17ffdc385ba93a1475181272a8669c99c74edf2 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.16.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..fb91f1fcf69e09d4c7fec2949e2ee586184b588d 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"; @@ -198,14 +199,16 @@ impl Peerset { tx: tx.clone(), }; + let now = Instant::now(); + let mut peerset = Peerset { data: peersstate::PeersState::new(config.in_peers, config.out_peers, config.reserved_only), tx, rx, reserved_only: config.reserved_only, message_queue: VecDeque::new(), - created: Instant::now(), - latest_time_update: Instant::now(), + created: now, + latest_time_update: now, }; peerset.data.set_priority_group(RESERVED_NODES, config.reserved_nodes.into_iter().collect()); diff --git a/client/peerset/src/peersstate.rs b/client/peerset/src/peersstate.rs index a27d6e616b23e1eb70026572c3c2fd1045cec30d..96a6698734b744436cc846fcc772e2edb53b221a 100644 --- a/client/peerset/src/peersstate.rs +++ b/client/peerset/src/peersstate.rs @@ -156,7 +156,7 @@ impl PeersState { pub fn priority_not_connected_peer(&mut self) -> Option { let id = self.priority_nodes.values() .flatten() - .find(|id| self.nodes.get(id).map_or(false, |node| !node.connection_state.is_connected())) + .find(|&id| self.nodes.get(id).map_or(false, |node| !node.connection_state.is_connected())) .cloned(); id.map(move |id| NotConnectedPeer { state: self, @@ -170,7 +170,7 @@ impl PeersState { pub fn priority_not_connected_peer_from_group(&mut self, group_id: &str) -> Option { let id = self.priority_nodes.get(group_id) .and_then(|group| group.iter() - .find(|id| self.nodes.get(id).map_or(false, |node| !node.connection_state.is_connected())) + .find(|&id| self.nodes.get(id).map_or(false, |node| !node.connection_state.is_connected())) .cloned()); id.map(move |id| NotConnectedPeer { state: self, @@ -300,7 +300,7 @@ impl PeersState { for id in &peers { // update slots for nodes that become priority - if !all_other_groups.contains(&id) { + if !all_other_groups.contains(id) { let peer = self.nodes.entry(id.clone()).or_default(); match peer.connection_state { ConnectionState::In => self.num_in -= 1, @@ -322,7 +322,7 @@ impl PeersState { /// Remove a peer from a priority group. pub fn remove_from_priority_group(&mut self, group_id: &str, peer_id: &PeerId) { let mut peers = self.priority_nodes.get(group_id).cloned().unwrap_or_default(); - peers.remove(&peer_id); + peers.remove(peer_id); self.set_priority_group(group_id, peers); } diff --git a/client/peerset/tests/fuzz.rs b/client/peerset/tests/fuzz.rs index b591d83d8f2af9ec0caaa7a4fc1038a7e8f37f82..c2b0b44a3a95d43f30210efc73563e4e0ae520a3 100644 --- a/client/peerset/tests/fuzz.rs +++ b/client/peerset/tests/fuzz.rs @@ -108,7 +108,7 @@ fn test_once() { // If we generate 4, connect to a random node. 4 => if let Some(id) = known_nodes.iter() - .filter(|n| incoming_nodes.values().all(|m| m != *n) && !connected_nodes.contains(n)) + .filter(|n| incoming_nodes.values().all(|m| m != *n) && !connected_nodes.contains(*n)) .choose(&mut rng) { peerset.incoming(id.clone(), next_incoming_id.clone()); incoming_nodes.insert(next_incoming_id.clone(), id.clone()); @@ -120,7 +120,7 @@ fn test_once() { 6 => peerset_handle.set_reserved_only(false), // 7 and 8 are about switching a random node in or out of reserved mode. - 7 => if let Some(id) = known_nodes.iter().filter(|n| !reserved_nodes.contains(n)).choose(&mut rng) { + 7 => if let Some(id) = known_nodes.iter().filter(|n| !reserved_nodes.contains(*n)).choose(&mut rng) { peerset_handle.add_reserved_peer(id.clone()); reserved_nodes.insert(id.clone()); } diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index 7f1a12f80e80dbc94b67e682ac1291adfdc22062..b9f4cbb0159ce5e059499c38829860aa7cef5599 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,9 +14,10 @@ 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" } +sp-runtime = { path = "../../primitives/runtime" } serde = { version = "1.0.101", features = ["derive"] } serde_json = "1.0.41" sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } 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..49c4c996fa9e1705fa4344bcce708ecf66d5b45d 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>; @@ -62,7 +77,7 @@ pub trait AuthorApi { /// Submit an extrinsic to watch. /// /// See [`TransactionStatus`](sp_transaction_pool::TransactionStatus) for details on transaction - /// lifecycle. + /// life cycle. #[pubsub( subscription = "author_extrinsicUpdate", subscribe, diff --git a/client/rpc-api/src/errors.rs b/client/rpc-api/src/errors.rs index 9db41d0497068abfdfe67635352e2f8b06aac227..b75c34ead3806a7a0be3bb8104c4a30cd961138e 100644 --- a/client/rpc-api/src/errors.rs +++ b/client/rpc-api/src/errors.rs @@ -20,7 +20,7 @@ pub fn internal(e: E) -> jsonrpc_core::Error { warn!("Unknown error: {:?}", e); jsonrpc_core::Error { code: jsonrpc_core::ErrorCode::InternalError, - message: "Unknown error occured".into(), + message: "Unknown error occurred".into(), data: Some(format!("{:?}", e).into()), } } 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-api/src/subscriptions.rs b/client/rpc-api/src/subscriptions.rs index 808c9d5ba449019052f467464718862f346a1d98..54881bad5123bfb9c98c0b716f7b1b514bab2d40 100644 --- a/client/rpc-api/src/subscriptions.rs +++ b/client/rpc-api/src/subscriptions.rs @@ -71,7 +71,7 @@ impl Subscriptions { /// Borrows the internal task executor. /// - /// This can be used to spawn additional tasks on the underyling event loop. + /// This can be used to spawn additional tasks on the underlying event loop. pub fn executor(&self) -> &TaskExecutor { &self.executor } 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..41bfc46d388bd5e64b3efc63f5645394916a1db1 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())), + ).0); 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,13 @@ 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" @@ -50,10 +52,11 @@ sc-rpc-server = { version = "2.0.0", path = "../rpc-servers" } sc-rpc = { version = "2.0.0", path = "../rpc" } sc-telemetry = { version = "2.0.0", path = "../telemetry" } sc-offchain = { version = "2.0.0", path = "../offchain" } -parity-multiaddr = { package = "parity-multiaddr", version = "0.5.0" } +parity-multiaddr = { package = "parity-multiaddr", version = "0.7.1" } grafana-data-source = { version = "0.8", path = "../../utils/grafana-data-source" } 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 044798701c6e192fa69e7dddf9a9e6441226db6b..c67551afa35564b7f91d39ac2317f1361f2496d4 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,15 +44,19 @@ 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, ChainEvent}; use sp_blockchain; use grafana_data_source::{self, record_metrics}; +pub type BackgroundTask = Pin + Send>>; + /// Aggregator for the components required to build a service. /// /// # Usage @@ -71,10 +75,10 @@ use grafana_data_source::{self, record_metrics}; /// 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>, @@ -88,6 +92,7 @@ pub struct ServiceBuilder>>, marker: PhantomData<(TBl, TRtApi)>, + background_tasks: Vec<(&'static str, BackgroundTask)>, } /// Full client type. @@ -143,8 +148,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, @@ -154,8 +159,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, @@ -176,13 +181,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() @@ -194,7 +200,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(), @@ -213,7 +219,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, @@ -223,15 +229,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, @@ -263,17 +268,17 @@ where TGen: RuntimeGenesis, TCSExt: Extension { transaction_pool: Arc::new(()), rpc_extensions: Default::default(), remote_backend: None, + background_tasks: Default::default(), marker: PhantomData, }) } /// Start the service builder with a configuration. pub fn new_light( - config: Configuration + config: Configuration ) -> Result, @@ -307,7 +312,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(), @@ -331,7 +336,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, )?); @@ -349,13 +354,14 @@ where TGen: RuntimeGenesis, TCSExt: Extension { transaction_pool: Arc::new(()), rpc_extensions: Default::default(), remote_backend: Some(remote_blockchain), + background_tasks: Default::default(), marker: PhantomData, }) } } -impl - ServiceBuilder + ServiceBuilder { /// Returns a reference to the client that was stored in this builder. @@ -373,13 +379,37 @@ impl Arc> { + self.keystore.clone() + } + + /// Returns a reference to the transaction pool stored in this builder + pub fn pool(&self) -> Arc { + self.transaction_pool.clone() + } + + /// Returns a reference to the fetcher, only available if builder + /// was created with `new_light`. + pub fn fetcher(&self) -> Option + where TFchr: Clone + { + self.fetcher.clone() + } + + /// Returns a reference to the remote_backend, only available if builder + /// was created with `new_light`. + pub fn remote_backend(&self) -> Option>> { + self.remote_backend.clone() + } + /// Defines which head-of-chain strategy to use. pub fn with_opt_select_chain( 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)?; @@ -397,6 +427,7 @@ 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)) } @@ -413,9 +444,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( @@ -439,6 +470,7 @@ impl( self, - network_protocol_builder: impl FnOnce(&Configuration) -> Result - ) -> Result) -> Result + ) -> Result, Error> { let network_protocol = network_protocol_builder(&self.config)?; @@ -465,6 +497,7 @@ 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( @@ -571,6 +603,7 @@ 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| @@ -597,21 +630,25 @@ impl( - self, + mut self, transaction_pool_builder: impl FnOnce( sc_transaction_pool::txpool::Options, Arc, Option, - ) -> Result - ) -> Result Result<(UExPool, Option), Error> + ) -> Result, Error> where TSc: Clone, TFchr: Clone { - let transaction_pool = transaction_pool_builder( + let (transaction_pool, background_task) = transaction_pool_builder( self.config.transaction_pool.clone(), self.client.clone(), self.fetcher.clone(), )?; + if let Some(background_task) = background_task{ + self.background_tasks.push(("txpool-background", background_task)); + } + Ok(ServiceBuilder { config: self.config, client: self.client, @@ -626,6 +663,7 @@ impl( self, - rpc_ext_builder: impl FnOnce( - Arc, - Arc, - Arc, - Option, - Option>>, - ) -> Result, - ) -> Result Result, + ) -> Result, Error> where TSc: Clone, TFchr: Clone { - let rpc_extensions = rpc_ext_builder( - self.client.clone(), - self.transaction_pool.clone(), - self.backend.clone(), - self.fetcher.clone(), - self.remote_backend.clone(), - )?; + let rpc_extensions = rpc_ext_builder(&self)?; Ok(ServiceBuilder { config: self.config, @@ -665,6 +691,7 @@ impl Pin> + Send>>; } -impl +impl ServiceBuilder< TBl, TRtApi, - TCfg, TGen, TCSExt, Client, @@ -732,7 +760,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, @@ -740,9 +767,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, { @@ -781,6 +806,7 @@ ServiceBuilder< transaction_pool, rpc_extensions, remote_backend, + background_tasks, } = self; sp_session::generate_initial_session_keys( @@ -793,13 +819,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); @@ -818,11 +845,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 \ @@ -839,6 +866,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, @@ -857,7 +892,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))) }, @@ -868,6 +903,15 @@ ServiceBuilder< _ => None, }; + // Spawn background tasks which were stacked during the + // service building. + for (title, background_task) in background_tasks { + let _ = to_spawn_tx.unbounded_send(( + background_task, + title.into(), + )); + } + { // block notifications let txpool = Arc::downgrade(&transaction_pool); @@ -876,31 +920,59 @@ ServiceBuilder< let network_state_info: Arc = network.clone(); let is_validator = config.roles.is_authority(); - let events = client.import_notification_stream() - .for_each(move |notification| { - let txpool = txpool.upgrade(); + let (import_stream, finality_stream) = ( + client.import_notification_stream().map(|n| ChainEvent::NewBlock { + id: BlockId::Hash(n.hash), + header: n.header, + retracted: n.retracted, + is_new_best: n.is_new_best, + }), + client.finality_notification_stream().map(|n| ChainEvent::Finalized { + hash: n.hash + }) + ); + let events = futures::stream::select(import_stream, finality_stream) + .for_each(move |event| { + // offchain worker is only interested in block import events + if let ChainEvent::NewBlock { ref header, is_new_best, .. } = event { + let offchain = offchain.as_ref().and_then(|o| o.upgrade()); + match offchain { + Some(offchain) if is_new_best => { + let future = offchain.on_block_imported( + &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: {:?}", + header, + ), + _ => {}, + } + }; + let txpool = txpool.upgrade(); if let Some(txpool) = txpool.as_ref() { - let future = txpool.maintain( - &BlockId::hash(notification.hash), - ¬ification.retracted, - ); - let _ = to_spawn_tx_.unbounded_send(Box::pin(future)); - } - - 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)); + let future = txpool.maintain(event); + let _ = to_spawn_tx_.unbounded_send(( + Box::pin(future), + From::from("txpool-maintain") + )); } 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"), + )); } { @@ -908,9 +980,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"; @@ -920,7 +992,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"), + )); } // Periodically notify the telemetry. @@ -967,6 +1042,10 @@ ServiceBuilder< "disk_read_per_sec" => info.usage.as_ref().map(|usage| usage.io.bytes_read).unwrap_or(0), "disk_write_per_sec" => info.usage.as_ref().map(|usage| usage.io.bytes_written).unwrap_or(0), ); + #[cfg(not(target_os = "unknown"))] + let memory_transaction_pool = parity_util_mem::malloc_size(&*transaction_pool_); + #[cfg(target_os = "unknown")] + let memory_transaction_pool = 0; let _ = record_metrics!( "peers" => num_peers, "height" => best_number, @@ -980,11 +1059,16 @@ ServiceBuilder< "used_db_cache_size" => info.usage.as_ref().map(|usage| usage.memory.database_cache).unwrap_or(0), "disk_read_per_sec" => info.usage.as_ref().map(|usage| usage.io.bytes_read).unwrap_or(0), "disk_write_per_sec" => info.usage.as_ref().map(|usage| usage.io.bytes_written).unwrap_or(0), + "memory_transaction_pool" => memory_transaction_pool, ); 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)>(); @@ -997,18 +1081,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 { @@ -1048,28 +1135,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| { @@ -1078,7 +1181,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, @@ -1108,9 +1211,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 }); @@ -1121,8 +1224,8 @@ ServiceBuilder< exit.clone() ).map(drop); - let _ = to_spawn_tx.unbounded_send(Box::pin(future)); - } + let _ = to_spawn_tx.unbounded_send((Box::pin(future), From::from("grafana-server"))); + } // Instrumentation if let Some(tracing_targets) = config.tracing_targets.as_ref() { @@ -1147,10 +1250,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 8a145dec165b4ca0599569c581e5a3655b56e22e..2099c600df15e3282774e13e9a558f734bef7b6c 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. @@ -118,7 +137,7 @@ pub enum KeystoreConfig { password: Option> }, /// In-memory keystore. Recommended for in-browser nodes. - InMemory + InMemory, } impl KeystoreConfig { @@ -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 14b03d7e95de7eff3836af4e74e0968ffa9aeab5..059e1c19e490d6861339e278a274f5c060382950 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..a2e53616aba94ad39d7c096e4b67fe5ad4f90fc7 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. @@ -592,7 +625,7 @@ where E: IntoPoolError + From, { pool.ready() - .filter(|t| t.is_propagateable()) + .filter(|t| t.is_propagable()) .map(|t| { let hash = t.hash().clone(); let ex: B::Extrinsic = t.data().clone(); @@ -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)] @@ -670,6 +705,7 @@ mod tests { use futures::executor::block_on; use sp_consensus::SelectChain; use sp_runtime::traits::BlindCheckable; + use sp_runtime::generic::CheckSignature; use substrate_test_runtime_client::{prelude::*, runtime::{Extrinsic, Transfer}}; use sc_transaction_pool::{BasicPool, FullChainApi}; @@ -678,7 +714,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())), + ).0); let best = longest_chain.best_chain().unwrap(); let transaction = Transfer { amount: 5, @@ -695,7 +734,7 @@ mod tests { // then assert_eq!(transactions.len(), 1); - assert!(transactions[0].1.clone().check().is_ok()); + assert!(transactions[0].1.clone().check(CheckSignature::Yes).is_ok()); // this should not panic let _ = transactions[0].1.transfer(); } 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 961c8d98ffd09aa6e7bee6d355b612f50ecb78cf..b65bccc151823af8bc6db72702de57d97467a032 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,16 +489,16 @@ 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, { const NUM_FULL_NODES: usize = 10; const NUM_LIGHT_NODES: usize = 10; const NUM_BLOCKS: usize = 10; // 10 * 2 sec block production time = ~20 seconds - let temp = tempdir_with_prefix("substrate-conensus-test"); + let temp = tempdir_with_prefix("substrate-consensus-test"); let mut network = TestNet::new( &temp, spec.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..e9a8f1228c5e19e15ba21d6702301342b623b2cf 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 { @@ -1113,7 +1192,7 @@ impl Client where // NOTE: we're setting the finalized block as best block, this might // be slightly inaccurate since we might have a "better" block // further along this chain, but since best chain selection logic is - // pluggable we cannot make a better choice here. usages that need + // plugable we cannot make a better choice here. usages that need // an accurate "best" block need to go through `SelectChain` // instead. operation.op.mark_head(BlockId::Hash(block))?; @@ -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; @@ -2965,7 +3041,7 @@ pub(crate) mod tests { .unwrap().build().unwrap().block; // we will finalize A2 which should make it impossible to import a new - // B3 at the same height but that doesnt't include it + // B3 at the same height but that doesn't include it ClientExt::finalize_block(&client, BlockId::Hash(a2.hash()), None).unwrap(); let import_err = client.import(BlockOrigin::Own, b3).err().unwrap(); @@ -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/state-db/src/lib.rs b/client/state-db/src/lib.rs index f2722ae308068ea2f03720f3d16a1b3bdc9a6df3..0eab640de84e898e41c229890effbe6975d214e0 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -102,7 +102,7 @@ impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Db(e) => e.fmt(f), - Error::Decoding(e) => write!(f, "Error decoding slicable value: {}", e.what()), + Error::Decoding(e) => write!(f, "Error decoding sliceable value: {}", e.what()), Error::InvalidBlock => write!(f, "Trying to canonicalize invalid block"), Error::InvalidBlockNumber => write!(f, "Trying to insert block with invalid number"), Error::InvalidParent => write!(f, "Trying to insert block with unknown parent"), diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index 373c1aa0da076690ff29299f1286efb887851b36..db2f58fa8981d2231a750f8c68472c92f278ff87 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -106,7 +106,7 @@ fn discard_descendants( // save to be discarded later. pinned_insertions.insert(overlay.hash.clone(), overlay.inserted); } else { - // discard immediatelly. + // discard immediately. parents.remove(&overlay.hash); discard_values(&mut values, overlay.inserted); } diff --git a/client/state-db/src/pruning.rs b/client/state-db/src/pruning.rs index a993df4f111ac756828c2624c576af1c8b4a1b5f..71d018087b5903cfdb8ccc24999612fcfb12c02f 100644 --- a/client/state-db/src/pruning.rs +++ b/client/state-db/src/pruning.rs @@ -36,7 +36,7 @@ pub struct RefWindow { death_rows: VecDeque>, /// An index that maps each key from `death_rows` to block number. death_index: HashMap, - /// Block number that corresponts to the front of `death_rows` + /// Block number that corresponds to the front of `death_rows`. pending_number: u64, /// Number of call of `note_canonical` after /// last call `apply_pending` or `revert_pending` @@ -348,7 +348,7 @@ mod tests { } #[test] - fn reinserted_survivew_pending() { + fn reinserted_survive_pending() { let mut db = make_db(&[1, 2, 3]); let mut pruning: RefWindow = RefWindow::new(&db).unwrap(); let mut commit = make_commit(&[], &[2]); diff --git a/client/telemetry/Cargo.toml b/client/telemetry/Cargo.toml index c536b7599b59b5325844f16b10dc2122bb415c5c..10f4b188af6b994f5f7297022ae84c53db5d9a30 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.16.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..f8ca6d5c73d41bdd86dff66a2ebbff8dc7d33dbc 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; @@ -125,7 +126,7 @@ pub struct Telemetry { /// Behind the `Mutex` in `Telemetry`. /// -/// Note that ideally we wouldn't have to make the `Telemetry` clonable, as that would remove the +/// Note that ideally we wouldn't have to make the `Telemetry` cloneable, as that would remove the /// need for a `Mutex`. However there is currently a weird hack in place in `sc-service` /// where we extract the telemetry registration so that it continues running during the shutdown /// process. @@ -194,7 +195,7 @@ impl Stream for Telemetry { fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let before = Instant::now(); - // Because the `Telemetry` is clonable, we need to put the actual fields behind a `Mutex`. + // Because the `Telemetry` is cloneable, we need to put the actual fields behind a `Mutex`. // However, the user is only ever supposed to poll from one instance of `Telemetry`, while // the other instances are used only for RAII purposes. // We assume that the user is following this advice and therefore that the `Mutex` is only diff --git a/client/tracing/Cargo.toml b/client/tracing/Cargo.toml index 2268c5719f22a4f5eee4c31221963ecdb0c1b094..b476db6a0113d73167196b22f839ec2d0844e282 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..7ccc98a9c09e92f63815e76f3bddc61b8d2c35a9 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,10 @@ 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" } +futures-timer = "2.0" +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..daec970a69493a5e9f49272c5a3bd3cae0bd9954 100644 --- a/client/transaction-pool/graph/Cargo.toml +++ b/client/transaction-pool/graph/Cargo.toml @@ -3,16 +3,21 @@ 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-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } 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"] } +linked-hash-map = "0.5.2" [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..54bbe930b393bd37a94cb648272f1a66199cb691 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 { @@ -130,8 +135,8 @@ fn bench_configured(pool: Pool, number: u64) { let res = block_on(futures::future::join_all(futures.into_iter())); assert!(res.iter().all(Result::is_ok)); - assert_eq!(pool.status().future, 0); - assert_eq!(pool.status().ready, number as usize); + assert_eq!(pool.validated_pool().status().future, 0); + assert_eq!(pool.validated_pool().status().ready, number as usize); // Prune all transactions. let block_num = 6; @@ -142,21 +147,21 @@ fn bench_configured(pool: Pool, number: u64) { )).expect("Prune failed"); // pool is empty - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 0); } 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..33b7a51f9415b5bc04a288e3a773e817178b4130 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, @@ -138,7 +138,7 @@ impl InPoolTransaction for Transaction { &self.provides } - fn is_propagateable(&self) -> bool { + fn is_propagable(&self) -> bool { self.propagate } } @@ -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 @@ -1025,7 +1058,7 @@ requires: [03,02], provides: [04], data: [4]}".to_owned() requires: vec![vec![3], vec![2]], provides: vec![vec![4]], propagate: true, - }.is_propagateable(), true); + }.is_propagable(), true); assert_eq!(Transaction { data: vec![4u8], @@ -1036,7 +1069,7 @@ requires: [03,02], provides: [04], data: [4]}".to_owned() requires: vec![vec![3], vec![2]], provides: vec![vec![4]], propagate: false, - }.is_propagateable(), false); + }.is_propagable(), false); } #[test] @@ -1093,7 +1126,7 @@ requires: [03,02], provides: [04], data: [4]}".to_owned() } #[test] - fn should_accept_future_transactions_when_explcitly_asked_to() { + fn should_accept_future_transactions_when_explicitly_asked_to() { // given let mut pool = pool(); pool.reject_future_transactions = true; 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/lib.rs b/client/transaction-pool/graph/src/lib.rs index 23970ba9b8f8c7014ab6d5b1e1c4811401ebdbd7..ed10ef38d2bfd868eda7504b74bbe3018dee0c6f 100644 --- a/client/transaction-pool/graph/src/lib.rs +++ b/client/transaction-pool/graph/src/lib.rs @@ -39,4 +39,5 @@ pub use self::pool::{ Pool, Options, ChainApi, EventStream, ExtrinsicFor, BlockHash, ExHash, NumberFor, TransactionFor, + ValidatedTransaction, }; diff --git a/client/transaction-pool/graph/src/listener.rs b/client/transaction-pool/graph/src/listener.rs index dab2a6f5aae3f5021b8cb4d71357db9257f32b64..92ba71007e209f6ef06d249864b25cdda191776b 100644 --- a/client/transaction-pool/graph/src/listener.rs +++ b/client/transaction-pool/graph/src/listener.rs @@ -16,30 +16,34 @@ // along with Substrate. If not, see . use std::{ - collections::HashMap, - fmt, - hash, + collections::HashMap, hash, }; +use linked_hash_map::LinkedHashMap; use serde::Serialize; -use crate::watcher; -use sp_runtime::traits; +use crate::{watcher, ChainApi, BlockHash}; use log::{debug, trace, warn}; +use sp_runtime::traits; /// Extrinsic pool default listener. -pub struct Listener { - watchers: HashMap> +pub struct Listener { + watchers: HashMap>>, + finality_watchers: LinkedHashMap, Vec>, } -impl Default for Listener { +/// Maximum number of blocks awaiting finality at any time. +const MAX_FINALITY_WATCHERS: usize = 512; + +impl Default for Listener { fn default() -> Self { Listener { watchers: Default::default(), + finality_watchers: Default::default(), } } } -impl Listener { - fn fire(&mut self, hash: &H, fun: F) where F: FnOnce(&mut watcher::Sender) { +impl Listener { + fn fire(&mut self, hash: &H, fun: F) where F: FnOnce(&mut watcher::Sender>) { let clean = if let Some(h) = self.watchers.get_mut(hash) { fun(h); h.is_done() @@ -54,8 +58,8 @@ impl Listene /// Creates a new watcher for given verified extrinsic. /// - /// The watcher can be used to subscribe to lifecycle events of that extrinsic. - pub fn create_watcher(&mut self, hash: H) -> watcher::Watcher { + /// The watcher can be used to subscribe to life-cycle events of that extrinsic. + pub fn create_watcher(&mut self, hash: H) -> watcher::Watcher> { let sender = self.watchers.entry(hash.clone()).or_insert_with(watcher::Sender::default); sender.new_watcher(hash) } @@ -101,8 +105,34 @@ impl Listene } /// Transaction was pruned from the pool. - pub fn pruned(&mut self, header_hash: H2, tx: &H) { - debug!(target: "txpool", "[{:?}] Pruned at {:?}", tx, header_hash); - self.fire(tx, |watcher| watcher.in_block(header_hash)) + pub fn pruned(&mut self, block_hash: BlockHash, tx: &H) { + debug!(target: "txpool", "[{:?}] Pruned at {:?}", tx, block_hash); + self.fire(tx, |s| s.in_block(block_hash)); + self.finality_watchers.entry(block_hash).or_insert(vec![]).push(tx.clone()); + + while self.finality_watchers.len() > MAX_FINALITY_WATCHERS { + if let Some((hash, txs)) = self.finality_watchers.pop_front() { + for tx in txs { + self.fire(&tx, |s| s.finality_timeout(hash.clone())); + } + } + } + } + + /// The block this transaction was included in has been retracted. + pub fn retracted(&mut self, block_hash: BlockHash) { + if let Some(hashes) = self.finality_watchers.remove(&block_hash) { + for hash in hashes { + self.fire(&hash, |s| s.retracted(block_hash)) + } + } + } + + /// Notify all watchers that transactions have been finalized + pub fn finalized(&mut self, block_hash: BlockHash, txs: Vec) { + self.finality_watchers.remove(&block_hash); + for h in txs { + self.fire(&h, |s| s.finalized(block_hash.clone())) + } } } diff --git a/client/transaction-pool/graph/src/pool.rs b/client/transaction-pool/graph/src/pool.rs index 629bd0a9a93a8c055a166bec8c2bb11ea9bf2ebe..12601bb8d21be1e3349cf4eb2e6879215ac983a9 100644 --- a/client/transaction-pool/graph/src/pool.rs +++ b/client/transaction-pool/graph/src/pool.rs @@ -27,19 +27,20 @@ use serde::Serialize; use futures::{ Future, FutureExt, channel::mpsc, - future::{Either, ready, join_all}, }; use sp_runtime::{ generic::BlockId, traits::{self, SaturatedConversion}, transaction_validity::{TransactionValidity, TransactionTag as Tag, TransactionValidityError}, }; -use sp_transaction_pool::{error, PoolStatus}; +use sp_transaction_pool::error; +use wasm_timer::Instant; -use crate::validated_pool::{ValidatedPool, ValidatedTransaction}; +use crate::validated_pool::ValidatedPool; +pub use crate::validated_pool::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 +69,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 +87,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 +107,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 +124,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,76 +155,47 @@ 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( + /// Resubmit some transaction that were validated elsewhere. + pub fn resubmit( &self, - at: &BlockId, - max: Option, - ) -> impl Future> { - use std::time::Instant; - log::debug!(target: "txpool", - "Fetching ready transactions (up to: {})", - max.map(|x| format!("{}", x)).unwrap_or_else(|| "all".into()) - ); - let validated_pool = self.validated_pool.clone(); - let ready = self.validated_pool.ready() - .map(|tx| tx.data.clone()) - .take(max.unwrap_or_else(usize::max_value)); + revalidated_transactions: HashMap, ValidatedTransactionFor>, + ) { 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 - }) + self.validated_pool.resubmit(revalidated_transactions); + log::debug!(target: "txpool", + "Resubmitted. Took {} ms. Status: {:?}", + now.elapsed().as_millis(), + self.validated_pool.status() + ); } /// Prunes known ready transactions. @@ -233,12 +221,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 +240,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,71 +283,40 @@ 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); - - 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(), - )) - ))) - } - - /// 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 { - self.validated_pool.import_notification_stream() - } - /// Invoked when extrinsics are broadcasted. - pub fn on_broadcasted(&self, propagated: HashMap, Vec>) { - self.validated_pool.on_broadcasted(propagated) - } - - /// Remove invalid transactions from the pool. - pub fn remove_invalid(&self, hashes: &[ExHash]) -> Vec> { - self.validated_pool.remove_invalid(hashes) - } + let reverified_transactions = self.verify(at, pruned_transactions, false).await?; - /// Get an iterator for ready transactions ordered by priority - pub fn ready(&self) -> impl Iterator> { - self.validated_pool.ready() - } + log::trace!(target: "txpool", "Pruning at {:?}. Resubmitting transactions.", at); + // And finally - submit reverified transactions back to the pool - /// Returns pool status. - pub fn status(&self) -> PoolStatus { - self.validated_pool.status() + self.validated_pool.resubmit_pruned( + &at, + known_imported_hashes, + pruned_hashes, + reverified_transactions.into_iter().map(|(_, xt)| xt).collect(), + ) } /// Returns transaction hash @@ -383,69 +332,78 @@ 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_at( + block_number.saturated_into::(), + hash.clone(), + xt, + bytes, + validity, + ) + } + }, + 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 a reference to the underlying validated pool. + pub fn validated_pool(&self) -> &ValidatedPool { + &self.validated_pool } } @@ -459,10 +417,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 +426,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 +444,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 +517,10 @@ mod tests { len ) } + + fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { + futures::future::ready(Ok(None)) + } } fn uxt(transfer: Transfer) -> Extrinsic { @@ -567,7 +528,7 @@ mod tests { } fn pool() -> Pool { - Pool::new(Default::default(), TestApi::default()) + Pool::new(Default::default(), TestApi::default().into()) } #[test] @@ -584,7 +545,7 @@ mod tests { }))).unwrap(); // then - assert_eq!(pool.ready().map(|v| v.hash).collect::>(), vec![hash]); + assert_eq!(pool.validated_pool().ready().map(|v| v.hash).collect::>(), vec![hash]); } #[test] @@ -601,8 +562,8 @@ mod tests { // when pool.validated_pool.rotator().ban(&Instant::now(), vec![pool.hash_of(&uxt)]); let res = block_on(pool.submit_one(&BlockId::Number(0), uxt)); - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 0); // then assert_matches!(res.unwrap_err(), error::Error::TemporarilyBanned); @@ -613,7 +574,7 @@ mod tests { let stream = { // given let pool = pool(); - let stream = pool.import_notification_stream(); + let stream = pool.validated_pool().import_notification_stream(); // when let _hash = block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer { @@ -636,15 +597,15 @@ mod tests { nonce: 3, }))).unwrap(); - assert_eq!(pool.status().ready, 2); - assert_eq!(pool.status().future, 1); + assert_eq!(pool.validated_pool().status().ready, 2); + assert_eq!(pool.validated_pool().status().future, 1); stream }; // 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); } @@ -675,9 +636,9 @@ mod tests { pool.validated_pool.clear_stale(&BlockId::Number(5)).unwrap(); // then - assert_eq!(pool.ready().count(), 0); - assert_eq!(pool.status().future, 0); - assert_eq!(pool.status().ready, 0); + assert_eq!(pool.validated_pool().ready().count(), 0); + assert_eq!(pool.validated_pool().status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); // make sure they are temporarily banned as well assert!(pool.validated_pool.rotator().is_banned(&hash1)); assert!(pool.validated_pool.rotator().is_banned(&hash2)); @@ -713,7 +674,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)), @@ -721,7 +682,7 @@ mod tests { amount: 5, nonce: 1, }))).unwrap(); - assert_eq!(pool.status().future, 1); + assert_eq!(pool.validated_pool().status().future, 1); // when let hash2 = block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer { @@ -732,7 +693,7 @@ mod tests { }))).unwrap(); // then - assert_eq!(pool.status().future, 1); + assert_eq!(pool.validated_pool().status().future, 1); assert!(pool.validated_pool.rotator().is_banned(&hash1)); assert!(!pool.validated_pool.rotator().is_banned(&hash2)); } @@ -748,7 +709,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 { @@ -759,8 +720,8 @@ mod tests { }))).unwrap_err(); // then - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 0); } #[test] @@ -777,8 +738,8 @@ mod tests { }))).unwrap_err(); // then - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 0); assert_matches!(err, error::Error::NoTagsProvided); } @@ -795,19 +756,18 @@ mod tests { amount: 5, nonce: 0, }))).unwrap(); - assert_eq!(pool.status().ready, 1); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 1); + assert_eq!(pool.validated_pool().status().future, 0); // when block_on(pool.prune_tags(&BlockId::Number(2), vec![vec![0u8]], vec![])).unwrap(); - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 0); // then let mut stream = futures::executor::block_on_stream(watcher.into_stream()); assert_eq!(stream.next(), Some(TransactionStatus::Ready)); assert_eq!(stream.next(), Some(TransactionStatus::InBlock(H256::from_low_u64_be(2).into()))); - assert_eq!(stream.next(), None); } #[test] @@ -820,19 +780,18 @@ mod tests { amount: 5, nonce: 0, }))).unwrap(); - assert_eq!(pool.status().ready, 1); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 1); + assert_eq!(pool.validated_pool().status().future, 0); // when block_on(pool.prune_tags(&BlockId::Number(2), vec![vec![0u8]], vec![2u64])).unwrap(); - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 0); // then let mut stream = futures::executor::block_on_stream(watcher.into_stream()); assert_eq!(stream.next(), Some(TransactionStatus::Ready)); assert_eq!(stream.next(), Some(TransactionStatus::InBlock(H256::from_low_u64_be(2).into()))); - assert_eq!(stream.next(), None); } #[test] @@ -845,8 +804,8 @@ mod tests { amount: 5, nonce: 1, }))).unwrap(); - assert_eq!(pool.status().ready, 0); - assert_eq!(pool.status().future, 1); + assert_eq!(pool.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 1); // when block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer { @@ -855,7 +814,7 @@ mod tests { amount: 5, nonce: 0, }))).unwrap(); - assert_eq!(pool.status().ready, 2); + assert_eq!(pool.validated_pool().status().ready, 2); // then let mut stream = futures::executor::block_on_stream(watcher.into_stream()); @@ -874,7 +833,7 @@ mod tests { nonce: 0, }); let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), uxt)).unwrap(); - assert_eq!(pool.status().ready, 1); + assert_eq!(pool.validated_pool().status().ready, 1); // when pool.validated_pool.remove_invalid(&[*watcher.hash()]); @@ -898,13 +857,13 @@ mod tests { nonce: 0, }); let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), uxt)).unwrap(); - assert_eq!(pool.status().ready, 1); + assert_eq!(pool.validated_pool().status().ready, 1); // when let mut map = HashMap::new(); let peers = vec!["a".into(), "b".into(), "c".into()]; map.insert(*watcher.hash(), peers.clone()); - pool.on_broadcasted(map); + pool.validated_pool().on_broadcasted(map); // then @@ -924,7 +883,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)), @@ -933,7 +892,7 @@ mod tests { nonce: 0, }); let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), xt)).unwrap(); - assert_eq!(pool.status().ready, 1); + assert_eq!(pool.validated_pool().status().ready, 1); // when let xt = uxt(Transfer { @@ -943,7 +902,7 @@ mod tests { nonce: 1, }); block_on(pool.submit_one(&BlockId::Number(1), xt)).unwrap(); - assert_eq!(pool.status().ready, 1); + assert_eq!(pool.validated_pool().status().ready, 1); // then let mut stream = futures::executor::block_on_stream(watcher.into_stream()); @@ -958,7 +917,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 { @@ -986,11 +945,11 @@ mod tests { // The tag the above transaction provides (TestApi is using just nonce as u8) let provides = vec![0_u8]; block_on(pool.submit_one(&BlockId::Number(0), xt)).unwrap(); - assert_eq!(pool.status().ready, 1); + assert_eq!(pool.validated_pool().status().ready, 1); // Now block import happens before the second transaction is able to finish verification. block_on(pool.prune_tags(&BlockId::Number(1), vec![provides], vec![])).unwrap(); - assert_eq!(pool.status().ready, 0); + assert_eq!(pool.validated_pool().status().ready, 0); // so when we release the verification of the previous one it will have @@ -1000,85 +959,8 @@ mod tests { // then is_ready.recv().unwrap(); // wait for finish - assert_eq!(pool.status().ready, 1); - assert_eq!(pool.status().future, 0); + assert_eq!(pool.validated_pool().status().ready, 1); + assert_eq!(pool.validated_pool().status().future, 0); } } - - #[test] - fn should_revalidate_ready_transactions() { - fn transfer(nonce: u64) -> Extrinsic { - uxt(Transfer { - from: AccountId::from_h256(H256::from_low_u64_be(1)), - to: AccountId::from_h256(H256::from_low_u64_be(2)), - amount: 5, - nonce, - }) - } - - // given - let pool = pool(); - let tx0 = transfer(0); - let hash0 = pool.validated_pool.api().hash_and_length(&tx0).0; - let watcher0 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx0)).unwrap(); - let tx1 = transfer(1); - let hash1 = pool.validated_pool.api().hash_and_length(&tx1).0; - let watcher1 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx1)).unwrap(); - let tx2 = transfer(2); - let hash2 = pool.validated_pool.api().hash_and_length(&tx2).0; - let watcher2 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx2)).unwrap(); - let tx3 = transfer(3); - let hash3 = pool.validated_pool.api().hash_and_length(&tx3).0; - let watcher3 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx3)).unwrap(); - let tx4 = transfer(4); - let hash4 = pool.validated_pool.api().hash_and_length(&tx4).0; - let watcher4 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx4)).unwrap(); - assert_eq!(pool.status().ready, 5); - - // when - pool.validated_pool.api().invalidate.lock().insert(hash3); - pool.validated_pool.api().clear_requirements.lock().insert(hash1); - pool.validated_pool.api().add_requirements.lock().insert(hash0); - block_on(pool.revalidate_ready(&BlockId::Number(0), None)).unwrap(); - - // then - // hash0 now has unsatisfied requirements => it is moved to the future queue - // hash1 is now independent of hash0 => it is in ready queue - // hash2 still depends on hash1 => it is in ready queue - // hash3 is now invalid => it is removed from the pool - // hash4 now depends on invalidated hash3 => it is moved to the future queue - // - // events for hash3 are: Ready, Invalid - // events for hash4 are: Ready, Invalid - assert_eq!(pool.status().ready, 2); - assert_eq!( - futures::executor::block_on_stream(watcher3.into_stream()).collect::>(), - vec![TransactionStatus::Ready, TransactionStatus::Invalid], - ); - - // when - pool.validated_pool.remove_invalid(&[hash0, hash1, hash2, hash4]); - - // then - // events for hash0 are: Ready, Future, Invalid - // events for hash1 are: Ready, Invalid - // events for hash2 are: Ready, Invalid - assert_eq!( - futures::executor::block_on_stream(watcher0.into_stream()).collect::>(), - vec![TransactionStatus::Ready, TransactionStatus::Future, TransactionStatus::Invalid], - ); - assert_eq!( - futures::executor::block_on_stream(watcher1.into_stream()).collect::>(), - vec![TransactionStatus::Ready, TransactionStatus::Invalid], - ); - assert_eq!( - futures::executor::block_on_stream(watcher2.into_stream()).collect::>(), - vec![TransactionStatus::Ready, TransactionStatus::Invalid], - ); - assert_eq!( - futures::executor::block_on_stream(watcher4.into_stream()).collect::>(), - vec![TransactionStatus::Ready, TransactionStatus::Future, TransactionStatus::Invalid], - ); - } } - 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..a62822a91858b3d0bc0023832e0fe82a880116c3 100644 --- a/client/transaction-pool/graph/src/validated_pool.rs +++ b/client/transaction-pool/graph/src/validated_pool.rs @@ -16,13 +16,11 @@ use std::{ collections::{HashSet, HashMap}, - fmt, hash, sync::Arc, - time, }; -use crate::base_pool as base; +use crate::{base_pool as base, BlockHash}; use crate::listener::Listener; use crate::rotator::PoolRotator; use crate::watcher::Watcher; @@ -34,12 +32,13 @@ use parking_lot::{Mutex, RwLock}; use sp_runtime::{ generic::BlockId, traits::{self, SaturatedConversion}, - transaction_validity::TransactionTag as Tag, + transaction_validity::{TransactionTag as Tag, ValidTransaction}, }; 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}; +use crate::pool::{EventStream, Options, ChainApi, ExHash, ExtrinsicFor, TransactionFor}; /// Pre-validated transaction. Validated pool only accepts transactions wrapped in this enum. #[derive(Debug)] @@ -54,6 +53,30 @@ pub enum ValidatedTransaction { Unknown(Hash, Error), } +impl ValidatedTransaction { + /// Consume validity result, transaction data and produce ValidTransaction. + pub fn valid_at( + at: u64, + hash: Hash, + data: Ex, + bytes: usize, + validity: ValidTransaction, + ) -> Self { + Self::Valid(base::Transaction { + data, + bytes, + hash, + priority: validity.priority, + requires: validity.requires, + provides: validity.provides, + propagate: validity.propagate, + valid_till: at + .saturated_into::() + .saturating_add(validity.longevity), + }) + } +} + /// A type of validated transaction stored in the pool. pub type ValidatedTransactionFor = ValidatedTransaction< ExHash, @@ -62,26 +85,38 @@ pub type ValidatedTransactionFor = ValidatedTransaction< >; /// Pool that deals with validated transactions. -pub(crate) struct ValidatedPool { - api: B, +pub struct ValidatedPool { + api: Arc, options: Options, - listener: RwLock, BlockHash>>, + listener: RwLock, B>>, 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, options, listener: Default::default(), + api, pool: RwLock::new(base_pool), import_notification_sinks: Default::default(), rotator: Default::default(), @@ -89,7 +124,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,22 +160,23 @@ 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(); fire_events(&mut *listener, &imported); 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) => { self.listener.write().invalid(&hash, false); Err(err.into()) - } + }, } } @@ -150,19 +186,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 +233,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,14 +364,18 @@ 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() - .collect())) + .map(|transaction| transaction.provides.iter().cloned().collect())) .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, @@ -391,8 +439,14 @@ impl ValidatedPool { let header_hash = self.api.block_id_to_hash(at)? .ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into())?; let mut listener = self.listener.write(); + let mut set = HashSet::with_capacity(hashes.size_hint().0); for h in hashes { - listener.pruned(header_hash, &h); + // `hashes` has possibly duplicate hashes. + // we'd like to send out the `InBlock` notification only once. + if !set.contains(&h) { + listener.pruned(header_hash, &h); + set.insert(h); + } } Ok(()) } @@ -406,7 +460,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)) @@ -443,8 +497,11 @@ impl ValidatedPool { &self.api } - /// Return an event stream of transactions imported to the pool. - pub fn import_notification_stream(&self) -> EventStream { + /// 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> { let (sink, stream) = mpsc::unbounded(); self.import_notification_sinks.lock().push(sink); stream @@ -467,13 +524,13 @@ impl ValidatedPool { pub fn remove_invalid(&self, hashes: &[ExHash]) -> Vec> { // early exit in case there is no invalid transactions. if hashes.is_empty() { - return vec![] + return vec![]; } 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); @@ -496,14 +553,34 @@ impl ValidatedPool { pub fn status(&self) -> PoolStatus { self.pool.read().status() } + + /// Notify all watchers that transactions in the block with hash have been finalized + pub async fn on_block_finalized(&self, block_hash: BlockHash) -> Result<(), B::Error> { + debug!(target: "txpool", "Attempting to notify watchers of finalization for {}", block_hash); + // fetch all extrinsic hashes + if let Some(txs) = self.api.block_body(&BlockId::Hash(block_hash.clone())).await? { + let tx_hashes = txs.into_iter() + .map(|tx| self.api.hash_and_length(&tx).0) + .collect::>(); + // notify the watcher that these extrinsics have been finalized + self.listener.write().finalized(block_hash, tx_hashes); + } + + Ok(()) + } + + /// Notify the listener of retracted blocks + pub fn on_block_retracted(&self, block_hash: BlockHash) { + self.listener.write().retracted(block_hash) + } } -fn fire_events( - listener: &mut Listener, +fn fire_events( + listener: &mut Listener, imported: &base::Imported, ) where H: hash::Hash + Eq + traits::Member + Serialize, - H2: Clone + fmt::Debug, + B: ChainApi, { match *imported { base::Imported::Ready { ref promoted, ref failed, ref removed, ref hash } => { diff --git a/client/transaction-pool/graph/src/watcher.rs b/client/transaction-pool/graph/src/watcher.rs index f9c234f73c3b8aad3fa86618750e9cab06b70676..d28f6814e455213a443b583646ee2af6a3abb335 100644 --- a/client/transaction-pool/graph/src/watcher.rs +++ b/client/transaction-pool/graph/src/watcher.rs @@ -26,12 +26,12 @@ use sp_transaction_pool::TransactionStatus; /// /// Represents a stream of status updates for particular extrinsic. #[derive(Debug)] -pub struct Watcher { - receiver: mpsc::UnboundedReceiver>, +pub struct Watcher { + receiver: mpsc::UnboundedReceiver>, hash: H, } -impl Watcher { +impl Watcher { /// Returns the transaction hash. pub fn hash(&self) -> &H { &self.hash @@ -40,30 +40,30 @@ impl Watcher { /// Pipe the notifications to given sink. /// /// Make sure to drive the future to completion. - pub fn into_stream(self) -> impl Stream> { + pub fn into_stream(self) -> impl Stream> { self.receiver } } /// Sender part of the watcher. Exposed only for testing purposes. #[derive(Debug)] -pub struct Sender { - receivers: Vec>>, - finalized: bool, +pub struct Sender { + receivers: Vec>>, + is_finalized: bool, } -impl Default for Sender { +impl Default for Sender { fn default() -> Self { Sender { receivers: Default::default(), - finalized: false, + is_finalized: false, } } } -impl Sender { +impl Sender { /// Add a new watcher to this sender object. - pub fn new_watcher(&mut self, hash: H) -> Watcher { + pub fn new_watcher(&mut self, hash: H) -> Watcher { let (tx, receiver) = mpsc::unbounded(); self.receivers.push(tx); Watcher { @@ -85,26 +85,42 @@ impl Sender { /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. pub fn usurped(&mut self, hash: H) { self.send(TransactionStatus::Usurped(hash)); - self.finalized = true; + self.is_finalized = true; } /// Extrinsic has been included in block with given hash. - pub fn in_block(&mut self, hash: H2) { + pub fn in_block(&mut self, hash: BH) { self.send(TransactionStatus::InBlock(hash)); - self.finalized = true; + } + + /// Extrinsic has been finalized by a finality gadget. + pub fn finalized(&mut self, hash: BH) { + self.send(TransactionStatus::Finalized(hash)); + self.is_finalized = true; + } + + /// The block this extrinsic was included in has been retracted + pub fn finality_timeout(&mut self, hash: BH) { + self.send(TransactionStatus::FinalityTimeout(hash)); + self.is_finalized = true; + } + + /// The block this extrinsic was included in has been retracted + pub fn retracted(&mut self, hash: BH) { + self.send(TransactionStatus::Retracted(hash)); } /// Extrinsic has been marked as invalid by the block builder. pub fn invalid(&mut self) { self.send(TransactionStatus::Invalid); // we mark as finalized as there are no more notifications - self.finalized = true; + self.is_finalized = true; } /// Transaction has been dropped from the pool because of the limit. pub fn dropped(&mut self) { self.send(TransactionStatus::Dropped); - self.finalized = true; + self.is_finalized = true; } /// The extrinsic has been broadcast to the given peers. @@ -114,10 +130,10 @@ impl Sender { /// Returns true if the are no more listeners for this extrinsic or it was finalized. pub fn is_done(&self) -> bool { - self.finalized || self.receivers.is_empty() + self.is_finalized || self.receivers.is_empty() } - fn send(&mut self, status: TransactionStatus) { + fn send(&mut self, status: TransactionStatus) { self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok()) } } diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index 8495b8b65f17bbaeaadf60b3cbb34fa2ac4af685..84e06cc33e4f5c83e001dce41d68e882fd2657c7 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,8 @@ impl FullChainApi where impl sc_transaction_graph::ChainApi for FullChainApi where Block: BlockT, - Client: ProvideRuntimeApi + BlockIdTo + 'static + Send + Sync, + Client: ProvideRuntimeApi + BlockBody + BlockIdTo, + Client: Send + Sync + 'static, Client::Api: TaggedTransactionQueue, sp_api::ApiErrorFor: Send, { @@ -71,6 +73,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 +88,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 +205,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..a69323553fa8f53c33021e482ac2eedfd79a881c 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; +mod revalidation; + +#[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, + TransactionPool, PoolStatus, ImportNotificationStream, TxHash, TransactionFor, + TransactionStatusStreamFor, MaintainedTransactionPool, PoolFuture, ChainEvent, }; +use wasm_timer::Instant; /// Basic implementation of transaction pool that can be customized by providing PoolApi. pub struct BasicPool @@ -49,18 +50,86 @@ pub struct BasicPool PoolApi: sc_transaction_graph::ChainApi, { pool: Arc>, + api: Arc, + revalidation_strategy: Arc>>>, + revalidation_queue: 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 where Block: BlockT, - PoolApi: sc_transaction_graph::ChainApi, + PoolApi: sc_transaction_graph::ChainApi + 'static, { /// Create new basic transaction pool with provided api. - pub fn new(options: sc_transaction_graph::Options, pool_api: PoolApi) -> Self { - BasicPool { - pool: Arc::new(sc_transaction_graph::Pool::new(options, pool_api)), - } + /// + /// It will also optionally return background task that might be started by the + /// caller. + pub fn new( + options: sc_transaction_graph::Options, + pool_api: Arc, + ) -> (Self, Option + Send>>>) { + 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, Option + Send>>>) { + let pool = Arc::new(sc_transaction_graph::Pool::new(options, pool_api.clone())); + let (revalidation_queue, background_task) = match revalidation_type { + RevalidationType::Light => (revalidation::RevalidationQueue::new(pool_api.clone(), pool.clone()), None), + RevalidationType::Full => { + let (queue, background) = revalidation::RevalidationQueue::new_background(pool_api.clone(), pool.clone()); + (queue, Some(background)) + }, + }; + ( + BasicPool { + api: pool_api, + pool, + revalidation_queue: Arc::new(revalidation_queue), + revalidation_strategy: Arc::new(Mutex::new( + match revalidation_type { + RevalidationType::Light => RevalidationStrategy::Light(RevalidationStatus::NotScheduled), + RevalidationType::Full => RevalidationStrategy::Always, + } + )), + }, + background_task, + ) } /// Gets shared reference to the underlying pool. @@ -72,54 +141,66 @@ 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> { - self.pool.remove_invalid(hashes) + self.pool.validated_pool().remove_invalid(hashes) } fn status(&self) -> PoolStatus { - self.pool.status() + self.pool.validated_pool().status() } fn ready(&self) -> Box>> { - Box::new(self.pool.ready()) + Box::new(self.pool.validated_pool().ready()) } - fn import_notification_stream(&self) -> ImportNotificationStream { - self.pool.import_notification_stream() + fn import_notification_stream(&self) -> ImportNotificationStream> { + self.pool.validated_pool().import_notification_stream() } fn hash_of(&self, xt: &TransactionFor) -> TxHash { @@ -127,6 +208,191 @@ impl TransactionPool for BasicPool } fn on_broadcasted(&self, propagations: HashMap, Vec>) { - self.pool.on_broadcasted(propagations) + self.pool.validated_pool().on_broadcasted(propagations) + } + + fn ready_transaction(&self, hash: &TxHash) -> Option> { + self.pool.validated_pool().ready_by_hash(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, +} + +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, + }, + Self::Always => RevalidationAction { + revalidate: true, + resubmit: true, + } + } + } +} + +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, event: ChainEvent) -> Pin + Send>> { + match event { + ChainEvent::NewBlock { id, retracted, .. } => { + 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.clone(); + let revalidation_queue = self.revalidation_queue.clone(); + + async move { + // We don't query block if we won't prune anything + if !pool.validated_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 { + // notify txs awaiting finality that it has been retracted + pool.validated_pool().on_block_retracted(retracted_hash.clone()); + + 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 { + let hashes = pool.validated_pool().ready().map(|tx| tx.hash.clone()).collect(); + revalidation_queue.revalidate_later(block_number, hashes).await; + } + + revalidation_strategy.lock().clear(); + }.boxed() + } + ChainEvent::Finalized { hash } => { + let pool = self.pool.clone(); + async move { + if let Err(e) = pool.validated_pool().on_block_finalized(hash).await { + log::warn!( + target: "txpool", + "Error [{}] occurred while attempting to notify watchers of finalization {}", + e, hash + ) + } + }.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/client/transaction-pool/src/revalidation.rs b/client/transaction-pool/src/revalidation.rs new file mode 100644 index 0000000000000000000000000000000000000000..dbf8a2935427113916c78ceffcb8bde742da78db --- /dev/null +++ b/client/transaction-pool/src/revalidation.rs @@ -0,0 +1,313 @@ +// 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 . + +//! Pool periodic revalidation. + +use std::{sync::Arc, pin::Pin, collections::{HashMap, HashSet, BTreeMap}}; + +use sc_transaction_graph::{ChainApi, Pool, ExHash, NumberFor, ValidatedTransaction}; +use sp_runtime::traits::{Zero, SaturatedConversion}; +use sp_runtime::generic::BlockId; +use sp_runtime::transaction_validity::TransactionValidityError; + +use futures::{prelude::*, channel::mpsc, stream::unfold}; +use std::time::Duration; +use futures_timer::Delay; + +#[cfg(not(test))] +const BACKGROUND_REVALIDATION_INTERVAL: Duration = Duration::from_millis(200); +#[cfg(test)] +pub const BACKGROUND_REVALIDATION_INTERVAL: Duration = Duration::from_millis(5); + +const BACKGROUND_REVALIDATION_BATCH_SIZE: usize = 20; + +/// Payload from queue to worker. +struct WorkerPayload { + at: NumberFor, + transactions: Vec>, +} + +/// Async revalidation worker. +/// +/// Implements future and can be spawned in place or in background. +struct RevalidationWorker { + api: Arc, + pool: Arc>, + best_block: NumberFor, + block_ordered: BTreeMap, HashSet>>, + members: HashMap, NumberFor>, +} + +impl Unpin for RevalidationWorker {} + +fn interval(duration: Duration) -> impl Stream + Unpin { + unfold((), move |_| { + Delay::new(duration).map(|_| Some(((), ()))) + }).map(drop) +} + +/// Revalidate batch of transaction. +/// +/// Each transaction is validated against chain, and invalid are +/// removed from the `pool`, while valid are resubmitted. +async fn batch_revalidate( + pool: Arc>, + api: Arc, + at: NumberFor, + batch: impl IntoIterator>, +) { + let mut invalid_hashes = Vec::new(); + let mut revalidated = HashMap::new(); + + for ext_hash in batch { + let ext = match pool.validated_pool().ready_by_hash(&ext_hash) { + Some(ext) => ext, + None => continue, + }; + + match api.validate_transaction(&BlockId::Number(at), ext.data.clone()).await { + Ok(Err(TransactionValidityError::Invalid(err))) => { + log::debug!(target: "txpool", "[{:?}]: Revalidation: invalid {:?}", ext_hash, err); + invalid_hashes.push(ext_hash); + }, + Ok(Err(TransactionValidityError::Unknown(err))) => { + // skipping unknown, they might be pushed by valid or invalid transaction + // when latter resubmitted. + log::trace!(target: "txpool", "[{:?}]: Unknown during revalidation: {:?}", ext_hash, err); + }, + Ok(Ok(validity)) => { + revalidated.insert( + ext_hash.clone(), + ValidatedTransaction::valid_at( + at.saturated_into::(), + ext_hash, + ext.data.clone(), + api.hash_and_length(&ext.data).1, + validity, + ) + ); + }, + Err(validation_err) => { + log::debug!( + target: "txpool", + "[{:?}]: Error during revalidation: {:?}. Removing.", + ext_hash, + validation_err + ); + invalid_hashes.push(ext_hash); + } + } + } + + pool.validated_pool().remove_invalid(&invalid_hashes); + pool.resubmit(revalidated); +} + +impl RevalidationWorker { + fn new( + api: Arc, + pool: Arc>, + ) -> Self { + Self { + api, + pool, + block_ordered: Default::default(), + members: Default::default(), + best_block: Zero::zero(), + } + } + + fn prepare_batch(&mut self) -> Vec> { + let mut queued_exts = Vec::new(); + let mut left = BACKGROUND_REVALIDATION_BATCH_SIZE; + + // Take maximum of count transaction by order + // which they got into the pool + while left > 0 { + let first_block = match self.block_ordered.keys().next().cloned() { + Some(bn) => bn, + None => break, + }; + let mut block_drained = false; + if let Some(extrinsics) = self.block_ordered.get_mut(&first_block) { + let to_queue = extrinsics.iter().take(left).cloned().collect::>(); + if to_queue.len() == extrinsics.len() { + block_drained = true; + } else { + for xt in &to_queue { + extrinsics.remove(xt); + } + } + left -= to_queue.len(); + queued_exts.extend(to_queue); + } + + if block_drained { + self.block_ordered.remove(&first_block); + } + } + + queued_exts + } + + fn push(&mut self, worker_payload: WorkerPayload) { + // we don't add something that already scheduled for revalidation + let transactions = worker_payload.transactions; + let block_number = worker_payload.at; + + for ext_hash in transactions { + // we don't add something that already scheduled for revalidation + if self.members.contains_key(&ext_hash) { continue; } + + self.block_ordered.entry(block_number) + .and_modify(|value| { value.insert(ext_hash.clone()); }) + .or_insert_with(|| { + let mut bt = HashSet::new(); + bt.insert(ext_hash.clone()); + bt + }); + self.members.insert(ext_hash.clone(), block_number); + } + } + + /// Background worker main loop. + /// + /// It does two things: periodically tries to process some transactions + /// from the queue and also accepts messages to enqueue some more + /// transactions from the pool. + pub async fn run(mut self, from_queue: mpsc::UnboundedReceiver>) { + let interval = interval(BACKGROUND_REVALIDATION_INTERVAL).fuse(); + let from_queue = from_queue.fuse(); + futures::pin_mut!(interval, from_queue); + let this = &mut self; + + loop { + futures::select! { + _ = interval.next() => { + let next_batch = this.prepare_batch(); + batch_revalidate(this.pool.clone(), this.api.clone(), this.best_block, next_batch).await; + }, + workload = from_queue.next() => { + match workload { + Some(worker_payload) => { + this.best_block = worker_payload.at; + this.push(worker_payload); + continue; + }, + // R.I.P. worker! + None => break, + } + } + } + } + } +} + + +/// Revalidation queue. +/// +/// Can be configured background (`new_background`) +/// or immediate (just `new`). +pub struct RevalidationQueue { + pool: Arc>, + api: Arc, + background: Option>>, +} + +impl RevalidationQueue +where + Api: 'static, +{ + /// New revalidation queue without background worker. + pub fn new(api: Arc, pool: Arc>) -> Self { + Self { + api, + pool, + background: None, + } + } + + /// New revalidation queue with background worker. + pub fn new_background(api: Arc, pool: Arc>) -> + (Self, Pin + Send>>) + { + let (to_worker, from_queue) = mpsc::unbounded(); + + let worker = RevalidationWorker::new(api.clone(), pool.clone()); + + let queue = + Self { + api, + pool, + background: Some(to_worker), + }; + + (queue, worker.run(from_queue).boxed()) + } + + /// Queue some transaction for later revalidation. + /// + /// If queue configured with background worker, this will return immediately. + /// If queue configured without background worker, this will resolve after + /// revalidation is actually done. + pub async fn revalidate_later(&self, at: NumberFor, transactions: Vec>) { + if let Some(ref to_worker) = self.background { + if let Err(e) = to_worker.unbounded_send(WorkerPayload { at, transactions }) { + log::warn!(target: "txpool", "Failed to update background worker: {:?}", e); + } + return; + } else { + let pool = self.pool.clone(); + let api = self.api.clone(); + batch_revalidate(pool, api, at, transactions).await + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use sc_transaction_graph::Pool; + use substrate_test_runtime_transaction_pool::{TestApi, uxt}; + use futures::executor::block_on; + use substrate_test_runtime_client::{ + AccountKeyring::*, + }; + + fn setup() -> (Arc, Pool) { + let test_api = Arc::new(TestApi::empty()); + let pool = Pool::new(Default::default(), test_api.clone()); + (test_api, pool) + } + + #[test] + fn smoky() { + let (api, pool) = setup(); + let pool = Arc::new(pool); + let queue = Arc::new(RevalidationQueue::new(api.clone(), pool.clone())); + + let uxt = uxt(Alice, 0); + let uxt_hash = block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).expect("Should be valid"); + + block_on(queue.revalidate_later(0, vec![uxt_hash])); + + // revalidated in sync offload 2nd time + assert_eq!(api.validation_requests().len(), 2); + // number of ready + assert_eq!(pool.validated_pool().status().ready, 1); + } +} \ No newline at end of file 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/testing/pool.rs b/client/transaction-pool/src/testing/pool.rs new file mode 100644 index 0000000000000000000000000000000000000000..37b80df9e1be42d0d9f5c12c1055e4032a4d92af --- /dev/null +++ b/client/transaction-pool/src/testing/pool.rs @@ -0,0 +1,538 @@ +// 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::*; +use sp_transaction_pool::TransactionStatus; +use futures::executor::block_on; +use txpool::{self, Pool}; +use sp_runtime::{ + generic::BlockId, + transaction_validity::ValidTransaction, +}; +use substrate_test_runtime_client::{ + runtime::{Block, Hash, Index, Header, Extrinsic}, + AccountKeyring::*, +}; +use substrate_test_runtime_transaction_pool::{TestApi, uxt}; +use crate::revalidation::BACKGROUND_REVALIDATION_INTERVAL; + +fn pool() -> Pool { + Pool::new(Default::default(), TestApi::with_alice_nonce(209).into()) +} + +fn maintained_pool() -> (BasicPool, futures::executor::ThreadPool) { + let (pool, background_task) = BasicPool::new(Default::default(), std::sync::Arc::new(TestApi::with_alice_nonce(209))); + + let thread_pool = futures::executor::ThreadPool::new().unwrap(); + thread_pool.spawn_ok(background_task.expect("basic pool have background task")); + (pool, thread_pool) +} + +fn header(number: u64) -> Header { + Header { + number, + digest: Default::default(), + extrinsics_root: Default::default(), + parent_hash: Default::default(), + state_root: Default::default(), + } +} + +#[test] +fn submission_should_work() { + let pool = pool(); + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).unwrap(); + + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, vec![209]); +} + +#[test] +fn multiple_submission_should_work() { + let pool = pool(); + 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.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, vec![209, 210]); +} + +#[test] +fn early_nonce_should_be_culled() { + let pool = pool(); + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 208))).unwrap(); + + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, Vec::::new()); +} + +#[test] +fn late_nonce_should_be_queued() { + let pool = pool(); + + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 210))).unwrap(); + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, Vec::::new()); + + block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).unwrap(); + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, vec![209, 210]); +} + +#[test] +fn prune_tags_should_work() { + let pool = pool(); + 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.validated_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![hash209], + ) + ).expect("Prune tags"); + + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, vec![210]); +} + +#[test] +fn should_ban_invalid_transactions() { + let pool = pool(); + let uxt = uxt(Alice, 209); + let hash = block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap(); + pool.validated_pool().remove_invalid(&[hash]); + block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap_err(); + + // when + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + assert_eq!(pending, Vec::::new()); + + // then + block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap_err(); +} + +#[test] +fn should_correctly_prune_transactions_providing_more_than_one_tag() { + 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.clone()); + let xt = uxt(Alice, 209); + block_on(pool.submit_one(&BlockId::number(0), xt.clone())).expect("1. Imported"); + assert_eq!(pool.validated_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.validated_pool().status().ready, 0); + // it's re-imported to future + assert_eq!(pool.validated_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.validated_pool().status().ready, 1); + assert_eq!(pool.validated_pool().status().future, 1); + let pending: Vec<_> = pool.validated_pool().ready().map(|a| a.data.transfer().nonce).collect(); + 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.validated_pool().status().ready, 0); + assert_eq!(pool.validated_pool().status().future, 2); +} + +fn block_event(id: u64) -> ChainEvent { + ChainEvent::NewBlock { + id: BlockId::number(id), + is_new_best: true, + retracted: vec![], + header: header(id), + } +} + +fn block_event_with_retracted(id: u64, retracted: Vec) -> ChainEvent { + ChainEvent::NewBlock { + id: BlockId::number(id), + is_new_best: true, + retracted: retracted, + header: header(id), + } +} + + +#[test] +fn should_prune_old_during_maintenance() { + let xt = uxt(Alice, 209); + + let (pool, _guard) = 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(block_event(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, _guard) = 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(block_event(1))); + + // maintaince is in background + block_on(futures_timer::Delay::new(BACKGROUND_REVALIDATION_INTERVAL*2)); + + block_on(pool.maintain(block_event(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_maintenance() { + let xt = uxt(Alice, 209); + let retracted_hash = Hash::random(); + + let (pool, _guard) = 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()]); + + let event = block_event_with_retracted(1, vec![retracted_hash]); + + block_on(pool.maintain(event)); + 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, _guard) = 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); + + let event = block_event_with_retracted(1, vec![retracted_hash]); + + block_on(pool.maintain(event)); + + // maintenance is in background + block_on(futures_timer::Delay::new(BACKGROUND_REVALIDATION_INTERVAL*2)); + + let event = block_event_with_retracted(1, vec![retracted_hash]); + + block_on(pool.maintain(event)); + assert_eq!(pool.status().ready, 0); +} + +#[test] +fn should_push_watchers_during_maintaince() { + fn alice_uxt(nonce: u64) -> Extrinsic { + uxt(Alice, 209 + nonce) + } + + // given + let (pool, _guard) = maintained_pool(); + + let tx0 = alice_uxt(0); + let watcher0 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx0.clone())).unwrap(); + let tx1 = alice_uxt(1); + let watcher1 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx1.clone())).unwrap(); + let tx2 = alice_uxt(2); + let watcher2 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx2.clone())).unwrap(); + let tx3 = alice_uxt(3); + let watcher3 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx3.clone())).unwrap(); + let tx4 = alice_uxt(4); + let watcher4 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx4.clone())).unwrap(); + assert_eq!(pool.status().ready, 5); + + // when + pool.api.add_invalid(&tx3); + pool.api.add_invalid(&tx4); + block_on(pool.maintain(block_event(0))); + + // revalidation is in background + block_on(futures_timer::Delay::new(BACKGROUND_REVALIDATION_INTERVAL*2)); + + // then + // hash3 is now invalid + // hash4 is now invalid + + assert_eq!(pool.status().ready, 3); + assert_eq!( + futures::executor::block_on_stream(watcher3).collect::>(), + vec![TransactionStatus::Ready, TransactionStatus::Invalid], + ); + assert_eq!( + futures::executor::block_on_stream(watcher4).collect::>(), + vec![TransactionStatus::Ready, TransactionStatus::Invalid], + ); + + // when + let header_hash = pool.api.push_block(1, vec![tx0, tx1, tx2]).hash(); + block_on(pool.maintain(block_event(1))); + + let event = ChainEvent::Finalized { hash: header_hash.clone() }; + block_on(pool.maintain(event)); + + // then + // events for hash0 are: Ready, InBlock + // events for hash1 are: Ready, InBlock + // events for hash2 are: Ready, InBlock + assert_eq!( + futures::executor::block_on_stream(watcher0).collect::>(), + vec![TransactionStatus::Ready, TransactionStatus::InBlock(header_hash.clone()), TransactionStatus::Finalized(header_hash.clone())], + ); + assert_eq!( + futures::executor::block_on_stream(watcher1).collect::>(), + vec![TransactionStatus::Ready, TransactionStatus::InBlock(header_hash.clone()), TransactionStatus::Finalized(header_hash.clone())], + ); + assert_eq!( + futures::executor::block_on_stream(watcher2).collect::>(), + vec![TransactionStatus::Ready, TransactionStatus::InBlock(header_hash.clone()), TransactionStatus::Finalized(header_hash.clone())], + ); +} + +#[test] +fn can_track_heap_size() { + let (pool, _guard) = 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); +} + +#[test] +fn finalization() { + let xt = uxt(Alice, 209); + let api = TestApi::with_alice_nonce(209); + api.push_block(1, vec![]); + let (pool, _background) = BasicPool::new(Default::default(), api.into()); + let watcher = block_on(pool.submit_and_watch(&BlockId::number(1), xt.clone())).expect("1. Imported"); + pool.api.push_block(2, vec![xt.clone()]); + + let header = pool.api.chain().read().header_by_number.get(&2).cloned().unwrap(); + let event = ChainEvent::NewBlock { + id: BlockId::Hash(header.hash()), + is_new_best: true, + header: header.clone(), + retracted: vec![] + }; + block_on(pool.maintain(event)); + + let event = ChainEvent::Finalized { hash: header.hash() }; + block_on(pool.maintain(event)); + + let mut stream = futures::executor::block_on_stream(watcher); + assert_eq!(stream.next(), Some(TransactionStatus::Ready)); + assert_eq!(stream.next(), Some(TransactionStatus::InBlock(header.hash()))); + assert_eq!(stream.next(), Some(TransactionStatus::Finalized(header.hash()))); + assert_eq!(stream.next(), None); +} + +#[test] +fn fork_aware_finalization() { + let api = TestApi::empty(); + // starting block A1 (last finalized.) + api.push_block(1, vec![]); + + let (pool, _background) = BasicPool::new(Default::default(), api.into()); + let mut canon_watchers = vec![]; + + let from_alice = uxt(Alice, 1); + let from_dave = uxt(Dave, 1); + let from_bob = uxt(Bob, 1); + let from_charlie = uxt(Charlie, 1); + pool.api.increment_nonce(Alice.into()); + pool.api.increment_nonce(Dave.into()); + pool.api.increment_nonce(Charlie.into()); + pool.api.increment_nonce(Bob.into()); + + let from_dave_watcher; + let from_bob_watcher; + let b1; + let d1; + let c2; + let d2; + + + // block B1 + { + let watcher = block_on(pool.submit_and_watch(&BlockId::number(1), from_alice.clone())).expect("1. Imported"); + let header = pool.api.push_block(2, vec![from_alice.clone()]); + canon_watchers.push((watcher, header.hash())); + + let event = ChainEvent::NewBlock { + id: BlockId::Number(2), + is_new_best: true, + header: header.clone(), + retracted: vec![], + }; + b1 = header.hash(); + block_on(pool.maintain(event)); + let event = ChainEvent::Finalized { hash: b1 }; + block_on(pool.maintain(event)); + } + + // block C2 + { + let header = pool.api.push_fork_block_with_parent(b1, vec![from_dave.clone()]); + from_dave_watcher = block_on(pool.submit_and_watch(&BlockId::number(1), from_dave.clone())) + .expect("1. Imported"); + let event = ChainEvent::NewBlock { + id: BlockId::Hash(header.hash()), + is_new_best: true, + header: header.clone(), + retracted: vec![] + }; + c2 = header.hash(); + block_on(pool.maintain(event)); + } + + // block D2 + { + from_bob_watcher = block_on(pool.submit_and_watch(&BlockId::number(1), from_bob.clone())).expect("1. Imported"); + let header = pool.api.push_fork_block_with_parent(c2, vec![from_bob.clone()]); + + let event = ChainEvent::NewBlock { + id: BlockId::Hash(header.hash()), + is_new_best: true, + header: header.clone(), + retracted: vec![] + }; + d2 = header.hash(); + block_on(pool.maintain(event)); + } + + // block C1 + { + let watcher = block_on(pool.submit_and_watch(&BlockId::number(1), from_charlie.clone())).expect("1.Imported"); + let header = pool.api.push_block(3, vec![from_charlie.clone()]); + + canon_watchers.push((watcher, header.hash())); + let event = ChainEvent::NewBlock { + id: BlockId::Number(3), + is_new_best: true, + header: header.clone(), + retracted: vec![c2, d2], + }; + block_on(pool.maintain(event)); + let event = ChainEvent::Finalized { hash: header.hash() }; + block_on(pool.maintain(event)); + } + + // block D1 + { + let xt = uxt(Eve, 0); + let w = block_on(pool.submit_and_watch(&BlockId::number(1), xt.clone())).expect("1. Imported"); + let header = pool.api.push_block(4, vec![xt.clone()]); + canon_watchers.push((w, header.hash())); + + let event = ChainEvent::NewBlock { + id: BlockId::Hash(header.hash()), + is_new_best: true, + header: header.clone(), + retracted: vec![] + }; + d1 = header.hash(); + block_on(pool.maintain(event)); + let event = ChainEvent::Finalized { hash: d1 }; + block_on(pool.maintain(event)); + } + + let e1; + + // block e1 + { + let header = pool.api.push_block(5, vec![from_dave]); + e1 = header.hash(); + let event = ChainEvent::NewBlock { + id: BlockId::Hash(header.hash()), + is_new_best: true, + header: header.clone(), + retracted: vec![] + }; + block_on(pool.maintain(event)); + block_on(pool.maintain(ChainEvent::Finalized { hash: e1 })); + } + + + for (canon_watcher, h) in canon_watchers { + let mut stream = futures::executor::block_on_stream(canon_watcher); + assert_eq!(stream.next(), Some(TransactionStatus::Ready)); + assert_eq!(stream.next(), Some(TransactionStatus::InBlock(h.clone()))); + assert_eq!(stream.next(), Some(TransactionStatus::Finalized(h))); + assert_eq!(stream.next(), None); + } + + + { + let mut stream= futures::executor::block_on_stream(from_dave_watcher); + assert_eq!(stream.next(), Some(TransactionStatus::Ready)); + assert_eq!(stream.next(), Some(TransactionStatus::InBlock(c2.clone()))); + assert_eq!(stream.next(), Some(TransactionStatus::Retracted(c2))); + + // can be either Ready, or InBlock, depending on which event comes first + assert_eq!( + match stream.next() { + Some(TransactionStatus::Ready) => stream.next(), + val @ _ => val, + }, + Some(TransactionStatus::InBlock(e1)), + ); + assert_eq!(stream.next(), Some(TransactionStatus::Finalized(e1.clone()))); + assert_eq!(stream.next(), None); + } + + { + let mut stream= futures::executor::block_on_stream(from_bob_watcher); + assert_eq!(stream.next(), Some(TransactionStatus::Ready)); + assert_eq!(stream.next(), Some(TransactionStatus::InBlock(d2.clone()))); + assert_eq!(stream.next(), Some(TransactionStatus::Retracted(d2))); + } + +} diff --git a/client/transaction-pool/src/tests.rs b/client/transaction-pool/src/tests.rs deleted file mode 100644 index 1199e41cf8740f1ffd05be970c3ae70028582b0a..0000000000000000000000000000000000000000 --- a/client/transaction-pool/src/tests.rs +++ /dev/null @@ -1,222 +0,0 @@ -// 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 . - - -use super::*; - -use codec::Encode; -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}, -}; - -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::default()) -} - -#[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(); - assert_eq!(pending, vec![209]); -} - -#[test] -fn multiple_submission_should_work() { - let pool = pool(); - 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]); -} - -#[test] -fn early_nonce_should_be_culled() { - let pool = pool(); - block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 208))).unwrap(); - - let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); - assert_eq!(pending, Vec::::new()); -} - -#[test] -fn late_nonce_should_be_queued() { - let pool = pool(); - - 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::::new()); - - block_on(pool.submit_one(&BlockId::number(0), uxt(Alice, 209))).unwrap(); - let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); - assert_eq!(pending, vec![209, 210]); -} - -#[test] -fn prune_tags_should_work() { - let pool = pool(); - 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(); - - let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); - assert_eq!(pending, vec![210]); -} - -#[test] -fn should_ban_invalid_transactions() { - let pool = pool(); - let uxt = uxt(Alice, 209); - let hash = block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap(); - pool.remove_invalid(&[hash]); - block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap_err(); - - // when - let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); - assert_eq!(pending, Vec::::new()); - - // then - block_on(pool.submit_one(&BlockId::number(0), uxt.clone())).unwrap_err(); -} - -#[test] -fn should_correctly_prune_transactions_providing_more_than_one_tag() { - let mut api = TestApi::default(); - api.modifier = Box::new(|v: &mut ValidTransaction| { - v.provides.push(vec![155]); - }); - let pool = Pool::new(Default::default(), api); - 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. - 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 - let xt = uxt(Alice, 211); - block_on(pool.submit_one(&BlockId::number(2), xt.clone())).expect("2. Imported"); - assert_eq!(pool.status().ready, 1); - assert_eq!(pool.status().future, 1); - let pending: Vec<_> = pool.ready().map(|a| a.data.transfer().nonce).collect(); - assert_eq!(pending, vec![211]); - - // prune it and make sure the pool is empty - 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); -} diff --git a/docs/CODE_OF_CONDUCT.adoc b/docs/CODE_OF_CONDUCT.adoc index 7cb0210e8efac2575e23bd46a000af59bb36cb3e..0f7de7c7efee14bc03258e0aa4f5e7771a912067 100644 --- a/docs/CODE_OF_CONDUCT.adoc +++ b/docs/CODE_OF_CONDUCT.adoc @@ -22,7 +22,7 @@ Examples of unacceptable behavior by participants include: * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting -=== Facilitation, Not Strongarming +=== Facilitation, Not Strong Arming We recognize that this software is merely a tool for users to create and maintain their blockchain of preference. We see that blockchains are naturally community platforms with users being the ultimate decision makers. We assert that good software will maximize user agency by facilitate user-expression on the network. As such: diff --git a/docs/CONTRIBUTING.adoc b/docs/CONTRIBUTING.adoc index 817e1d7489f3968c2a851e78b1728949776b544d..c83b686b097e6a486a22cd846b5b25e373e88642 100644 --- a/docs/CONTRIBUTING.adoc +++ b/docs/CONTRIBUTING.adoc @@ -52,7 +52,7 @@ If your PR changes the external APIs or interfaces used by Polkadot, **a corresp To update a corresponding Polkadot PR: -0. Pull lastet Polkadot master (or clone it, if you haven't yet). +0. Pull latest Polkadot master (or clone it, if you haven't yet). 1. Replace `polkadot-master` in all `Cargo.toml` with the name of the PR-branch - e.g. by running `find . -name "Cargo.toml" -exec sed -i "s/polkadot-master/PR_BRANCH/g" {}` (and to your repo, if the branch is not on mainline); Commit this change. 2. Make the changes required to pass the build again. 3. Submit all this as a PR against the Polkadot Repo, link that new PR in the existing substrate PR for reference diff --git a/docs/README.adoc b/docs/README.adoc index d8c582296cabbfa396a7e1761a07f0555d3f1fd1..bbc2713fbeb798620bfb4fad22ae52940a066313 100644 --- a/docs/README.adoc +++ b/docs/README.adoc @@ -24,13 +24,13 @@ Substrate is designed for use in one of three ways: **1. Trivial**: By running the Substrate binary `substrate` and configuring it with a genesis block that includes the current demonstration runtime. In this case, you just build Substrate, configure a JSON file, and launch your own blockchain. This affords you the least amount of customizability, primarily allowing you to change the genesis parameters of the various included runtime modules such as balances, staking, block-period, fees, and governance. -**2. Modular**: By hacking together pallets built with Substrate FRAME into a new runtime and possibly altering or reconfiguring the Substrate client's block authoring logic. This affords you a very large amount of freedom over your blockchain's logic, letting you change datatypes, add or remove modules, and crucially, add your own modules. Much can be changed without touching the block authoring logic (since it is generic). If this is the case, then the existing Substrate binary can be used for block authoring and syncing. If the block authoring logic needs to be tweaked, then a new, altered block authoring binary must be built as a separate project and used by validators. This is how the Polkadot relay chain is built and should suffice for almost all circumstances in the near to mid-term. +**2. Modular**: By hacking together pallets built with Substrate FRAME into a new runtime and possibly altering or reconfiguring the Substrate client's block authoring logic. This affords you a very large amount of freedom over your blockchain's logic, letting you change data types, add or remove modules, and crucially, add your own modules. Much can be changed without touching the block authoring logic (since it is generic). If this is the case, then the existing Substrate binary can be used for block authoring and syncing. If the block authoring logic needs to be tweaked, then a new, altered block authoring binary must be built as a separate project and used by validators. This is how the Polkadot relay chain is built and should suffice for almost all circumstances in the near to mid-term. **3. Generic**: The entire SRML can be ignored and the entire runtime designed and implemented from scratch. If desired, this can be done in a language other than Rust, provided it can target WebAssembly. If the runtime can be made compatible with the existing client's block authoring logic, then you can simply construct a new genesis block from your Wasm blob and launch your chain with the existing Rust-based Substrate client. If not, then you'll need to alter the client's block authoring logic accordingly. This is probably a useless option for most projects right now, but provides complete flexibility allowing for a long-term, far-reaching upgrade path for the Substrate paradigm. === The Basics of Substrate -Substrate is a blockchain platform with a completely generic state transition function. That said, it does come with both standards and conventions (particularly regarding the Runtime Module Library) regarding underlying data structures. Roughly speaking, these core datatypes correspond to +trait+s in terms of the actual non-negotiable standard and generic +struct+s in terms of the convention. +Substrate is a blockchain platform with a completely generic state transition function. That said, it does come with both standards and conventions (particularly regarding the Runtime Module Library) regarding underlying data structures. Roughly speaking, these core data types correspond to +trait+s in terms of the actual non-negotiable standard and generic +struct+s in terms of the convention. ``` Header := Parent + ExtrinsicsRoot + StorageRoot + Digest diff --git a/docs/SECURITY.md b/docs/SECURITY.md index b850e5462e2a8368c1e58a417eb9cef229b49537..7240218fa87296309fea68ee4f51471d923292a8 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -36,7 +36,7 @@ Responsible investigation and reporting includes, but isn't limited to, the foll ## Bug Bounty Program -Our Bug Bounty Program allows us to recognise and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html). +Our Bug Bounty Program allows us to recognize and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html). diff --git a/docs/Structure.adoc b/docs/Structure.adoc index cb0e4b28cb001fb77e62e41a98775b893c447a8e..c8cd63506a347f3252a8f5d40c9cf3d2dd4db7d8 100644 --- a/docs/Structure.adoc +++ b/docs/Structure.adoc @@ -67,7 +67,7 @@ There are a few crates with the `frame-` prefix. These do not contain domain-spe ** only helpers may be published ** purely testing crates must be `publish = false` -All tests that have to pull (dev)-dependencies out of their subtree and would thus break the dependency rules are considered intergration tests and should be stored in here. Only helper-crates in here shall be published, everything else is expected to be non-publish. +All tests that have to pull (dev)-dependencies out of their subtree and would thus break the dependency rules are considered integration tests and should be stored in here. Only helper-crates in here shall be published, everything else is expected to be non-publish. === Binaries and template 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 888bca100179744506dc43fccafdd1bf82d1b3f2..a26437134bfae5c0378df39ae0a185ffdfe64612 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -438,6 +438,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..e4e37a4b7ac1f36b32764e03954d5a413feea730 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, }); } @@ -360,7 +365,7 @@ impl Module { // finds the start slot of the current epoch. only guaranteed to // give correct results after `do_initialize` of the first block // in the chain (as its result is based off of `GenesisSlot`). - fn current_epoch_start() -> SlotNumber { + pub fn current_epoch_start() -> SlotNumber { (EpochIndex::get() * T::EpochDuration::get()) + GenesisSlot::get() } @@ -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..2c8ae3e18fe8e05b6937698f8e47ff17be10ccee 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 data type. + 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 4c7f85bede0588bee19b97b3ca2bc7aec93a180f..958164a8a057d99c74be01171bfae4ffa24bfd03 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; @@ -94,32 +85,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, } } } @@ -128,15 +113,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 { @@ -144,14 +120,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(); @@ -168,26 +138,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..a3ea901a6fd248c4c775e024525d286e913ecdaa 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . //! Collective system: Members of a set of account IDs can make their collective feelings known -//! through dispatched calls from one of two specialised origins. +//! through dispatched calls from one of two specialized origins. //! //! The membership can be provided in one of two ways: either directly, using the Root-dispatchable //! function `set_members`, or indirectly, through implementing the `ChangeMembers` @@ -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/COMPLEXITY.md b/frame/contracts/COMPLEXITY.md index a29127f778f7ebc37f048a2bcb302e081385564f..e44d3006c8ed363e63909f9c8504545653f71d71 100644 --- a/frame/contracts/COMPLEXITY.md +++ b/frame/contracts/COMPLEXITY.md @@ -141,7 +141,7 @@ Note that in case of storage modification we need to construct a key in the unde - then perform `blake2_256` hashing of the storage key. - concatenation of these hashes will constitute the key in the underlying storage. -There is also a special case to think of: if the balance of some account goes below `existential_deposit`, then all storage entries of that account will be erased, which requires time proprotional to the number of storage entries that account has. +There is also a special case to think of: if the balance of some account goes below `existential_deposit`, then all storage entries of that account will be erased, which requires time proportional to the number of storage entries that account has. **complexity**: `N` inserts into a `Map` or eventually into the storage (if committed). Every deleted account will induce removal of all its storage which is proportional to the number of storage entries that account has. @@ -236,7 +236,7 @@ This function takes the code of the constructor and input data. Instantiation of **Note** that the complexity of executing the constructor code should be considered separately. -**Note** that the complexity of `DetermineContractAddress` hook should be considered separately as well. Most likely it will use some kind of hashing over the code of the constructor and input data. The default `SimpleAddressDeterminator` does precisely that. +**Note** that the complexity of `DetermineContractAddress` hook should be considered separately as well. Most likely it will use some kind of hashing over the code of the constructor and input data. The default `SimpleAddressDeterminer` does precisely that. **Note** that the constructor returns code in the owned form and it's obtained via return facilities, which should have take fee for the return value. 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..6a74a417fa0fe30040297de90a52805ba3a15855 --- /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 relevant 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..6fb629b02458129e0bc8ffe13bdf363cd45479e1 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 relevant 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..52dddb177bbc74538f869419d20beebd1cc2b916 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 relevant 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..53a6c484fccdbd992a67175909b7b6e90d0665a0 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 }, ); @@ -1130,7 +1122,7 @@ mod tests { ); }); - // This test sends 50 units of currency as an endownment to a newly + // This test sends 50 units of currency as an endowment to a newly // instantiated contract. ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let mut loader = MockLoader::empty(); @@ -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..42cbaa3a7c2af2e7251d885c1e55c2187fa145b8 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; @@ -142,7 +143,7 @@ pub trait ComputeDispatchFee { fn compute_dispatch_fee(call: &Call) -> Balance; } -/// Information for managing an acocunt and its sub trie abstraction. +/// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account #[derive(Encode, Decode, RuntimeDebug)] pub enum ContractInfo { @@ -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>; @@ -437,8 +432,8 @@ pub trait Trait: frame_system::Trait { /// and the account id that requested the account creation. /// /// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)` -pub struct SimpleAddressDeterminator(PhantomData); -impl ContractAddressFor, T::AccountId> for SimpleAddressDeterminator +pub struct SimpleAddressDeterminer(PhantomData); +impl ContractAddressFor, T::AccountId> for SimpleAddressDeterminer where T::AccountId: UncheckedFrom + AsRef<[u8]> { @@ -499,7 +494,7 @@ decl_module! { /// The minimum amount required to generate a tombstone. const TombstoneDeposit: BalanceOf = T::TombstoneDeposit::get(); - /// Size of a contract at the time of instantiaion. This is a simple way to ensure that + /// Size of a contract at the time of instantiation. This is a simple way to ensure that /// empty contracts eventually gets deleted. const StorageSizeOffset: u32 = T::StorageSizeOffset::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..8b6825419c81e570033f695f52cf03c1f20847b6 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 relevant 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..ddd532334c158181feb8d3041f4f6984d45db727 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::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::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,52 +540,67 @@ 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::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![], }, // 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![], }, - // Event emited as a result of dispatch. + // Event emitted 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::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![], + }, + 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 @@ -685,6 +722,51 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { }); } +const CODE_RUN_OUT_OF_GAS: &str = r#" +(module + (func (export "call") + (loop $inf (br $inf)) ;; just run out of gas + (unreachable) + ) + (func (export "deploy")) +) +"#; + +#[test] +fn run_out_of_gas() { + let (wasm, code_hash) = compile_module::(CODE_RUN_OUT_OF_GAS).unwrap(); + + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); + + 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![], + )); + + // Call the contract with a fixed gas limit. It must run out of gas because it just + // loops forever. + assert_err!( + Contracts::call( + Origin::signed(ALICE), + BOB, // newly created account + 0, + 1000, + vec![], + ), + "ran out of gas during contract execution" + ); + }); +} + const CODE_SET_RENT: &str = r#" (module (import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32))) @@ -814,19 +896,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 +928,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 +938,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 +965,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 +981,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 +996,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 +1008,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,46 +1021,46 @@ 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. -/// if removes is true then assert that the contract is a tombstonedead +/// If `removes` is true then assert that the contract is a tombstone. fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); 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 +1092,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 +1105,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 +1113,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 +1121,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 +1139,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 +1148,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 +1156,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 +1174,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 +1187,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 +1195,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 +1207,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 +1216,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 +1300,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 +1317,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 +1413,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 +1457,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 +1473,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 +1492,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 +1511,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 +1533,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 +1544,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 +1606,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 +1692,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 +1706,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 +1716,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 +2056,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 +2069,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, @@ -2017,11 +2119,11 @@ const CODE_SELF_DESTRUCT: &str = r#" ;; Read own address into memory. (call $ext_scratch_read (i32.const 16) ;; Pointer to write address to - (i32.const 0) ;; Offset into scrach buffer + (i32.const 0) ;; Offset into scratch buffer (i32.const 8) ;; Length of encoded address ) - ;; Recursively call self with empty imput data. + ;; Recursively call self with empty input data. (call $assert (i32.eq (call $ext_call @@ -2053,7 +2155,7 @@ const CODE_SELF_DESTRUCT: &str = r#" ;; Read balance into memory. (call $ext_scratch_read (i32.const 8) ;; Pointer to write balance to - (i32.const 0) ;; Offset into scrach buffer + (i32.const 0) ;; Offset into scratch buffer (i32.const 8) ;; Length of encoded balance ) @@ -2081,10 +2183,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 +2201,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 +2219,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 +2239,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 +2419,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 +2439,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, @@ -2382,7 +2484,7 @@ const CODE_SELF_DESTRUCTING_CONSTRUCTOR: &str = r#" ;; Read balance into memory. (call $ext_scratch_read (i32.const 8) ;; Pointer to write balance to - (i32.const 0) ;; Offset into scrach buffer + (i32.const 0) ;; Offset into scratch buffer (i32.const 8) ;; Length of encoded balance ) @@ -2412,12 +2514,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 +2635,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..a84556d884ce97a3a2c16fec8b1051a6d72cfe35 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}; @@ -41,6 +39,8 @@ const TRAP_RETURN_CODE: u32 = 0x0100; enum SpecialTrap { /// Signals that trap was generated in response to call `ext_return` host function. Return(Vec), + /// Signals that trap was generated because the contract exhausted its gas limit. + OutOfGas, } /// Can only be used for one call. @@ -76,9 +76,21 @@ pub(crate) fn to_execution_result( runtime: Runtime, sandbox_result: Result, ) -> ExecResult { - // Special case. The trap was the result of the execution `return` host function. - if let Some(SpecialTrap::Return(data)) = runtime.special_trap { - return Ok(ExecReturnValue { status: STATUS_SUCCESS, data }); + match runtime.special_trap { + // The trap was the result of the execution `return` host function. + Some(SpecialTrap::Return(data)) => { + return Ok(ExecReturnValue { + status: STATUS_SUCCESS, + data, + }) + } + Some(SpecialTrap::OutOfGas) => { + return Err(ExecError { + reason: "ran out of gas during contract execution".into(), + buffer: runtime.scratch_buf, + }) + } + _ => (), } // Check the exact type of the error. @@ -89,7 +101,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 }) @@ -181,11 +193,15 @@ impl Token for RuntimeToken { fn charge_gas>( gas_meter: &mut GasMeter, metadata: &Tok::Metadata, + special_trap: &mut Option, token: Tok, ) -> Result<(), sp_sandbox::HostError> { match gas_meter.charge(metadata, token) { GasMeterResult::Proceed => Ok(()), - GasMeterResult::OutOfGas => Err(sp_sandbox::HostError), + GasMeterResult::OutOfGas => { + *special_trap = Some(SpecialTrap::OutOfGas); + Err(sp_sandbox::HostError) + }, } } @@ -202,7 +218,12 @@ fn read_sandbox_memory( ptr: u32, len: u32, ) -> Result, sp_sandbox::HostError> { - charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReadMemory(len))?; + charge_gas( + ctx.gas_meter, + ctx.schedule, + &mut ctx.special_trap, + RuntimeToken::ReadMemory(len), + )?; let mut buf = vec![0u8; len as usize]; ctx.memory.get(ptr, buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?; @@ -222,7 +243,12 @@ fn read_sandbox_memory_into_scratch( ptr: u32, len: u32, ) -> Result<(), sp_sandbox::HostError> { - charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReadMemory(len))?; + charge_gas( + ctx.gas_meter, + ctx.schedule, + &mut ctx.special_trap, + RuntimeToken::ReadMemory(len), + )?; ctx.scratch_buf.resize(len as usize, 0); ctx.memory.get(ptr, ctx.scratch_buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?; @@ -242,7 +268,12 @@ fn read_sandbox_memory_into_buf( ptr: u32, buf: &mut [u8], ) -> Result<(), sp_sandbox::HostError> { - charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReadMemory(buf.len() as u32))?; + charge_gas( + ctx.gas_meter, + ctx.schedule, + &mut ctx.special_trap, + RuntimeToken::ReadMemory(buf.len() as u32), + )?; ctx.memory.get(ptr, buf).map_err(Into::into) } @@ -275,12 +306,18 @@ fn read_sandbox_memory_as( /// - designated area is not within the bounds of the sandbox memory. fn write_sandbox_memory( schedule: &Schedule, + special_trap: &mut Option, gas_meter: &mut GasMeter, memory: &sp_sandbox::Memory, ptr: u32, buf: &[u8], ) -> Result<(), sp_sandbox::HostError> { - charge_gas(gas_meter, schedule, RuntimeToken::WriteMemory(buf.len() as u32))?; + charge_gas( + gas_meter, + schedule, + special_trap, + RuntimeToken::WriteMemory(buf.len() as u32), + )?; memory.set(ptr, buf)?; @@ -302,7 +339,12 @@ define_env!(Env, , // // - amount: How much gas is used. gas(ctx, amount: u32) => { - charge_gas(&mut ctx.gas_meter, ctx.schedule, RuntimeToken::Explicit(amount))?; + charge_gas( + &mut ctx.gas_meter, + ctx.schedule, + &mut ctx.special_trap, + RuntimeToken::Explicit(amount) + )?; Ok(()) }, @@ -522,7 +564,12 @@ define_env!(Env, , // // This is the only way to return a data buffer to the caller. ext_return(ctx, data_ptr: u32, data_len: u32) => { - charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReturnData(data_len))?; + charge_gas( + ctx.gas_meter, + ctx.schedule, + &mut ctx.special_trap, + RuntimeToken::ReturnData(data_len) + )?; read_sandbox_memory_into_scratch(ctx, data_ptr, data_len)?; let output_buf = mem::replace(&mut ctx.scratch_buf, Vec::new()); @@ -623,6 +670,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. // @@ -637,7 +701,12 @@ define_env!(Env, , let balance_fee = <::T as Trait>::ComputeDispatchFee::compute_dispatch_fee(&call); approx_gas_for_balance(ctx.gas_meter.gas_price(), balance_fee) }; - charge_gas(&mut ctx.gas_meter, ctx.schedule, RuntimeToken::ComputedDispatchFee(fee))?; + charge_gas( + &mut ctx.gas_meter, + ctx.schedule, + &mut ctx.special_trap, + RuntimeToken::ComputedDispatchFee(fee) + )?; ctx.ext.note_dispatch_call(call); @@ -647,10 +716,10 @@ define_env!(Env, , // Record a request to restore the caller contract to the specified contract. // // At the finalization stage, i.e. when all changes from the extrinsic that invoked this - // contract are commited, this function will compute a tombstone hash from the caller's + // contract are committed, this function will compute a tombstone hash from the caller's // storage and the given code hash and if the hash matches the hash found in the tombstone at // the specified address - kill the caller contract and restore the destination contract and set - // the specified `rent_allowance`. All caller's funds are transfered to the destination. + // the specified `rent_allowance`. All caller's funds are transferred to the destination. // // This function doesn't perform restoration right away but defers it to the end of the // transaction. If there is no tombstone in the destination address or if the hashes don't match @@ -741,6 +810,7 @@ define_env!(Env, , // Finally, perform the write. write_sandbox_memory( ctx.schedule, + &mut ctx.special_trap, ctx.gas_meter, &ctx.memory, dest_ptr, @@ -788,6 +858,7 @@ define_env!(Env, , charge_gas( ctx.gas_meter, ctx.schedule, + &mut ctx.special_trap, RuntimeToken::DepositEvent(topics.len() as u32, data_len) )?; ctx.ext.deposit_event(topics, event_data); 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..c2c44918b036cfb05974c7f748870b36417d3754 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,12 +2061,12 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } #[test] - /// If transactor already voted, delegated vote is overwriten. + /// If transactor already voted, delegated vote is overwritten. fn single_proposal_should_work_with_vote_and_delegation() { new_test_ext().execute_with(|| { System::set_block_number(0); @@ -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,12 +2116,12 @@ mod tests { fast_forward_to(6); - assert_eq!(Balances::free_balance(&42), 2); + assert_eq!(Balances::free_balance(42), 2); }); } #[test] - /// If transactor voted, delegated vote is overwriten. + /// If transactor voted, delegated vote is overwritten. fn single_proposal_should_work_with_delegation_and_vote() { new_test_ext().execute_with(|| { System::set_block_number(0); @@ -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..95d1858476287aa2c809e3d569c1e56b36638c61 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 } }; @@ -81,7 +81,7 @@ mod tests; // for B blocks following, there's a counting period whereby each of the candidates that believe // they fall in the top K+C voted can present themselves. they get the total stake -// recorded (based on the snapshot); an ordered list is maintained (the leaderboard). Noone may +// recorded (based on the snapshot); an ordered list is maintained (the leaderboard). No one may // present themselves that, if elected, would result in being included twice in the collective // (important since existing members will have their approval votes as it may be that they // don't get removed), nor if existing presenters would mean they're not in the top K+C. @@ -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/backend.rs b/frame/evm/src/backend.rs index b6c3078a3339a2d27c43c08a732599ae9972e84f..d72c8b785e3468762054c9bd028fe3e3a1b730db 100644 --- a/frame/evm/src/backend.rs +++ b/frame/evm/src/backend.rs @@ -29,7 +29,7 @@ pub struct Log { pub address: H160, /// Topics of the log. pub topics: Vec, - /// Bytearray data of the log. + /// Byte array data of the log. pub data: Vec, } diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 8d504127245d0b18d9b14cf35e649dcd20daa2aa..abf20114f2c6a7f2fb70760ca0de6ee3c45d26c7 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, } } @@ -204,7 +177,7 @@ decl_module! { fn deposit_event() = default; - /// Despoit balance from currency/balances module into EVM. + /// Deposit balance from currency/balances module into EVM. #[weight = SimpleDispatchInfo::FixedNormal(10_000)] fn deposit_balance(origin, value: BalanceOf) { let sender = ensure_signed(origin)?; @@ -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..dbdc2efcf80b514bbc7686d79ea16cdb70030256 100644 --- a/frame/example/src/lib.rs +++ b/frame/example/src/lib.rs @@ -32,7 +32,7 @@ //! not the internals of the module implementation. Only state inputs, //! outputs, and a brief description that mentions whether calling it //! requires root, but without repeating the source code details. -//! Capitalise the first word of each documentation comment and end it with +//! Capitalize the first word of each documentation comment and end it with //! a full stop. See //! Generic example of annotating source code with documentation comments @@ -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; @@ -383,7 +383,7 @@ decl_event!( // - Unsigned calls that can be of two kinds: // * "Inherent extrinsics" that are opinions generally held by the block // authors that build child blocks. -// * Unsigned Transactions that are of intrinsic recognisable utility to the +// * Unsigned Transactions that are of intrinsic recognizable utility to the // network, and are validated by the runtime. // // Information about where this dispatch initiated from is provided as the first argument @@ -572,7 +572,7 @@ impl Module { // method. This example will not cover this type of extension. See `CheckRuntime` in system module // for an example. // -// Using the extension, you can add some hooks to the lifecycle of each transaction. Note that by +// Using the extension, you can add some hooks to the life cycle of each transaction. Note that by // default, an extension is applied to all `Call` functions (i.e. all transactions). the `Call` enum // variant is given to each function of `SignedExtension`. Hence, you can filter based on module or // a particular call if needed. @@ -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 f3da41729d2c4600dec057812bf4259e2fafc811..715521f68581baf9541843ddb79e88914892a710 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -79,13 +79,15 @@ use sp_std::{prelude::*, marker::PhantomData}; use frame_support::weights::{GetDispatchInfo, WeighBlock, DispatchInfo}; use sp_runtime::{ - generic::Digest, ApplyExtrinsicResult, + generic::Digest, + ApplyExtrinsicResult, traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize, OnInitialize, NumberFor, Block as BlockT, OffchainWorker, Dispatchable, Saturating, }, transaction_validity::TransactionValidity, }; +use sp_runtime::generic::CheckSignature; #[allow(deprecated)] use sp_runtime::traits::ValidateUnsigned; use codec::{Codec, Encode}; @@ -255,15 +257,23 @@ where pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult { let encoded = uxt.encode(); let encoded_len = encoded.len(); - Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded)) + Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded), CheckSignature::Yes) + } + + /// Apply extrinsic outside of the block execution function. + /// + /// Same as `apply_extrinsic`, but skips signature checks. + pub fn apply_trusted_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult { + let encoded = uxt.encode(); + let encoded_len = encoded.len(); + Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded), CheckSignature::No) } /// Apply an extrinsic inside the block execution function. 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), + match Self::apply_extrinsic_with_len(uxt, l, None, CheckSignature::Yes) { + Ok(_) => (), Err(e) => { let err: &'static str = e.into(); panic!(err) }, } } @@ -273,9 +283,13 @@ where uxt: Block::Extrinsic, encoded_len: usize, to_note: Option>, + check_signature: CheckSignature, ) -> ApplyExtrinsicResult { // Verify that the signature is good. - let xt = uxt.check(&Default::default())?; + let xt = uxt.check( + check_signature, + &Default::default(), + )?; // We don't need to make sure to `note_extrinsic` only after we know it's going to be // executed to prevent it from leaking in storage since at this point, it will either @@ -323,7 +337,7 @@ where /// Changes made to storage should be discarded. pub fn validate_transaction(uxt: Block::Extrinsic) -> TransactionValidity { let encoded_len = uxt.using_encoded(|d| d.len()); - let xt = uxt.check(&Default::default())?; + let xt = uxt.check(CheckSignature::Yes, &Default::default())?; let dispatch_info = xt.get_dispatch_info(); xt.validate::(dispatch_info, encoded_len) @@ -358,7 +372,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 +400,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 +431,7 @@ mod tests { impl_outer_event!{ pub enum MetaEvent for Runtime { + system, balances, } } @@ -452,23 +467,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! { @@ -522,8 +533,8 @@ mod tests { ) } - fn sign_extra(who: u64, nonce: u64, fee: u64) -> Option<(u64, SignedExtra)> { - Some((who, extra(nonce, fee))) + fn sign_extra(who: u64, nonce: u64, fee: u64) -> (u64, SignedExtra) { + (who, extra(nonce, fee)) } #[test] @@ -531,9 +542,8 @@ 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 xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(2, 69))); let weight = xt.get_dispatch_info().weight as u64; let mut t = sp_io::TestExternalities::new(t); t.execute_with(|| { @@ -555,7 +565,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() } @@ -567,7 +576,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![], }, }, @@ -614,7 +623,7 @@ mod tests { fn bad_extrinsic_not_inserted() { let mut t = new_test_ext(1); // bad nonce check! - let xt = sp_runtime::testing::TestXt(sign_extra(1, 30, 0), Call::Balances(BalancesCall::transfer(33, 69))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 30, 0), Call::Balances(BalancesCall::transfer(33, 69))); t.execute_with(|| { Executive::initialize_block(&Header::new( 1, @@ -632,7 +641,7 @@ mod tests { fn block_weight_limit_enforced() { let mut t = new_test_ext(10000); // given: TestXt uses the encoded len as fixed Len: - let xt = sp_runtime::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); let encoded = xt.encode(); let encoded_len = encoded.len() as Weight; let limit = AvailableBlockRatio::get() * MaximumBlockWeight::get() - 175; @@ -649,7 +658,7 @@ mod tests { assert_eq!(>::all_extrinsics_weight(), 175); for nonce in 0..=num_to_exhaust_block { - let xt = sp_runtime::testing::TestXt( + let xt = sp_runtime::testing::TestXt::new_signed( sign_extra(1, nonce.into(), 0), Call::Balances(BalancesCall::transfer(33, 0)), ); let res = Executive::apply_extrinsic(xt); @@ -669,9 +678,9 @@ mod tests { #[test] fn block_weight_and_size_is_stored_per_tx() { - let xt = sp_runtime::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); - let x1 = sp_runtime::testing::TestXt(sign_extra(1, 1, 0), Call::Balances(BalancesCall::transfer(33, 0))); - let x2 = sp_runtime::testing::TestXt(sign_extra(1, 2, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let xt = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let x1 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 1, 0), Call::Balances(BalancesCall::transfer(33, 0))); + let x2 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 2, 0), Call::Balances(BalancesCall::transfer(33, 0))); let len = xt.clone().encode().len() as u32; let mut t = new_test_ext(1); t.execute_with(|| { @@ -695,7 +704,7 @@ mod tests { #[test] fn validate_unsigned() { - let xt = sp_runtime::testing::TestXt(None, Call::Balances(BalancesCall::set_balance(33, 69, 69))); + let xt = sp_runtime::testing::TestXt::new_unsigned(Call::Balances(BalancesCall::set_balance(33, 69, 69))); let mut t = new_test_ext(1); t.execute_with(|| { @@ -704,6 +713,28 @@ mod tests { }); } + #[test] + fn apply_trusted_skips_signature_check_but_not_others() { + let xt1 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))) + .badly_signed(); + + let mut t = new_test_ext(1); + + t.execute_with(|| { + assert_eq!(Executive::apply_trusted_extrinsic(xt1), Ok(Ok(()))); + }); + + let xt2 = sp_runtime::testing::TestXt::new_signed(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(33, 0))) + .invalid(TransactionValidityError::Invalid(InvalidTransaction::Call)); + + t.execute_with(|| { + assert_eq!( + Executive::apply_trusted_extrinsic(xt2), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }); + } + #[test] fn can_pay_for_tx_fee_on_full_lock() { let id: LockIdentifier = *b"0 "; @@ -714,10 +745,9 @@ mod tests { id, &1, 110, - Bounded::max_value(), lock, ); - let xt = sp_runtime::testing::TestXt( + let xt = sp_runtime::testing::TestXt::new_signed( sign_extra(1, 0, 0), Call::System(SystemCall::remark(vec![1u8])), ); 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..233054dd9dccb9318fd2c8dcf51750527d208116 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 { @@ -208,7 +208,7 @@ impl Subtrait for T { /// Asset creation options. #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub struct AssetOptions { - /// Initial issuance of this asset. All deposit to the creater of the asset. + /// Initial issuance of this asset. All deposit to the creator of the asset. #[codec(compact)] pub initial_issuance: Balance, /// Which accounts are allowed to possess this asset. @@ -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); } } @@ -915,7 +898,7 @@ pub trait AssetIdProvider { fn asset_id() -> Self::AssetId; } -// wrapping these imbalanes in a private module is necessary to ensure absolute privacy +// wrapping these imbalances in a private module is necessary to ensure absolute privacy // of the inner member. mod imbalances { use super::{ @@ -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..d5c0a877dfe75aba4236a2130372a80b06f74a6c 100644 --- a/frame/generic-asset/src/tests.rs +++ b/frame/generic-asset/src/tests.rs @@ -440,7 +440,7 @@ fn reserve_should_not_moves_amount_from_balance_to_reserved_balance() { // Then // - unreserved should return 20. #[test] -fn unreserve_should_return_substratced_value_from_unreserved_amount_by_actual_acount_balance() { +fn unreserve_should_return_subtracted_value_from_unreserved_amount_by_actual_account_balance() { ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::unreserve(&1, &0, 120), 20); @@ -553,10 +553,10 @@ fn slash_reserved_should_return_none() { // Then // - Should not return None. #[test] -fn repatriate_reserved_return_amount_substracted_by_slash_amount() { +fn repatriate_reserved_return_amount_subtracted_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..6d3223f094859ffd8b3ccc07314026b0803a6e27 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, @@ -154,7 +154,7 @@ decl_storage! { /// DEPRECATED /// /// This used to store the current authority set, which has been migrated to the well-known - /// GRANDPA_AUTHORITES_KEY unhashed key. + /// GRANDPA_AUTHORITIES_KEY unhashed key. #[cfg(feature = "migrate-authorities")] pub(crate) Authorities get(fn authorities): AuthorityList; @@ -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; @@ -435,7 +435,7 @@ impl pallet_session::OneSessionHandler for Module where I: Iterator { // Always issue a change if `session` says that the validators have changed. - // Even if their session keys are the same as before, the underyling economic + // Even if their session keys are the same as before, the underlying economic // identities have changed. let current_set_id = if changed { let next_authorities = validators.map(|(_, k)| (k, 1)).collect::>(); 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..341356394da88447b4ec2ac0fd5df8b3b58eb4f5 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); @@ -614,7 +627,7 @@ decl_module! { /// - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as: /// /// ```nocompile - /// Self::registrars(reg_index).uwnrap().fee + /// Self::registrars(reg_index).unwrap().fee /// ``` /// /// Emits `JudgementRequested` if successful. @@ -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..a050ad3d8a98d5360344a8314bcc979f1327b987 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}; @@ -101,9 +102,10 @@ pub mod sr25519 { app_crypto!(sr25519, IM_ONLINE); } - /// An i'm online keypair using sr25519 as its crypto. - #[cfg(feature = "std")] - pub type AuthorityPair = app_sr25519::Pair; + sp_application_crypto::with_pair! { + /// An i'm online keypair using sr25519 as its crypto. + pub type AuthorityPair = app_sr25519::Pair; + } /// An i'm online signature using sr25519 as its crypto. pub type AuthoritySignature = app_sr25519::Signature; @@ -118,9 +120,10 @@ pub mod ed25519 { app_crypto!(ed25519, IM_ONLINE); } - /// An i'm online keypair using ed25519 as its crypto. - #[cfg(feature = "std")] - pub type AuthorityPair = app_ed25519::Pair; + sp_application_crypto::with_pair! { + /// An i'm online keypair using ed25519 as its crypto. + pub type AuthorityPair = app_ed25519::Pair; + } /// An i'm online signature using ed25519 as its crypto. pub type AuthoritySignature = app_ed25519::Signature; @@ -129,37 +132,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 +233,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 heartbeats, + /// 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 +263,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 +317,7 @@ decl_module! { ensure_none(origin)?; let current_session = >::current_index(); - let exists = ::exists( + let exists = ::contains_key( ¤t_session, &heartbeat.authority_index ); @@ -300,14 +345,31 @@ 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. +/// well since they're a valid proof of being online. impl pallet_authorship::EventHandler for Module { fn note_author(author: T::ValidatorId) { Self::note_authorship(author); @@ -338,7 +400,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 +411,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 +425,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 +576,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 deferred a bit to prevent spamming. 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..80056023e505c48fdc066cfdcc47559d84bab257 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(); @@ -218,7 +222,7 @@ fn should_generate_heartbeats() { assert_eq!(state.read().transactions.len(), 2); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.1 { + let heartbeat = match ex.call { crate::mock::Call::ImOnline(crate::Call::heartbeat(h, _)) => h, e => panic!("Unexpected call: {:?}", e), }; @@ -315,15 +319,20 @@ 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(); - // All validators have `0` as their session key, but we should only produce 1 hearbeat. + // All validators have `0` as their session key, but we should only produce 1 heartbeat. assert_eq!(pool_state.read().transactions.len(), 0); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.1 { + let heartbeat = match ex.call { crate::mock::Call::ImOnline(crate::Call::heartbeat(h, _)) => h, e => panic!("Unexpected call: {:?}", e), }; 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..d59a50179372d0064da3455ba34020c98de16b55 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 transferred 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..c275bb783b4aaa5b3b4e66ca39bd1a8f7262ad39 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -233,7 +233,7 @@ mod tests { }; 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. + // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup, BadOrigin}, testing::Header}; use frame_system::EnsureSignedBy; @@ -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..5ecb9f3443876ddb21f8ceb440e9505c6bc6773e 100644 --- a/frame/metadata/src/lib.rs +++ b/frame/metadata/src/lib.rs @@ -33,7 +33,7 @@ use sp_core::RuntimeDebug; #[cfg(feature = "std")] type StringBuf = String; -/// Curent prefix of metadata +/// Current prefix of metadata pub const META_RESERVED: u32 = 0x6174656d; // 'meta' warn endianness /// On `no_std` we do not support `Decode` and thus `StringBuf` is just `&'static str`. @@ -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..16797ffdef56a34b2d32fecc3229faf1b4548161 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)>; } } @@ -104,7 +104,7 @@ decl_error! { TooShort, /// A name is too long. TooLong, - /// An account in't named. + /// An account isn't named. Unnamed, } } @@ -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..7831ba65a3b067b4b68746d4272073e3a79b77c8 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, @@ -185,7 +187,7 @@ struct TriageOutcome { concurrent_offenders: Vec>, } -/// An auxilary struct for working with storage of indexes localized for a specific offence +/// An auxiliary struct for working with storage of indexes localized for a specific offence /// kind (specified by the `O` type parameter). /// /// This struct is responsible for aggregating storage writes and the underlying storage should not 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/offences/src/tests.rs b/frame/offences/src/tests.rs index 703361f77f9a102f4f5e2a0cddc40eca345bb182..f2f82cf7a87eee70677d28dc81cb88808ef19451 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -98,7 +98,7 @@ fn should_report_in_different_time_slot() { }); // when - // reportfor the second time + // report for the second time offence.time_slot += 1; Offences::report_offence(vec![], offence); 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..53d640688ef7dfaa0e4cfb171de84ce656546316 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/randomness-collective-flip/src/lib.rs @@ -135,7 +135,7 @@ impl Randomness for Module { let hash_series = >::get(); if !hash_series.is_empty() { - // Always the case after block 1 is initialised. + // Always the case after block 1 is initialized. hash_series.iter() .cycle() .skip(index) @@ -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; @@ -226,7 +231,7 @@ mod tests { } #[test] - fn test_random_material_parital() { + fn test_random_material_partial() { new_test_ext().execute_with(|| { let genesis_hash = System::parent_hash(); 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..6fa00af751e67cb33b0b05f8dd3b189083989462 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) //! @@ -44,9 +44,9 @@ //! the number of friends chosen. This deposit is returned in full when the account //! owner removes their recovery configuration. //! -//! ### Recovery Lifecycle +//! ### Recovery Life Cycle //! -//! The intended lifecycle of a successful recovery takes the following steps: +//! The intended life cycle of a successful recovery takes the following steps: //! 1. The account owner calls `create_recovery` to set up a recovery configuration //! for their account. //! 2. At some later time, the account owner loses access to their account and wants @@ -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 @@ -160,11 +160,8 @@ use codec::{Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, decl_error, ensure, Parameter, RuntimeDebug, - weights::{ - GetDispatchInfo, PaysFee, DispatchClass, ClassifyDispatch, Weight, WeighData, - SimpleDispatchInfo, - }, - traits::{Currency, ReservableCurrency, Get, OnReapAccount}, + weights::{GetDispatchInfo, SimpleDispatchInfo, FunctionOf}, + traits::{Currency, ReservableCurrency, Get, OnReapAccount, BalanceStatus}, }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -242,7 +239,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 +251,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 +313,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 @@ -327,10 +328,14 @@ decl_module! { /// - `call`: The call you want to make with the recovered account. /// /// # - /// - The weight of the `call`. + /// - The weight of the `call` + 10,000. /// - One storage lookup to check account is recovered by `who`. O(1) /// # - #[weight = ::Call>>::new()] + #[weight = FunctionOf( + |args: (&T::AccountId, &Box<::Call>)| args.1.get_dispatch_info().weight + 10_000, + |args: (&T::AccountId, &Box<::Call>)| args.1.get_dispatch_info().class, + true + )] fn as_recovered(origin, account: T::AccountId, call: Box<::Call> @@ -340,7 +345,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 +366,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 +402,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 +430,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 +457,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 +473,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 +517,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 +560,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 +588,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 @@ -642,25 +647,3 @@ impl OnReapAccount for Module { >::remove(who); } } - -/// Simple pass through for the weight functions. -struct Passthrough(sp_std::marker::PhantomData<(AccountId, Call)>); - -impl Passthrough { - fn new() -> Self { Self(Default::default()) } -} -impl WeighData<(&AccountId, &Box)> for Passthrough { - fn weigh_data(&self, (_, call): (&AccountId, &Box)) -> Weight { - call.get_dispatch_info().weight + 10_000 - } -} -impl ClassifyDispatch<(&AccountId, &Box)> for Passthrough { - fn classify_dispatch(&self, (_, call): (&AccountId, &Box)) -> DispatchClass { - call.get_dispatch_info().class - } -} -impl PaysFee<(&AccountId, &Box)> for Passthrough { - fn pays_fee(&self, (_, call): (&AccountId, &Box)) -> bool { - call.get_dispatch_info().pays_fee - } -} 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..97d4791cce508052d212117a4120290d53599eaa 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); }); } @@ -56,7 +56,7 @@ fn set_recovered_works() { } #[test] -fn recovery_lifecycle_works() { +fn recovery_life_cycle_works() { new_test_ext().execute_with(|| { let friends = vec![2, 3, 4]; let threshold = 3; @@ -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..cccb3c2b4cfb4c37dc69fb6a44665f70f0df6053 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -22,7 +22,7 @@ use std::cell::RefCell; use frame_support::{impl_outer_origin, parameter_types, weights::Weight, ord_parameter_types}; 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. +// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sp_runtime::{ Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header, }; @@ -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..5226415400ad4c3d223eb15f4aeb638217d1d693 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)] @@ -188,7 +168,7 @@ impl SessionManager for () { fn end_session(_: SessionIndex) {} } -/// Handler for session lifecycle events. +/// Handler for session life cycle events. pub trait SessionHandler { /// All the key type ids this session handler can process. /// @@ -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..38dc491b805b77e4dd1e5c92705a85b31d2243a6 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); @@ -833,7 +834,7 @@ decl_module! { Self::deposit_event(RawEvent::Founded(founder)); } - /// Anull the founding of the society. + /// Annul the founding of the society. /// /// The dispatch origin for this call must be Signed, and the signing account must be both /// the `Founder` and the `Head`. This implies that it may only be done when there is one @@ -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..84b2343fae44e295b8ee2fd5092a149e73787454 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -21,9 +21,11 @@ use super::*; use frame_support::{impl_outer_origin, parameter_types, ord_parameter_types}; 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. +// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. 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..8b10dc32e7ba0de12199d35ce580e77319090d26 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -282,7 +282,7 @@ fn slash_payout_multi_works() { } #[test] -fn suspended_member_lifecycle_works() { +fn suspended_member_life_cycle_works() { EnvBuilder::new().execute(|| { // Add 20 to members, who is not the head and can be suspended/removed. assert_ok!(Society::add_member(&20)); @@ -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..51af0a33a35f7029a1054a08d12c8e08ede09403 100644 --- a/frame/staking/reward-curve/src/lib.rs +++ b/frame/staking/reward-curve/src/lib.rs @@ -27,7 +27,7 @@ use syn::parse::{Parse, ParseStream}; /// Expressed in millionth, must be between 0_100_000 and 0_900_000. /// /// - `falloff`: Known as `decay_rate` in the literature. A co-efficient dictating the strength of -/// the global incentivisation to get the `ideal_stake`. A higher number results in less typical +/// the global incentivization to get the `ideal_stake`. A higher number results in less typical /// inflation at the cost of greater volatility for validators. /// Expressed in millionth, must be between 0 and 1_000_000. /// @@ -271,7 +271,7 @@ fn compute_points(input: &INposInput) -> Vec<(u32, u32)> { points.push((0, inpos.i_0)); points.push((inpos.x_ideal, inpos.i_ideal_times_x_ideal)); - // For each point p: (next_p.0 - p.0) < segment_lenght && (next_p.1 - p.1) < segment_lenght. + // For each point p: (next_p.0 - p.0) < segment_length && (next_p.1 - p.1) < segment_length. // This ensures that the total number of segment doesn't overflow max_piece_count. let max_length = (input.max_inflation - input.min_inflation + 1_000_000 - inpos.x_ideal) / (input.max_piece_count - 1); @@ -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..e75ac3af2bf332cc7f357807da33d386732d12f0 100644 --- a/frame/staking/src/inflation.rs +++ b/frame/staking/src/inflation.rs @@ -19,12 +19,12 @@ //! 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. /// /// Defined as such: -/// `payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokans / era_per_year` +/// `payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year` /// /// `era_duration` is expressed in millisecond. pub fn compute_total_payout( @@ -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..3f84597912f53921cd0790f72d0a3a1a6037409e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -171,7 +171,7 @@ //! //! Validators and nominators are rewarded at the end of each era. The total reward of an era is //! calculated using the era duration and the staking rate (the total amount of tokens staked by -//! nominators and validators, divided by the total token supply). It aims to incentivise toward a +//! nominators and validators, divided by the total token supply). It aims to incentivize toward a //! defined staking rate. The full specification can be found //! [here](https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model). //! @@ -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. @@ -1358,7 +1360,7 @@ impl Module { Self::new_era(session_index) } - /// Initialise the first session (and consequently the first era) + /// Initialize the first session (and consequently the first era) fn initial_session() -> Option> { // note: `CurrentEraStart` is set in `on_finalize` of the first block because now is not // available yet. @@ -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..73215d611777696087650395d4da5a9d70db9fe0 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); @@ -236,7 +235,7 @@ fn multi_era_reward_should_work() { // Compute now as other parameter won't change let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 10); // Test is meaningfull if reward something + assert!(total_payout_0 > 10); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); start_session(0); @@ -250,7 +249,7 @@ fn multi_era_reward_should_work() { start_session(4); let total_payout_1 = current_total_payout_for_duration(3000); - assert!(total_payout_1 > 10); // Test is meaningfull if reward something + assert!(total_payout_1 > 10); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 101)]); // new era is triggered here. @@ -459,7 +458,7 @@ fn nominating_and_rewards_should_work() { // the total reward for era 0 let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 100); // Test is meaningfull if reward something + assert!(total_payout_0 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(41, 1)]); >::reward_by_ids(vec![(31, 1)]); >::reward_by_ids(vec![(21, 10)]); // must be no-op @@ -507,7 +506,7 @@ fn nominating_and_rewards_should_work() { // the total reward for era 1 let total_payout_1 = current_total_payout_for_duration(3000); - assert!(total_payout_1 > 100); // Test is meaningfull if reward something + assert!(total_payout_1 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(41, 10)]); // must be no-op >::reward_by_ids(vec![(31, 10)]); // must be no-op >::reward_by_ids(vec![(21, 2)]); @@ -576,7 +575,7 @@ fn nominators_also_get_slashed() { assert_ok!(Staking::nominate(Origin::signed(2), vec![20, 10])); let total_payout = current_total_payout_for_duration(3000); - assert!(total_payout > 100); // Test is meaningfull if reward something + assert!(total_payout > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); // new era, pay rewards, @@ -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, @@ -851,7 +850,7 @@ fn reward_destination_works() { // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 100); // Test is meaningfull if reward something + assert!(total_payout_0 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); start_era(1); @@ -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, @@ -873,7 +872,7 @@ fn reward_destination_works() { // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(3000); - assert!(total_payout_1 > 100); // Test is meaningfull if reward something + assert!(total_payout_1 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); start_era(2); @@ -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,11 +895,11 @@ 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); - assert!(total_payout_2 > 100); // Test is meaningfull if reward something + assert!(total_payout_2 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); start_era(3); @@ -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); }); } @@ -950,7 +949,7 @@ fn validator_payment_prefs_work() { // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 100); // Test is meaningfull if reward something + assert!(total_payout_0 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); start_era(1); @@ -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); @@ -1394,7 +1393,7 @@ fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment( // Compute total payout now for whole duration as other parameter won't change let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 100); // Test is meaningfull if reward something + assert!(total_payout_0 > 100); // Test is meaningful if reward something >::reward_by_ids(vec![(11, 1)]); >::reward_by_ids(vec![(21, 1)]); @@ -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)); }); } @@ -1679,7 +1678,7 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() { assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default())); let total_payout_0 = current_total_payout_for_duration(3000); - assert!(total_payout_0 > 100); // Test is meaningfull if reward something + assert!(total_payout_0 > 100); // Test is meaningful if reward something reward_all_elected(); start_era(1); @@ -1689,19 +1688,19 @@ 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 + assert!(total_payout_1 > 100); // Test is meaningful if reward something reward_all_elected(); start_era(2); 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..8ee09ba223ab1a393fadb9ff41056dbac21c756b 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 @@ -91,8 +90,8 @@ use sp_runtime::{traits::{StaticLookup, Dispatchable}, DispatchError}; use frame_support::{ Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, - weights::SimpleDispatchInfo, }; +use frame_support::weights::{GetDispatchInfo, FunctionOf}; use frame_system::{self as system, ensure_signed}; pub trait Trait: frame_system::Trait { @@ -100,7 +99,7 @@ pub trait Trait: frame_system::Trait { type Event: From> + Into<::Event>; /// A sudo-able call. - type Proposal: Parameter + Dispatchable; + type Call: Parameter + Dispatchable + GetDispatchInfo; } decl_module! { @@ -118,15 +117,19 @@ decl_module! { /// - O(1). /// - Limited storage reads. /// - One DB write (event). - /// - Unknown weight of derivative `proposal` execution. + /// - Weight of derivative `call` execution + 10,000. /// # - #[weight = SimpleDispatchInfo::FreeOperational] - fn sudo(origin, proposal: Box) { + #[weight = FunctionOf( + |args: (&Box<::Call>,)| args.0.get_dispatch_info().weight + 10_000, + |args: (&Box<::Call>,)| args.0.get_dispatch_info().class, + true + )] + fn sudo(origin, call: Box<::Call>) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; ensure!(sender == Self::key(), Error::::RequireSudo); - let res = match proposal.dispatch(frame_system::RawOrigin::Root.into()) { + let res = match call.dispatch(frame_system::RawOrigin::Root.into()) { Ok(_) => true, Err(e) => { let e: DispatchError = e.into(); @@ -166,17 +169,25 @@ decl_module! { /// - O(1). /// - Limited storage reads. /// - One DB write (event). - /// - Unknown weight of derivative `proposal` execution. + /// - Weight of derivative `call` execution + 10,000. /// # - #[weight = SimpleDispatchInfo::FixedOperational(0)] - fn sudo_as(origin, who: ::Source, proposal: Box) { + #[weight = FunctionOf( + |args: (&::Source, &Box<::Call>,)| { + args.1.get_dispatch_info().weight + 10_000 + }, + |args: (&::Source, &Box<::Call>,)| { + args.1.get_dispatch_info().class + }, + true + )] + fn sudo_as(origin, who: ::Source, call: Box<::Call>) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; ensure!(sender == Self::key(), Error::::RequireSudo); let who = T::Lookup::lookup(who)?; - let res = match proposal.dispatch(frame_system::RawOrigin::Signed(who).into()) { + let res = match call.dispatch(frame_system::RawOrigin::Signed(who).into()) { Ok(_) => true, Err(e) => { let e: DispatchError = e.into(); diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index d8460ef7e8aeaff44fe0f08c9684f38ab2b93409..b20f5c73bfd99911aa74467ac055fecea1fcacb2 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" } @@ -17,7 +18,7 @@ sp-arithmetic = { version = "2.0.0", default-features = false, path = "../../pri sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } frame-support-procedural = { version = "2.0.0", path = "./procedural" } paste = "0.1.6" -once_cell = { version = "0.2.4", default-features = false, optional = true } +once_cell = { version = "1", default-features = false, optional = true } sp-state-machine = { version = "0.8", optional = true, path = "../../primitives/state-machine" } bitmask = { version = "0.5.0", default-features = false } impl-trait-for-tuples = "0.1.3" 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..942a47a533e5f894425d9eb60def32c51ed73ff6 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| { @@ -218,8 +217,8 @@ fn decl_runtime_metadata<'a>( let filtered_names: Vec<_> = module_declaration .module_parts() .into_iter() - .filter(|part| part.name != "Module") - .map(|part| part.name.clone()) + .filter(|part| part.name() != "Module") + .map(|part| part.ident()) .collect(); (module_declaration, filtered_names) }) @@ -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/construct_runtime/parse.rs b/frame/support/procedural/src/construct_runtime/parse.rs index 0559c45bbcdd7c65573eabc205abfd9ac714aaab..4a81a7efd6b77ad09cad319a88eb16e8a4589e97 100644 --- a/frame/support/procedural/src/construct_runtime/parse.rs +++ b/frame/support/procedural/src/construct_runtime/parse.rs @@ -27,6 +27,14 @@ mod keyword { syn::custom_keyword!(Block); syn::custom_keyword!(NodeBlock); syn::custom_keyword!(UncheckedExtrinsic); + syn::custom_keyword!(Module); + syn::custom_keyword!(Call); + syn::custom_keyword!(Storage); + syn::custom_keyword!(Event); + syn::custom_keyword!(Config); + syn::custom_keyword!(Origin); + syn::custom_keyword!(Inherent); + syn::custom_keyword!(ValidateUnsigned); } #[derive(Debug)] @@ -83,7 +91,7 @@ impl Parse for WhereSection { }) = definitions.first() { let msg = format!( - "`{:?}` was declared above. Please use exactly one delcataion for `{:?}`.", + "`{:?}` was declared above. Please use exactly one declaration for `{:?}`.", kind, kind ); return Err(Error::new(*kind_span, msg)); @@ -145,7 +153,7 @@ pub struct ModuleDeclaration { pub name: Ident, pub module: Ident, pub instance: Option, - pub details: Option>>, + pub module_parts: Vec, } impl Parse for ModuleDeclaration { @@ -162,160 +170,183 @@ impl Parse for ModuleDeclaration { } else { None }; - let details = if input.peek(Token![::]) { - let _: Token![::] = input.parse()?; - Some(input.parse()?) - } else { - None - }; + + let _: Token![::] = input.parse()?; + let module_parts = parse_module_parts(input)?; + let parsed = Self { name, module, instance, - details, + module_parts, }; - if let Some(ref details) = parsed.details { - let parts = &details.content.inner; - let mut resolved = HashSet::new(); - let has_default = parts.into_iter().any(|m| m.is_default()); - for entry in parts { - match entry { - ModuleEntry::Part(part) => { - if has_default && part.is_included_in_default() { - let msg = format!( - "`{}` is already included in `default`. Either remove `default` or remove `{}`", - part.name, - part.name - ); - return Err(Error::new(part.name.span(), msg)); - } - - if !resolved.insert(part.name.clone()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.name - ); - return Err(Error::new(part.name.span(), msg)); - } - } - _ => {} - } - } - } + Ok(parsed) } } impl ModuleDeclaration { - /// Get resolved module parts, i.e. after expanding `default` keyword - /// or empty declaration - pub fn module_parts(&self) -> Vec { - if let Some(ref details) = self.details { - details - .content - .inner - .iter() - .flat_map(|entry| match entry { - ModuleEntry::Default(ref token) => Self::default_modules(token.span()), - ModuleEntry::Part(ref part) => vec![part.clone()], - }) - .collect() - } else { - Self::default_modules(self.module.span()) - } + /// Get resolved module parts + pub fn module_parts(&self) -> &[ModulePart] { + &self.module_parts } - pub fn find_part(&self, name: &str) -> Option { - self.module_parts() - .into_iter() - .find(|part| part.name == name) + pub fn find_part(&self, name: &str) -> Option<&ModulePart> { + self.module_parts.iter().find(|part| part.name() == name) } pub fn exists_part(&self, name: &str) -> bool { self.find_part(name).is_some() } +} - fn default_modules(span: Span) -> Vec { - let mut res: Vec<_> = ["Module", "Call", "Storage"] - .iter() - .map(|name| ModulePart::with_name(name, span)) - .collect(); - res.extend( - ["Event", "Config"] - .iter() - .map(|name| ModulePart::with_generics(name, span)), - ); - res +/// Parse [`ModulePart`]'s from a braces enclosed list that is split by commas, e.g. +/// +/// `{ Call, Event }` +fn parse_module_parts(input: ParseStream) -> Result> { + let module_parts :ext::Braces> = input.parse()?; + + let mut resolved = HashSet::new(); + for part in module_parts.content.inner.iter() { + if !resolved.insert(part.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.name(), + ); + return Err(Error::new(part.keyword.span(), msg)); + } } + + Ok(module_parts.content.inner.into_iter().collect()) } -#[derive(Debug)] -pub enum ModuleEntry { - Default(Token![default]), - Part(ModulePart), +#[derive(Debug, Clone)] +pub enum ModulePartKeyword { + Module(keyword::Module), + Call(keyword::Call), + Storage(keyword::Storage), + Event(keyword::Event), + Config(keyword::Config), + Origin(keyword::Origin), + Inherent(keyword::Inherent), + ValidateUnsigned(keyword::ValidateUnsigned), } -impl Parse for ModuleEntry { +impl Parse for ModulePartKeyword { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); - if lookahead.peek(Token![default]) { - Ok(ModuleEntry::Default(input.parse()?)) - } else if lookahead.peek(Ident) { - Ok(ModuleEntry::Part(input.parse()?)) + + if lookahead.peek(keyword::Module) { + Ok(Self::Module(input.parse()?)) + } else if lookahead.peek(keyword::Call) { + Ok(Self::Call(input.parse()?)) + } else if lookahead.peek(keyword::Storage) { + Ok(Self::Storage(input.parse()?)) + } else if lookahead.peek(keyword::Event) { + Ok(Self::Event(input.parse()?)) + } else if lookahead.peek(keyword::Config) { + Ok(Self::Config(input.parse()?)) + } else if lookahead.peek(keyword::Origin) { + Ok(Self::Origin(input.parse()?)) + } else if lookahead.peek(keyword::Inherent) { + Ok(Self::Inherent(input.parse()?)) + } else if lookahead.peek(keyword::ValidateUnsigned) { + Ok(Self::ValidateUnsigned(input.parse()?)) } else { Err(lookahead.error()) } } } -impl ModuleEntry { - pub fn is_default(&self) -> bool { +impl ModulePartKeyword { + /// Returns the name of `Self`. + fn name(&self) -> &'static str { + match self { + Self::Module(_) => "Module", + Self::Call(_) => "Call", + Self::Storage(_) => "Storage", + Self::Event(_) => "Event", + Self::Config(_) => "Config", + Self::Origin(_) => "Origin", + Self::Inherent(_) => "Inherent", + Self::ValidateUnsigned(_) => "ValidateUnsigned", + } + } + + /// Returns the name as `Ident`. + fn ident(&self) -> Ident { + Ident::new(self.name(), self.span()) + } + + /// Returns `true` if this module part allows to have an argument. + /// + /// For example `Inherent(Timestamp)`. + fn allows_arg(&self) -> bool { + Self::all_allow_arg().iter().any(|n| *n == self.name()) + } + + /// Returns the names of all module parts that allow to have an argument. + fn all_allow_arg() -> &'static [&'static str] { + &["Inherent"] + } + + /// Returns `true` if this module part is allowed to have generic arguments. + fn allows_generic(&self) -> bool { + Self::all_generic_arg().iter().any(|n| *n == self.name()) + } + + /// Returns the names of all module parts that allow to have a generic argument. + fn all_generic_arg() -> &'static [&'static str] { + &["Event", "Origin", "Config"] + } +} + +impl Spanned for ModulePartKeyword { + fn span(&self) -> Span { match self { - ModuleEntry::Default(_) => true, - _ => false, + Self::Module(inner) => inner.span(), + Self::Call(inner) => inner.span(), + Self::Storage(inner) => inner.span(), + Self::Event(inner) => inner.span(), + Self::Config(inner) => inner.span(), + Self::Origin(inner) => inner.span(), + Self::Inherent(inner) => inner.span(), + Self::ValidateUnsigned(inner) => inner.span(), } } } #[derive(Debug, Clone)] pub struct ModulePart { - pub name: Ident, + pub keyword: ModulePartKeyword, pub generics: syn::Generics, pub args: Option>>, } impl Parse for ModulePart { fn parse(input: ParseStream) -> Result { - let name: Ident = input.parse()?; - - if !ModulePart::all_allowed().iter().any(|n| name == n) { - return Err(syn::Error::new( - name.span(), - format!( - "Only the following modules are allowed: {}", - ModulePart::format_names(ModulePart::all_allowed()), - ), - )) - } + let keyword: ModulePartKeyword = input.parse()?; let generics: syn::Generics = input.parse()?; - if !generics.params.is_empty() && !Self::is_allowed_generic(&name) { - let valid_generics = ModulePart::format_names(ModulePart::allowed_generics()); + if !generics.params.is_empty() && !keyword.allows_generic() { + let valid_generics = ModulePart::format_names(ModulePartKeyword::all_generic_arg()); let msg = format!( "`{}` is not allowed to have generics. \ Only the following modules are allowed to have generics: {}.", - name, valid_generics + keyword.name(), + valid_generics, ); - return Err(syn::Error::new(name.span(), msg)); + return Err(syn::Error::new(keyword.span(), msg)); } let args = if input.peek(token::Paren) { - if !Self::is_allowed_arg(&name) { + if !keyword.allows_arg() { let syn::group::Parens { token: parens, .. } = syn::group::parse_parens(input)?; - let valid_names = ModulePart::format_names(ModulePart::allowed_args()); + let valid_names = ModulePart::format_names(ModulePartKeyword::all_allow_arg()); let msg = format!( "`{}` is not allowed to have arguments in parens. \ Only the following modules are allowed to have arguments in parens: {}.", - name, valid_names + keyword.name(), + valid_names, ); return Err(syn::Error::new(parens.span, msg)); } @@ -325,7 +356,7 @@ impl Parse for ModulePart { }; Ok(Self { - name, + keyword, generics, args, }) @@ -333,70 +364,19 @@ impl Parse for ModulePart { } impl ModulePart { - pub fn is_allowed_generic(ident: &Ident) -> bool { - Self::allowed_generics().into_iter().any(|n| ident == n) - } - - pub fn is_allowed_arg(ident: &Ident) -> bool { - Self::allowed_args().into_iter().any(|n| ident == n) - } - - pub fn allowed_generics() -> &'static [&'static str] { - &["Event", "Origin", "Config"] - } - - pub fn allowed_args() -> &'static [&'static str] { - &["Inherent"] - } - - /// Returns all allowed names for module parts. - pub fn all_allowed() -> &'static [&'static str] { - &["Module", "Call", "Storage", "Event", "Config", "Origin", "Inherent", "ValidateUnsigned"] - } - pub fn format_names(names: &[&'static str]) -> String { let res: Vec<_> = names.into_iter().map(|s| format!("`{}`", s)).collect(); res.join(", ") } - pub fn is_included_in_default(&self) -> bool { - ["Module", "Call", "Storage", "Event", "Config"] - .iter() - .any(|name| self.name == name) - } - - /// Plain module name like `Event` or `Call`, etc. - pub fn with_name(name: &str, span: Span) -> Self { - let name = Ident::new(name, span); - Self { - name, - generics: syn::Generics { - lt_token: None, - gt_token: None, - where_clause: None, - ..Default::default() - }, - args: None, - } + /// The name of this module part. + pub fn name(&self) -> &'static str { + self.keyword.name() } - /// Module name with generic like `Event` or `Call`, etc. - pub fn with_generics(name: &str, span: Span) -> Self { - let name = Ident::new(name, span); - let typ = Ident::new("T", span); - let generic_param = syn::GenericParam::Type(typ.into()); - let generic_params = vec![generic_param].into_iter().collect(); - let generics = syn::Generics { - lt_token: Some(syn::token::Lt { spans: [span] }), - params: generic_params, - gt_token: Some(syn::token::Gt { spans: [span] }), - where_clause: None, - }; - Self { - name, - generics, - args: None, - } + /// The name of this module part as `Ident`. + pub fn ident(&self) -> Ident { + self.keyword.ident() } } diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 7c27bc2a04dcb185b9491727edcab5c8402fe4ed..8849e52c799467f3aff46220241f67651ac07c11 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 @@ -123,16 +123,15 @@ use proc_macro::TokenStream; /// And [`StoragePrefixedMap`](../frame_support/storage/trait.StoragePrefixedMap.html). /// /// `$hash1` and `$hash2` representing choices of hashing algorithms available in the -/// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be choosen with care, see +/// [`Hashable`](../frame_support/trait.Hashable.html) trait. They must be chosen 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: @@ -201,7 +200,7 @@ use proc_macro::TokenStream; /// /// ```nocompile /// construct_runtime!( -/// pub enum Runtume with ... { +/// pub enum Runtime with ... { /// ..., /// Example: example::{Module, Storage, ..., Config}, /// ..., @@ -269,8 +268,8 @@ pub fn decl_storage(input: TokenStream) -> TokenStream { /// NodeBlock = runtime::Block, /// UncheckedExtrinsic = UncheckedExtrinsic /// { -/// System: system, -/// Test: test::{default}, +/// System: system::{Module, Call, Event, Config}, +/// Test: test::{Module, Call}, /// Test2: test_with_long_module::{Module}, /// /// // Module with instances @@ -280,17 +279,12 @@ pub fn decl_storage(input: TokenStream) -> TokenStream { /// ) /// ``` /// -/// The module `System: system` will expand to `System: system::{Module, Call, Storage, Event, Config}`. -/// The identifier `System` is the name of the module and the lower case identifier `system` is the -/// name of the Rust module/crate for this Substrate module. -/// -/// The module `Test: test::{default}` will expand to -/// `Test: test::{Module, Call, Storage, Event, Config}`. -/// -/// The module `Test2: test_with_long_module::{Module}` will expand to -/// `Test2: test_with_long_module::{Module}`. +/// The identifier `System` is the name of the pallet and the lower case identifier `system` is the +/// name of the Rust module/crate for this Substrate module. The identifiers between the braces are +/// the module parts provided by the pallet. It is important to list these parts here to export +/// them correctly in the metadata or to make the pallet usable in the runtime. /// -/// We provide support for the following types in a module: +/// We provide support for the following module parts in a pallet: /// /// - `Module` /// - `Call` diff --git a/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs b/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs index 100907b2926d15d2c273a46ce5284d11f8794ca1..ae7ffa64bf5d5b5de1a390f20ce6e35aed1f6434 100644 --- a/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs +++ b/frame/support/procedural/src/storage/genesis_config/genesis_config_def.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Genesis config defintion. +//! Genesis config definition. use frame_support_procedural_tools::syn_ext as ext; use proc_macro2::TokenStream; diff --git a/frame/support/procedural/src/storage/mod.rs b/frame/support/procedural/src/storage/mod.rs index 453cb9c4678ca74122c045556f2ae970204aebcf..3345c5a123c18ef0cc7f2bbd9ee0ade0b5b9daf2 100644 --- a/frame/support/procedural/src/storage/mod.rs +++ b/frame/support/procedural/src/storage/mod.rs @@ -29,7 +29,7 @@ use frame_support_procedural_tools::{ generate_crate_access, generate_hidden_includes, syn_ext as ext }; -/// All informations contained in input of decl_storage +/// All information contained in input of decl_storage pub struct DeclStorageDef { /// Name of the module used to import hidden imports. hidden_crate: Option, @@ -219,9 +219,9 @@ pub struct StorageLineDefExt { storage_trait: proc_macro2::TokenStream, /// Full trait, for example: `storage::generator::StorageMap`. storage_generator_trait: proc_macro2::TokenStream, - /// Weither the storage is generic. + /// Whether the storage is generic. is_generic: bool, - /// Weither the storage value is an option. + /// Whether the storage value is an option. is_option: bool, } @@ -283,7 +283,7 @@ impl StorageLineDefExt { None }; - let storage_trait_trunkated = match &storage_def.storage_type { + let storage_trait_truncated = match &storage_def.storage_type { StorageLineTypeDef::Simple(_) => { quote!( StorageValue<#value_type> ) }, @@ -302,8 +302,8 @@ impl StorageLineDefExt { }, }; - let storage_trait = quote!( storage::#storage_trait_trunkated ); - let storage_generator_trait = quote!( storage::generator::#storage_trait_trunkated ); + let storage_trait = quote!( storage::#storage_trait_truncated ); + let storage_generator_trait = quote!( storage::generator::#storage_trait_truncated ); let doc_attrs = storage_def.attrs.iter() .filter_map(|a| a.parse_meta().ok()) 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..45774372325aeebc6b6a89a4aef3efdb480a658d 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()?; @@ -213,7 +180,7 @@ pub fn extract_type_option(typ: &syn::Type) -> Option { None } -/// Auxialary structure to check if a given `Ident` is contained in an ast. +/// Auxiliary structure to check if a given `Ident` is contained in an ast. struct ContainsIdent<'a> { ident: &'a Ident, result: bool, diff --git a/frame/support/src/debug.rs b/frame/support/src/debug.rs index 0316fb979725c0d90bfdf842a294f2662e5c1b7d..4b7ff6cc393366791d1225a4dbd048379a63b4d5 100644 --- a/frame/support/src/debug.rs +++ b/frame/support/src/debug.rs @@ -24,7 +24,7 @@ //! and size of the blob. Luckily there are some ways to mitigate //! this that are described below. //! -//! First component to utilize debug-printing and loggin is actually +//! First component to utilize debug-printing and logging is actually //! located in `primitives` crate: `sp_core::RuntimeDebug`. //! This custom-derive generates `core::fmt::Debug` implementation, //! just like regular `derive(Debug)`, however it does not generate @@ -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..2a3e638de30966ab11c0b19d3e83016de005ba33 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 {} @@ -818,7 +819,7 @@ macro_rules! decl_module { // TODO: this probably should be a different macro? (@call - $ingore:ident + $ignore:ident $mod_type:ident<$trait_instance:ident $(, $instance:ident)?> $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ] ) => { <$mod_type<$trait_instance $(, $instance)?>>::$fn_name( $origin $(, $param_name )* ) @@ -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/error.rs b/frame/support/src/error.rs index 34de38108f25405b22d9fc156e60006151154304..3b105e979d09affe2efe0a2a5b81cf99d474201f 100644 --- a/frame/support/src/error.rs +++ b/frame/support/src/error.rs @@ -28,7 +28,7 @@ pub use frame_metadata::{ModuleErrorMetadata, ErrorMetadata, DecodeDifferent}; /// implements `From for DispatchResult` to make the error type usable as error /// in the dispatchable functions. /// -/// It is required that the error type is registed in `decl_module!` to make the error +/// It is required that the error type is registered in `decl_module!` to make the error /// exported in the metadata. /// /// # Usage diff --git a/frame/support/src/event.rs b/frame/support/src/event.rs index 3ff6d4ab45c2daa36b8473e54aa20b380a2981ad..8b7de01159df3e1417dfdb82c08f684237442f13 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 )? ; )* } } @@ -676,7 +641,7 @@ mod tests { } decl_event!( - /// Event finish formatting on an unnamed one with trailling comma + /// Event finish formatting on an unnamed one with trailing comma pub enum Event where ::Balance, ::Origin, @@ -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/child.rs b/frame/support/src/storage/child.rs index f549ffc25fd943e093370c1fc390bcd8c2b996ed..d4d046a9d4245837702917e4cb5214d5af858d26 100644 --- a/frame/support/src/storage/child.rs +++ b/frame/support/src/storage/child.rs @@ -22,7 +22,7 @@ //! //! A **key collision free** unique id is required as parameter to avoid key collision //! between child tries. -//! This unique id management and generation responsability is delegated to pallet module. +//! This unique id management and generation responsibility is delegated to pallet module. // NOTE: could replace unhashed by having only one kind of storage (root being null storage key (storage_key can become Option<&[u8]>). use crate::sp_std::prelude::*; 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 2dee4d148d1759133bef29cc2590ba51483f02b9..d59f1a47d70b96114f34d829f134f6fc8740d8a1 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 { @@ -280,7 +394,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()) @@ -343,7 +457,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. @@ -389,9 +503,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`. @@ -509,10 +621,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. @@ -543,7 +660,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 @@ -562,16 +678,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; } @@ -594,7 +712,6 @@ pub trait LockableCurrency: Currency { id: LockIdentifier, who: &AccountId, amount: Self::Balance, - until: Self::Moment, reasons: WithdrawReasons, ); @@ -605,13 +722,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, ); @@ -622,26 +737,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); } @@ -657,7 +782,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, @@ -667,7 +792,7 @@ bitmask! { } pub trait Time { - type Moment: SimpleArithmetic + Parameter + Default + Copy; + type Moment: AtLeast32Bit + Parameter + Default + Copy; fn now() -> Self::Moment; } @@ -775,7 +900,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; @@ -815,3 +943,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..c46cca683ba934172c08a3b5437d1a7e9457d077 100644 --- a/frame/support/src/weights.rs +++ b/frame/support/src/weights.rs @@ -67,7 +67,7 @@ pub trait ClassifyDispatch { fn classify_dispatch(&self, target: T) -> DispatchClass; } -/// Means of determining the weight of a block's lifecycle hooks: on_initialize, on_finalize and +/// Means of determining the weight of a block's life cycle hooks: `on_initialize`, `on_finalize` and /// such. pub trait WeighBlock { /// Return the weight of the block's on_initialize hook. @@ -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, } } } @@ -239,32 +234,61 @@ impl SimpleDispatchInfo { /// A struct to represent a weight which is a function of the input arguments. The given items have /// the following types: /// -/// - `F`: a closure with the same argument list as the dispatched, wrapped in a tuple. -/// - `DispatchClass`: class of the dispatch. -/// - `bool`: whether this dispatch pays fee or not. -pub struct FunctionOf(pub F, pub DispatchClass, pub bool); +/// - `WD`: a raw `Weight` value or a closure that returns a `Weight` with the same +/// argument list as the dispatched, wrapped in a tuple. +/// - `CD`: a raw `DispatchClass` value or a closure that returns a `DispatchClass` +/// with the same argument list as the dispatched, wrapped in a tuple. +/// - `PF`: a `bool` for whether this dispatch pays fee or not or a closure that +/// returns a bool with the same argument list as the dispatched, wrapped in a tuple. +pub struct FunctionOf(pub WD, pub CD, pub PF); + +// `WeighData` as a raw value +impl WeighData for FunctionOf { + fn weigh_data(&self, _: Args) -> Weight { + self.0 + } +} -impl WeighData for FunctionOf -where - F : Fn(Args) -> Weight +// `WeighData` as a closure +impl WeighData for FunctionOf where + WD : Fn(Args) -> Weight { fn weigh_data(&self, args: Args) -> Weight { (self.0)(args) } } -impl ClassifyDispatch for FunctionOf { +// `ClassifyDispatch` as a raw value +impl ClassifyDispatch for FunctionOf { fn classify_dispatch(&self, _: Args) -> DispatchClass { - self.1.clone() + self.1 } } -impl PaysFee for FunctionOf { - fn pays_fee(&self, _: T) -> bool { +// `ClassifyDispatch` as a raw value +impl ClassifyDispatch for FunctionOf where + CD : Fn(Args) -> DispatchClass +{ + fn classify_dispatch(&self, args: Args) -> DispatchClass { + (self.1)(args) + } +} + +// `PaysFee` as a raw value +impl PaysFee for FunctionOf { + fn pays_fee(&self, _: Args) -> bool { self.2 } } +// `PaysFee` as a closure +impl PaysFee for FunctionOf where + PF : Fn(Args) -> bool +{ + fn pays_fee(&self, args: Args) -> bool { + (self.2)(args) + } +} /// Implementation for unchecked extrinsic. impl GetDispatchInfo 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/abundant_where_param.stderr b/frame/support/test/tests/construct_runtime_ui/abundant_where_param.stderr index 4bac57d63c8512cf4bcc7391c40e914ba2da8c56..b622adbfe65b417865827af90ab13397ddad0a85 100644 --- a/frame/support/test/tests/construct_runtime_ui/abundant_where_param.stderr +++ b/frame/support/test/tests/construct_runtime_ui/abundant_where_param.stderr @@ -1,4 +1,4 @@ -error: `Block` was declared above. Please use exactly one delcataion for `Block`. +error: `Block` was declared above. Please use exactly one declaration for `Block`. --> $DIR/abundant_where_param.rs:7:3 | 7 | Block = Block1, diff --git a/frame/support/test/tests/construct_runtime_ui/default_module_invalid_arg.rs b/frame/support/test/tests/construct_runtime_ui/default_module_invalid_arg.rs deleted file mode 100644 index 92a5ffff73f3ccf4232d59aa2adcfaa168d4871b..0000000000000000000000000000000000000000 --- a/frame/support/test/tests/construct_runtime_ui/default_module_invalid_arg.rs +++ /dev/null @@ -1,14 +0,0 @@ -use frame_support::construct_runtime; - -construct_runtime! { - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: system, - Balance: balances::{default, Error}, - } -} - -fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/default_module_invalid_arg.stderr b/frame/support/test/tests/construct_runtime_ui/default_module_invalid_arg.stderr deleted file mode 100644 index d4a46a3491027f06765d3995d1a697944894d8ef..0000000000000000000000000000000000000000 --- a/frame/support/test/tests/construct_runtime_ui/default_module_invalid_arg.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Only the following modules are allowed: `Module`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned` - --> $DIR/default_module_invalid_arg.rs:10:32 - | -10 | Balance: balances::{default, Error}, - | ^^^^^ diff --git a/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs b/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs index 0907f0bfb35d548754ef16c5e27ddb00602484e7..ec37456e58e79ed02b2df16c0b2f8b1f9a11ef56 100644 --- a/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs +++ b/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs @@ -6,7 +6,7 @@ construct_runtime! { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, + System: system::{Module}, Balance: balances::{Config, Call, Config, Origin}, } } diff --git a/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.rs b/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.rs deleted file mode 100644 index 3d61abebe8da1947b02b996bde91413bc1e5f13c..0000000000000000000000000000000000000000 --- a/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.rs +++ /dev/null @@ -1,14 +0,0 @@ -use frame_support::construct_runtime; - -construct_runtime! { - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: system, - Balance: balances::{default, Config}, - } -} - -fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.stderr b/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.stderr deleted file mode 100644 index d8205acc4a104e2f13f2a92222d760c8808e626d..0000000000000000000000000000000000000000 --- a/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `Config` is already included in `default`. Either remove `default` or remove `Config` - --> $DIR/double_module_parts_default.rs:10:32 - | -10 | Balance: balances::{default, Config}, - | ^^^^^^ diff --git a/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs b/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs index 5a9a4612ed0a476f8efd5f45c4d52705e9d99c20..b79d73ff5c022dbfc85e628873646fa685981389 100644 --- a/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs +++ b/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs @@ -6,7 +6,7 @@ construct_runtime! { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, + System: system::{Module}, Balance: balances::::{Call, Origin}, } } diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr index 4441f6dc0f6a2f923947528b231f452bbd548eb6..66c9fc95cb54624475da43fd465c73e118076a0b 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr @@ -1,4 +1,4 @@ -error: expected `default` or identifier +error: expected one of: `Module`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned` --> $DIR/invalid_module_details_keyword.rs:9:20 | 9 | system: System::{enum}, diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.rs b/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.rs index db1250cdf4d6e78b5e40fd19a466a95f0169f6d7..3754d41d6e81cdac3146270027e4fc1fec041f00 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.rs +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.rs @@ -6,7 +6,7 @@ construct_runtime! { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, + System: system::{Module}, Balance: balances::{Error}, } } diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr index da38a82d7e2b06736f6f8c44331fa214fa8996f1..7442c6be3a9a3c455aaaad61250089325728f2f6 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_entry.stderr @@ -1,4 +1,4 @@ -error: Only the following modules are allowed: `Module`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned` +error: expected one of: `Module`, `Call`, `Storage`, `Event`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned` --> $DIR/invalid_module_entry.rs:10:23 | 10 | Balance: balances::{Error}, diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr index 8a6f15f7915b260f11f3a45522be19272a85fa6b..3b967f96d7b4e36f5b46e561c5c83a0c41db2d2f 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr @@ -1,4 +1,4 @@ -error: expected `,` +error: expected `::` --> $DIR/invalid_token_after_module.rs:9:18 | 9 | system: System ? diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs b/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs index 13536338e07a679de291c795bd0bbffa35f32808..dc1dc430ed426184bb9c98b5e39b1203565c723d 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs +++ b/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs @@ -5,7 +5,7 @@ construct_runtime! { Block = Block, NodeBlock = Block, TypeX = Block, - UnchekcedExtrinsic = UnchekcedExtrinsic, + UncheckedExtrinsic = UncheckedExtrinsic, {} } diff --git a/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs b/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs index 928871fab234f319722f31d6d03e6cd6eb243f14..5eb7df5d18c20ce8390ea7f50d36792bb4a7b856 100644 --- a/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs +++ b/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs @@ -6,7 +6,7 @@ construct_runtime! { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, + System: system::{Module}, Balance: balances::::{Event}, } } diff --git a/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs b/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs index 35a5c8201ba03bcc2a6cae4aa00fd29ea30f1df3..5e44ae84d87c617683bc700def59cb6c7cab41ee 100644 --- a/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs +++ b/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs @@ -6,7 +6,7 @@ construct_runtime! { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, + System: system::{Module}, Balance: balances::::{Origin}, } } 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/construct_runtime_ui/params_in_invalid_module.rs b/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.rs index a739277d620998aac769af1d16e73d1a6e9b2daf..9c752a2f39e2ad70f3acb2cd976abfcedf6455b8 100644 --- a/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.rs +++ b/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.rs @@ -6,7 +6,7 @@ construct_runtime! { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system, + System: system::{Module}, Balance: balances::::{Call(toto), Origin}, } } 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..6fa2806dd348379008ddb2ea5f8ef7b91cd2d3c7 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 }, @@ -300,7 +300,7 @@ fn new_test_ext() -> sp_io::TestExternalities { } #[test] -fn storage_instance_independance() { +fn storage_instance_independence() { let mut storage = sp_core::storage::Storage { top: std::collections::BTreeMap::new(), children: std::collections::HashMap::new() @@ -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 b271b0e94a3cead2b0f0b1d6ee957d730b59d875..25f1b8b6abafc2b086a7cfe9eb37bb956b09e83c 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,31 +292,48 @@ 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. /// /// Put in on_initialize and removed in on_finalize, thus returns Default in between /// blocks. 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; @@ -474,7 +352,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; @@ -497,6 +375,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>, @@ -620,7 +642,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. @@ -701,7 +723,7 @@ impl Module { /// Even more dangerous is to note that this function does NOT take any action, if the new sum /// of block weight is more than the block weight limit. This is what the _unchecked_. /// - /// Another potential use-case could be for the `on_initialise` and `on_finalize` hooks. + /// Another potential use-case could be for the `on_initialize` and `on_finalize` hooks. /// /// If no previous weight exists, the function initializes the weight to zero. pub fn register_extra_weight_unchecked(weight: Weight) { @@ -830,9 +852,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 @@ -849,8 +876,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) + }, } ); @@ -874,12 +904,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), } } @@ -893,7 +1029,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(), } } @@ -955,6 +1091,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(()) } @@ -1035,6 +1172,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(()) } @@ -1045,7 +1183,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 { @@ -1055,8 +1193,7 @@ impl SignedExtension for CheckNonce { }.into() ) } - - >::insert(who, expected + T::Index::one()); + Account::::insert(who, (expected + T::Index::one(), extra)); Ok(()) } @@ -1068,7 +1205,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() } @@ -1090,6 +1227,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)); @@ -1119,6 +1262,7 @@ impl SignedExtension for CheckEra { type AdditionalSigned = T::Hash; type DispatchInfo = DispatchInfo; type Pre = (); + const IDENTIFIER: &'static str = "CheckEra"; fn validate( &self, @@ -1138,7 +1282,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)) @@ -1175,6 +1319,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())) @@ -1210,6 +1355,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) @@ -1278,13 +1424,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, } } } @@ -1464,7 +1615,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 @@ -1691,6 +1842,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..507092f626dcfdb5e77f52a344a77d6c5508cbb7 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. /// @@ -68,7 +68,7 @@ pub trait Signer { /// A `Signer` implementation for any `AppPublic` type. /// -/// This implementation additionaly supports conversion to/from multi-signature/multi-signer +/// This implementation additionally supports conversion to/from multi-signature/multi-signer /// wrappers. /// If the wrapped crypto doesn't match `AppPublic`s crypto `None` is returned. impl Signer for TAnyAppPublic where @@ -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 d5b9b85db12334e2856a793814af8f6cc4e6b11e..099a73d21c836e46db4962fd48df763a59513041 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -88,7 +88,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); } } @@ -187,9 +187,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); - - final_fee + base_fee.saturating_add(adjusted_fee) } else { 0u32.into() } @@ -210,6 +208,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 = (); @@ -342,28 +341,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); @@ -468,15 +462,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() } @@ -501,14 +498,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); }); } @@ -543,11 +540,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, @@ -559,7 +556,7 @@ mod tests { .is_ok() ); - // like a FreeNormal + // like a InsecureFreeNormal let free_transaction = DispatchInfo { weight: 0, class: DispatchClass::Normal, @@ -590,7 +587,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..192dd1aff61163d5ab3ba9074f95c9a378b5a507 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -38,7 +38,7 @@ //! should be paid. //! //! A group of `Tippers` is determined through the config `Trait`. After half of these have declared -//! some amount that they believe a particular reported reason deserves, then a countfown period is +//! some amount that they believe a particular reported reason deserves, then a countdown period is //! entered where any remaining members can declare their tip amounts also. After the close of the //! countdown period, the median of all declared tips is paid to the reported beneficiary, along //! with any finders fee, in case of a public (and bonded) original report. @@ -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); }); } @@ -1055,14 +1052,14 @@ mod tests { } #[test] - fn reject_non_existant_spend_proposal_fails() { + fn reject_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex); }); } #[test] - fn accept_non_existant_spend_proposal_fails() { + fn accept_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex); }); @@ -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,24 +1130,23 @@ 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 }); } // In case treasury account is not existing then it works fine. - // This is usefull for chain that will just update runtime. + // This is useful for chain that will just update runtime. #[test] - fn inexisting_account_works() { + fn inexistent_account_works() { 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..501e04ad55878ca0070b2223f63b85f09c8a1fbe 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -66,9 +66,9 @@ use codec::{Encode, Decode}; use sp_core::TypeId; use sp_io::hashing::blake2_256; use frame_support::{decl_module, decl_event, decl_error, decl_storage, Parameter, ensure, RuntimeDebug}; -use frame_support::{traits::{Get, ReservableCurrency, Currency}, weights::{ - GetDispatchInfo, ClassifyDispatch, WeighData, Weight, DispatchClass, PaysFee -}}; +use frame_support::{traits::{Get, ReservableCurrency, Currency}, + weights::{GetDispatchInfo, DispatchClass,FunctionOf}, +}; use frame_system::{self as system, ensure_signed}; use sp_runtime::{DispatchError, DispatchResult, traits::Dispatchable}; @@ -105,7 +105,7 @@ pub trait Trait: frame_system::Trait { /// composite was created to be uniquely identified. #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Default, RuntimeDebug)] pub struct Timepoint { - /// The hieght of the chain at the point in time. + /// The height of the chain at the point in time. height: BlockNumber, /// The index of the extrinsic at the point in time. index: u32, @@ -188,126 +188,6 @@ decl_event! { } } -/// Simple index-based pass through for the weight functions. -struct Passthrough(sp_std::marker::PhantomData); - -impl Passthrough { - fn new() -> Self { Self(Default::default()) } -} -impl WeighData<(&u16, &Box)> for Passthrough { - fn weigh_data(&self, (_, call): (&u16, &Box)) -> Weight { - call.get_dispatch_info().weight + 10_000 - } -} -impl ClassifyDispatch<(&u16, &Box)> for Passthrough { - fn classify_dispatch(&self, (_, call): (&u16, &Box)) -> DispatchClass { - call.get_dispatch_info().class - } -} -impl PaysFee<(&u16, &Box)> for Passthrough { - fn pays_fee(&self, (_, call): (&u16, &Box)) -> bool { - call.get_dispatch_info().pays_fee - } -} - -/// Sumation pass-through for the weight function of the batch call. -/// -/// This just adds all of the weights together of all of the calls. -struct BatchPassthrough(sp_std::marker::PhantomData); - -impl BatchPassthrough { - fn new() -> Self { Self(Default::default()) } -} -impl WeighData<(&Vec,)> for BatchPassthrough { - fn weigh_data(&self, (calls,): (&Vec,)) -> Weight { - calls.iter() - .map(|call| call.get_dispatch_info().weight) - .fold(10_000, |a, n| a + n) - } -} -impl ClassifyDispatch<(&Vec,)> for BatchPassthrough { - fn classify_dispatch(&self, (calls,): (&Vec,)) -> DispatchClass { - let all_operational = calls.iter() - .map(|call| call.get_dispatch_info().class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - } -} -impl PaysFee<(&Vec,)> for BatchPassthrough { - fn pays_fee(&self, (calls,): (&Vec,)) -> bool { - calls.iter() - .any(|call| call.get_dispatch_info().pays_fee) - } -} - -/// Simple index-based pass through for the weight functions. -struct MultiPassthrough( - sp_std::marker::PhantomData<(Call, AccountId, Timepoint)> -); - -impl MultiPassthrough { - fn new() -> Self { Self(Default::default()) } -} -impl WeighData<(&u16, &Vec, &Timepoint, &Box)> -for MultiPassthrough -{ - fn weigh_data(&self, (_, sigs, _, call): (&u16, &Vec, &Timepoint, &Box)) -> Weight { - call.get_dispatch_info().weight + 10_000 * (sigs.len() as u32 + 1) - } -} -impl ClassifyDispatch<(&u16, &Vec, &Timepoint, &Box)> -for MultiPassthrough -{ - fn classify_dispatch(&self, (_, _, _, call): (&u16, &Vec, &Timepoint, &Box)) - -> DispatchClass - { - call.get_dispatch_info().class - } -} -impl PaysFee<(&u16, &Vec, &Timepoint, &Box)> -for MultiPassthrough -{ - fn pays_fee(&self, _: (&u16, &Vec, &Timepoint, &Box)) -> bool { - true - } -} - -/// Simple index-based pass through for the weight functions. -struct SigsLen( - sp_std::marker::PhantomData<(AccountId, Timepoint)> -); - -impl SigsLen { - fn new() -> Self { Self(Default::default()) } -} -impl WeighData<(&u16, &Vec, &Timepoint, &[u8; 32])> -for SigsLen -{ - fn weigh_data(&self, (_, sigs, _, _): (&u16, &Vec, &Timepoint, &[u8; 32])) -> Weight { - 10_000 * (sigs.len() as u32 + 1) - } -} -impl ClassifyDispatch<(&u16, &Vec, &Timepoint, &[u8; 32])> -for SigsLen -{ - fn classify_dispatch(&self, _: (&u16, &Vec, &Timepoint, &[u8; 32])) - -> DispatchClass - { - DispatchClass::Normal - } -} -impl PaysFee<(&u16, &Vec, &Timepoint, &[u8; 32])> -for SigsLen -{ - fn pays_fee(&self, _: (&u16, &Vec, &Timepoint, &[u8; 32])) -> bool { - true - } -} - /// A module identifier. These are per module and should be stored in a registry somewhere. #[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)] struct IndexedUtilityModuleId(u16); @@ -318,6 +198,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; @@ -339,7 +221,24 @@ decl_module! { /// `BatchInterrupted` event is deposited, along with the number of successful calls made /// and the error of the failed call. If all were successful, then the `BatchCompleted` /// event is deposited. - #[weight = ::Call>>::new()] + #[weight = FunctionOf( + |args: (&Vec<::Call>,)| { + args.0.iter() + .map(|call| call.get_dispatch_info().weight) + .fold(10_000, |a, n| a + n) + }, + |args: (&Vec<::Call>,)| { + let all_operational = args.0.iter() + .map(|call| call.get_dispatch_info().class) + .all(|class| class == DispatchClass::Operational); + if all_operational { + DispatchClass::Operational + } else { + DispatchClass::Normal + } + }, + true + )] fn batch(origin, calls: Vec<::Call>) { for (index, call) in calls.into_iter().enumerate() { let result = call.dispatch(origin.clone()); @@ -356,9 +255,13 @@ decl_module! { /// The dispatch origin for this call must be _Signed_. /// /// # - /// - The weight of the `call`. + /// - The weight of the `call` + 10,000. /// # - #[weight = ::Call>>::new()] + #[weight = FunctionOf( + |args: (&u16, &Box<::Call>)| args.1.get_dispatch_info().weight + 10_000, + |args: (&u16, &Box<::Call>)| args.1.get_dispatch_info().class, + true + )] fn as_sub(origin, index: u16, call: Box<::Call>) -> DispatchResult { let who = ensure_signed(origin)?; let pseudonym = Self::sub_account_id(who, index); @@ -406,7 +309,15 @@ decl_module! { /// deposit taken for its lifetime of /// `MultisigDepositBase + threshold * MultisigDepositFactor`. /// # - #[weight = ::Call, T::AccountId, Option>>>::new()] + #[weight = FunctionOf( + |args: (&u16, &Vec, &Option>, &Box<::Call>)| { + args.3.get_dispatch_info().weight + 10_000 * (args.1.len() as u32 + 1) + }, + |args: (&u16, &Vec, &Option>, &Box<::Call>)| { + args.3.get_dispatch_info().class + }, + true + )] fn as_multi(origin, threshold: u16, other_signatories: Vec, @@ -496,7 +407,13 @@ decl_module! { /// deposit taken for its lifetime of /// `MultisigDepositBase + threshold * MultisigDepositFactor`. /// # - #[weight = >>>::new()] + #[weight = FunctionOf( + |args: (&u16, &Vec, &Option>, &[u8; 32])| { + 10_000 * (args.1.len() as u32 + 1) + }, + DispatchClass::Normal, + true + )] fn approve_as_multi(origin, threshold: u16, other_signatories: Vec, @@ -565,7 +482,13 @@ decl_module! { /// - I/O: 1 read `O(S)`, one remove. /// - Storage: removes one item. /// # - #[weight = >>::new()] + #[weight = FunctionOf( + |args: (&u16, &Vec, &Timepoint, &[u8; 32])| { + 10_000 * (args.1.len() as u32 + 1) + }, + DispatchClass::Normal, + true + )] fn cancel_as_multi(origin, threshold: u16, other_signatories: Vec, @@ -659,6 +582,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for Test { + system, pallet_balances, utility, } @@ -698,23 +622,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 +660,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..0d15ed11f74e0ba6fb22d1d3dde0b35e3e04f2ad --- /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 allocator 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 chosen 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..b7c9ccaa9821aa49b9f14e70f841e05e1ef9853f 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) + } } } } @@ -432,3 +436,30 @@ macro_rules! wrap { } } } + +/// Generate the given code if the pair type is available. +/// +/// The pair type is available when `feature = "std"` || `feature = "full_crypto"`. +/// +/// # Example +/// +/// ``` +/// sp_application_crypto::with_pair! { +/// pub type Pair = (); +/// } +/// ``` +#[macro_export] +#[cfg(any(feature = "std", feature = "full_crypto"))] +macro_rules! with_pair { + ( $( $def:tt )* ) => { + $( $def )* + } +} + + +#[doc(hidden)] +#[macro_export] +#[cfg(all(not(feature = "std"), not(feature = "full_crypto")))] +macro_rules! with_pair { + ( $( $def:tt )* ) => {} +} 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/fuzzer/src/biguint.rs b/primitives/arithmetic/fuzzer/src/biguint.rs index 1e2ec2a502b56d21a87c8181128a44ad9a639119..f217b080d25fa80c5c2199efb1440add203e9f06 100644 --- a/primitives/arithmetic/fuzzer/src/biguint.rs +++ b/primitives/arithmetic/fuzzer/src/biguint.rs @@ -22,7 +22,7 @@ //! Once a panic is found, it can be debugged with //! `cargo hfuzz run-debug biguint hfuzz_workspace/biguint/*.fuzz`. //! -//! # More infomation +//! # More information //! More information about `honggfuzz` can be found //! [here](https://docs.rs/honggfuzz/). diff --git a/primitives/arithmetic/fuzzer/src/rational128.rs b/primitives/arithmetic/fuzzer/src/rational128.rs index 60aa315c18aeea9dd4a69f2af9b1ff7662613ccd..586a165272244bc0ab8ee7a7ec5c5813de9b0a0e 100644 --- a/primitives/arithmetic/fuzzer/src/rational128.rs +++ b/primitives/arithmetic/fuzzer/src/rational128.rs @@ -22,7 +22,7 @@ //! Once a panic is found, it can be debugged with //! `cargo hfuzz run-debug rational128 hfuzz_workspace/rational128/*.fuzz`. //! -//! # More infomation +//! # More information //! More information about `honggfuzz` can be found //! [here](https://docs.rs/honggfuzz/). 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/authority-discovery/src/lib.rs b/primitives/authority-discovery/src/lib.rs index fc76da61afa539cdc65db428a5f0cf942309c9ec..8926825525976f9d284bc1b6969ddfde936aff99 100644 --- a/primitives/authority-discovery/src/lib.rs +++ b/primitives/authority-discovery/src/lib.rs @@ -25,9 +25,10 @@ mod app { app_crypto!(sr25519, AUTHORITY_DISCOVERY); } -/// An authority discovery authority keypair. -#[cfg(feature = "std")] -pub type AuthorityPair = app::Pair; +sp_application_crypto::with_pair! { + /// An authority discovery authority keypair. + pub type AuthorityPair = app::Pair; +} /// An authority discovery authority identifier. pub type AuthorityId = app::Public; 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/block-builder/src/lib.rs b/primitives/block-builder/src/lib.rs index d963fb7e3067e68b7ccefc656e94194cd086cdb9..e4e98e1f40cbf4979f6c04113e520fcb308ed96f 100644 --- a/primitives/block-builder/src/lib.rs +++ b/primitives/block-builder/src/lib.rs @@ -26,7 +26,7 @@ use sp_inherents::{InherentData, CheckInherentsResult}; /// /// These definitions are taken from the 2c58e30246a029b53d51e5b24c31974ac539ee8b git revision. #[deprecated(note = "These definitions here are only for compatibility reasons")] -pub mod compatability_v3 { +pub mod compatibility_v3 { use sp_runtime::{DispatchOutcome, transaction_validity}; use codec::{Encode, Decode}; @@ -43,7 +43,7 @@ pub mod compatability_v3 { sp_api::decl_runtime_apis! { /// The `BlockBuilder` api trait that provides the required functionality for building a block. - #[api_version(4)] + #[api_version(5)] pub trait BlockBuilder { /// Compatibility version of `apply_extrinsic` for v3. /// @@ -51,13 +51,17 @@ sp_api::decl_runtime_apis! { #[changed_in(4)] #[allow(deprecated)] fn apply_extrinsic(extrinsic: ::Extrinsic) - -> self::compatability_v3::ApplyResult; + -> self::compatibility_v3::ApplyResult; /// Apply the given extrinsic. /// /// Returns an inclusion outcome which specifies if this extrinsic is included in /// this block or not. fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult; + /// Apply the given extrinsic. + /// + /// Same as `apply_extrinsic`, but skips signature verification. + fn apply_trusted_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult; /// Finish the current block. #[renamed("finalise_block", 3)] fn finalize_block() -> ::Header; 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..ece20d1cf8586d5500195333fd38a5c71a502b0f 100644 --- a/primitives/blockchain/src/error.rs +++ b/primitives/blockchain/src/error.rs @@ -20,7 +20,7 @@ use std::{self, error, result}; use sp_state_machine; use sp_runtime::transaction_validity::TransactionValidityError; #[allow(deprecated)] -use sp_block_builder::compatability_v3; +use sp_block_builder::compatibility_v3; use sp_consensus; use derive_more::{Display, From}; use codec::Error as CodecError; @@ -37,7 +37,7 @@ pub enum ApplyExtrinsicFailed { /// unappliable onto the current block. #[display(fmt = "Extrinsic is not valid: {:?}", _0)] Validity(TransactionValidityError), - /// This is used for miscelanious errors that can be represented by string and not handleable. + /// This is used for miscellaneous errors that can be represented by string and not handleable. /// /// This will become obsolete with complete migration to v4 APIs. #[display(fmt = "Extrinsic failed: {:?}", _0)] @@ -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), @@ -148,9 +151,9 @@ impl<'a> From<&'a str> for Error { } #[allow(deprecated)] -impl From for ApplyExtrinsicFailed { - fn from(e: compatability_v3::ApplyError) -> Self { - use self::compatability_v3::ApplyError::*; +impl From for ApplyExtrinsicFailed { + fn from(e: compatibility_v3::ApplyError) -> Self { + use self::compatibility_v3::ApplyError::*; match e { Validity(tx_validity) => Self::Validity(tx_validity), e => Self::Msg(format!("Apply extrinsic failed: {:?}", e)), diff --git a/primitives/blockchain/src/header_metadata.rs b/primitives/blockchain/src/header_metadata.rs index fcd8062d1d6d577a304c840f7d31fa69c245d2cd..32dd0bcf06e27ef94f2c6abdf7be3494550e9066 100644 --- a/primitives/blockchain/src/header_metadata.rs +++ b/primitives/blockchain/src/header_metadata.rs @@ -151,7 +151,7 @@ pub fn tree_route>( } /// Hash and number of a block. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HashAndNumber { /// The number of the block. pub number: NumberFor, diff --git a/primitives/blockchain/src/lib.rs b/primitives/blockchain/src/lib.rs index 21c5bc99d20cfade93b775245ce2308c9754b9a9..8f83c7aec5d9d13cc18e929a48bd224c43a2ce46 100644 --- a/primitives/blockchain/src/lib.rs +++ b/primitives/blockchain/src/lib.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Substrate blockchain traits and primtives +//! Substrate blockchain traits and primitives. mod backend; mod header_metadata; 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/aura/src/lib.rs b/primitives/consensus/aura/src/lib.rs index 4f2045666793aafed9b692f122e6e10fdaca34d4..2dda5b28bf825e7716578298ac9340013f1dd087 100644 --- a/primitives/consensus/aura/src/lib.rs +++ b/primitives/consensus/aura/src/lib.rs @@ -30,9 +30,10 @@ pub mod sr25519 { app_crypto!(sr25519, AURA); } - /// An Aura authority keypair using S/R 25519 as its crypto. - #[cfg(feature = "std")] - pub type AuthorityPair = app_sr25519::Pair; + sp_application_crypto::with_pair! { + /// An Aura authority keypair using S/R 25519 as its crypto. + pub type AuthorityPair = app_sr25519::Pair; + } /// An Aura authority signature using S/R 25519 as its crypto. pub type AuthoritySignature = app_sr25519::Signature; @@ -47,9 +48,10 @@ pub mod ed25519 { app_crypto!(ed25519, AURA); } - /// An Aura authority keypair using Ed25519 as its crypto. - #[cfg(feature = "std")] - pub type AuthorityPair = app_ed25519::Pair; + sp_application_crypto::with_pair! { + /// An Aura authority keypair using Ed25519 as its crypto. + pub type AuthorityPair = app_ed25519::Pair; + } /// An Aura authority signature using Ed25519 as its crypto. pub type AuthoritySignature = app_ed25519::Signature; 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..392dcb560bb008202004015ea971dfeddca8b1b4 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 { @@ -173,5 +139,8 @@ sp_api::decl_runtime_apis! { /// /// Dynamic configuration may be supported in the future. fn configuration() -> BabeConfiguration; + + /// Returns the slot number that started the current epoch. + fn current_epoch_start() -> SlotNumber; } } diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 3ae79caa4312625f54faf13829d6c639a2fcc142..d7c33a6452c3bc94863e01d523a18494efee29c9 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.16.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..3000477ded527836b32f7382522fa8faab499fd5 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, @@ -112,6 +113,7 @@ pub struct BlockCheckParams { } /// Data required to import a Block. +#[non_exhaustive] pub struct BlockImportParams { /// Origin of the Block pub origin: BlockOrigin, @@ -142,57 +144,66 @@ 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. pub import_existing: bool, + /// Cached full header hash (with post-digests applied). + pub post_hash: Option, } impl BlockImportParams { - /// Deconstruct the justified header into parts. - pub fn into_inner(self) - -> ( - BlockOrigin, - ::Header, - Option, - Vec>, - Option>, - Option, NumberFor>>, - bool, - Vec<(Vec, Option>)>, - ) { - ( - self.origin, - self.header, - self.justification, - self.post_digests, - self.body, - self.storage_changes, - self.finalized, - self.auxiliary, - ) + /// Create a new block import params. + pub fn new( + origin: BlockOrigin, + header: Block::Header, + ) -> Self { + Self { + origin, header, + justification: None, + post_digests: Vec::new(), + body: None, + storage_changes: None, + finalized: false, + intermediates: HashMap::new(), + auxiliary: Vec::new(), + fork_choice: None, + allow_missing_state: false, + import_existing: false, + post_hash: None, + } } - /// Get a handle to full header (with post-digests applied). - pub fn post_header(&self) -> Cow { - if self.post_digests.is_empty() { - Cow::Borrowed(&self.header) + /// Get the full header hash (with post-digests applied). + pub fn post_hash(&self) -> Block::Hash { + if let Some(hash) = self.post_hash { + hash } else { - Cow::Owned({ + if self.post_digests.is_empty() { + self.header.hash() + } else { let mut hdr = self.header.clone(); for digest_item in &self.post_digests { hdr.digest_mut().push(digest_item.clone()); } - hdr - }) + hdr.hash() + } } } @@ -210,11 +221,42 @@ 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, + post_hash: self.post_hash, + } + } + + /// Take intermediate 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..d7e396223a2c53611856642631f363025107f486 100644 --- a/primitives/consensus/common/src/error.rs +++ b/primitives/consensus/common/src/error.rs @@ -31,7 +31,13 @@ pub enum Error { /// I/O terminated unexpectedly #[display(fmt="I/O terminated unexpectedly.")] IoTerminated, - /// Unable to schedule wakeup. + /// Intermediate missing. + #[display(fmt="Missing intermediate.")] + NoIntermediate, + /// Intermediate is of wrong type. + #[display(fmt="Invalid intermediate.")] + InvalidIntermediate, + /// Unable to schedule wake-up. #[display(fmt="Timer error: {}", _0)] FaultyTimer(std::io::Error), /// Error while working with inherent data. diff --git a/primitives/consensus/common/src/import_queue/basic_queue.rs b/primitives/consensus/common/src/import_queue/basic_queue.rs index 4dcb7eeb45047b64bb15013325086ea0bf479519..0d1aed7fb1c72e4225fab3c48cba9faade48c20c 100644 --- a/primitives/consensus/common/src/import_queue/basic_queue.rs +++ b/primitives/consensus/common/src/import_queue/basic_queue.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::{mem, pin::Pin, time::Duration, marker::PhantomData}; +use std::{mem, pin::Pin, time::Duration, marker::PhantomData, sync::Arc}; use futures::{prelude::*, channel::mpsc, task::Context, task::Poll}; use futures_timer::Delay; +use parking_lot::{Mutex, Condvar}; use sp_runtime::{Justification, traits::{Block as BlockT, Header as HeaderT, NumberFor}}; use crate::block_import::BlockOrigin; @@ -28,7 +29,7 @@ use crate::import_queue::{ }; /// Interface to a basic block import queue that is importing blocks sequentially in a separate -/// task, with pluggable verification. +/// task, with plugable verification. pub struct BasicQueue { /// Channel to send messages to the background task. sender: mpsc::UnboundedSender>, @@ -40,9 +41,28 @@ pub struct BasicQueue { manual_poll: Option + Send>>>, /// A thread pool where the background worker is being run. pool: Option, + pool_guard: Arc<(Mutex, Condvar)>, _phantom: PhantomData, } +impl Drop for BasicQueue { + fn drop(&mut self) { + self.pool = None; + // Flush the queue and close the receiver to terminate the future. + self.sender.close_channel(); + self.result_port.close(); + + // Make sure all pool threads terminate. + // https://github.com/rust-lang/futures-rs/issues/1470 + // https://github.com/rust-lang/futures-rs/issues/1349 + let (ref mutex, ref condvar) = *self.pool_guard; + let mut lock = mutex.lock(); + while *lock != 0 { + condvar.wait(&mut lock); + } + } +} + impl BasicQueue { /// Instantiate a new basic queue, with given verifier. /// @@ -63,15 +83,28 @@ impl BasicQueue { finality_proof_import, ); + let guard = Arc::new((Mutex::new(0usize), Condvar::new())); + let guard_start = guard.clone(); + let guard_end = guard.clone(); + let mut pool = futures::executor::ThreadPool::builder() .name_prefix("import-queue-worker-") .pool_size(1) + .after_start(move |_| *guard_start.0.lock() += 1) + .before_stop(move |_| { + let (ref mutex, ref condvar) = *guard_end; + let mut lock = mutex.lock(); + *lock -= 1; + if *lock == 0 { + condvar.notify_one(); + } + }) .create() .ok(); 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>); @@ -82,6 +115,7 @@ impl BasicQueue { result_port, manual_poll, pool, + pool_guard: guard, _phantom: PhantomData, } } diff --git a/primitives/consensus/common/src/import_queue/buffered_link.rs b/primitives/consensus/common/src/import_queue/buffered_link.rs index 143ab0eef8090e9200c5837087e24943e168196a..d0f6c87951354f602f5400fa28b95a87673cbef7 100644 --- a/primitives/consensus/common/src/import_queue/buffered_link.rs +++ b/primitives/consensus/common/src/import_queue/buffered_link.rs @@ -157,6 +157,11 @@ impl BufferedLinkReceiver { } } } + + /// Close the channel. + pub fn close(&mut self) { + self.rx.close() + } } #[cfg(test)] diff --git a/primitives/consensus/common/src/select_chain.rs b/primitives/consensus/common/src/select_chain.rs index 3ce2c6ccd0ec9e7d6db0d9e137a4a523167a8b98..d94511c11027acc4c93ee7dc9d54279bdc4a8a3b 100644 --- a/primitives/consensus/common/src/select_chain.rs +++ b/primitives/consensus/common/src/select_chain.rs @@ -22,16 +22,16 @@ use sp_runtime::traits::{Block as BlockT, NumberFor}; /// if multiple forks are present for an opaque definition of "best" in the /// specific chain build. /// -/// The Strategy can be customised for the two use cases of authoring new blocks +/// The Strategy can be customized for the two use cases of authoring new blocks /// upon the best chain or which fork to finalise. Unless implemented differently -/// by default finalisation methods fall back to use authoring, so as a minimum +/// by default finalization methods fall back to use authoring, so as a minimum /// `_authoring`-functions must be implemented. /// /// Any particular user must make explicit, however, whether they intend to finalise /// or author through the using the right function call, as these might differ in /// some implementations. /// -/// Non-deterministicly finalising chains may only use the `_authoring` functions. +/// Non-deterministically finalizing chains may only use the `_authoring` functions. pub trait SelectChain: Sync + Send + Clone { /// Get all leaves of the chain: block hashes that have no children currently. 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..0d28fe139a924c090267e2a1a2839605a5152935 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,24 +12,25 @@ 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"] } -impl-serde = { version = "0.2.3", optional = true } +primitive-types = { version = "0.6.2", default-features = false, features = ["codec"] } +impl-serde = { version = "0.3.0", optional = true } wasmi = { version = "0.6.2", optional = true } hash-db = { version = "0.15.2", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } base58 = { version = "0.1.0", optional = true } rand = { version = "0.7.2", optional = true } substrate-bip39 = { version = "0.3.1", optional = true } -tiny-bip39 = { version = "0.6.2", optional = true } +tiny-bip39 = { version = "0.7", optional = true } 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..3833c8a912cece644561116d4adfae87c2d1f007 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() } @@ -880,7 +882,7 @@ pub trait CryptoType { /// An identifier for a type of cryptographic key. /// -/// To avoid clashes with other modules when distributing your module publically, register your +/// To avoid clashes with other modules when distributing your module publicly, register your /// `KeyTypeId` on the list here by making a PR. /// /// Values whose first character is `_` are reserved for private use and won't conflict with any diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index f3aba47c204578ef0b5d3e1fb93d5c5fc219e66b..5e04dcceffb395fe5433bfcb28a217f8c44f06b5 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -433,7 +433,7 @@ impl TraitPair for Pair { /// Make a new key pair from secret seed material. /// - /// You should never need to use this; generate(), generate_with_phrasee + /// You should never need to use this; generate(), generate_with_phrase fn from_seed(seed: &Seed) -> Pair { Self::from_seed_slice(&seed[..]).expect("seed has valid length; qed") } diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index ee768ee4a6226b338fec1b958a40c6d20bbf09e4..01a96d8853afd9e3f81c7ce72eb29af85d801945 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}; @@ -136,6 +133,15 @@ impl Deref for Bytes { fn deref(&self) -> &[u8] { &self.0[..] } } +#[cfg(feature = "std")] +impl sp_std::str::FromStr for Bytes { + type Err = bytes::FromHexError; + + fn from_str(s: &str) -> Result { + bytes::from_hex(s).map(Bytes) + } +} + /// Stores the encoded `RuntimeMetadata` for the native side as opaque type. #[derive(Encode, Decode, PartialEq)] pub struct OpaqueMetadata(Vec); @@ -204,7 +210,7 @@ impl PartialEq for NativeOrEncoded { } /// A value that is never in a native representation. -/// This is type is useful in conjuction with `NativeOrEncoded`. +/// This is type is useful in conjunction with `NativeOrEncoded`. #[cfg(feature = "std")] #[derive(PartialEq)] pub enum NeverNativeValue {} @@ -309,3 +315,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..c393b0f9f88821e8c9f58c9e93b3d6544ba4c7e3 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 @@ -134,7 +135,7 @@ pub enum HttpRequestStatus { /// Note the deadline is controlled by the calling part, it not necessarily /// means that the request has timed out. DeadlineReached, - /// An error has occured during the request, for example a timeout or the + /// An error has occurred during the request, for example a timeout or the /// remote has closed our socket. /// /// The request is now considered destroyed. To retry the request you need @@ -225,7 +226,7 @@ impl Duration { } impl Timestamp { - /// Creates new `Timestamp` given unix timestamp in miliseconds. + /// Creates new `Timestamp` given unix timestamp in milliseconds. pub fn from_unix_millis(millis: u64) -> Self { Timestamp(millis) } @@ -334,7 +335,7 @@ pub trait Externalities: Send { /// Returns a random seed. /// - /// This is a trully random non deterministic seed generated by host environment. + /// This is a truly random non deterministic seed generated by host environment. /// Obviously fine in the off-chain worker context. fn random_seed(&mut self) -> [u8; 32]; 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..247323f9703b41ecf5a22897da4e4b0c6de1fb12 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 @@ -241,7 +245,7 @@ mod tests { let public = store.write() .ed25519_generate_new(ED25519, None) - .expect("Genrates key"); + .expect("Generates key"); let store_key_pair = store.read() .ed25519_key_pair(ED25519, &public) 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..431e449846c6efcf32ad5eead1d0f16111360e72 100644 --- a/primitives/externalities/src/lib.rs +++ b/primitives/externalities/src/lib.rs @@ -196,12 +196,20 @@ pub trait Externalities: ExtensionStore { ) -> Vec; /// Get the change trie root of the current storage overlay at a block with given parent. - /// `parent` is expects a SCALE endcoded hash. + /// `parent` is expects a SCALE encoded hash. /// /// The hash is defined by the `Block`. /// /// 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..6356ec82e532a8c902a89319f434596e62abbbe7 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/finality-grandpa/Cargo.toml @@ -3,9 +3,10 @@ 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" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0", default-features = false, path = "../std" } serde = { version = "1.0.101", optional = true, features = ["derive"] } @@ -15,7 +16,7 @@ sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" [features] default = ["std"] std = [ - "app-crypto/std", + "sp-application-crypto/std", "codec/std", "sp-std/std", "serde", diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index f1481c0aed4e5e71fbf372c22032916426b3aa3a..9dcb1c2363c1551c5968320ca3c9493c0e23e98b 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -29,13 +29,14 @@ use sp_std::borrow::Cow; use sp_std::vec::Vec; mod app { - use app_crypto::{app_crypto, key_types::GRANDPA, ed25519}; + use sp_application_crypto::{app_crypto, key_types::GRANDPA, ed25519}; app_crypto!(ed25519, GRANDPA); } -/// The grandpa crypto scheme defined via the keypair type. -#[cfg(feature = "std")] -pub type AuthorityPair = app::Pair; +sp_application_crypto::with_pair! { + /// The grandpa crypto scheme defined via the keypair type. + pub type AuthorityPair = app::Pair; +} /// Identity of a Grandpa authority. pub type AuthorityId = app::Public; @@ -163,7 +164,7 @@ pub const AUTHORITIES_CALL: &str = "grandpa_authorities"; /// The current version of the stored AuthorityList type. The encoding version MUST be updated any /// time the AuthorityList type changes. -const AUTHORITIES_VERISON: u8 = 1; +const AUTHORITIES_VERSION: u8 = 1; /// An AuthorityList that is encoded with a version specifier. The encoding version is updated any /// time the AuthorityList type changes. This ensures that encodings of different versions of an @@ -192,18 +193,18 @@ impl<'a> Into for VersionedAuthorityList<'a> { impl<'a> Encode for VersionedAuthorityList<'a> { fn size_hint(&self) -> usize { - (AUTHORITIES_VERISON, self.0.as_ref()).size_hint() + (AUTHORITIES_VERSION, self.0.as_ref()).size_hint() } fn using_encoded R>(&self, f: F) -> R { - (AUTHORITIES_VERISON, self.0.as_ref()).using_encoded(f) + (AUTHORITIES_VERSION, self.0.as_ref()).using_encoded(f) } } impl<'a> Decode for VersionedAuthorityList<'a> { fn decode(value: &mut I) -> Result { let (version, authorities): (u8, AuthorityList) = Decode::decode(value)?; - if version != AUTHORITIES_VERISON { + if version != AUTHORITIES_VERSION { return Err("unknown Grandpa authorities version".into()); } Ok(authorities.into()) 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..e8df2c49e51931659859b16912664ab3fcd4c208 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. @@ -307,7 +312,7 @@ impl InherentDataProviders { /// Converts a given encoded error into a `String`. /// - /// Useful if the implementation encouters an error for an identifier it does not know. + /// Useful if the implementation encounters an error for an identifier it does not know. pub fn error_to_string(&self, identifier: &InherentIdentifier, error: &[u8]) -> String { let res = self.providers.read().iter().filter_map(|p| if p.inherent_identifier() == identifier { 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..a1e9181f2835787907538c78ae9c19e439639527 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.")] @@ -595,7 +594,7 @@ pub trait Offchain { /// Returns a random seed. /// - /// This is a trully random non deterministic seed generated by host environment. + /// This is a truly random, non-deterministic seed generated by host environment. /// Obviously fine in the off-chain worker context. fn random_seed(&mut self) -> [u8; 32] { self.extension::() @@ -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/rpc/src/list.rs b/primitives/rpc/src/list.rs index b8b7c537c4c4c3602a529c016bbbee57ee93604b..469eae3d1479aab9412addd006badf70eee44907 100644 --- a/primitives/rpc/src/list.rs +++ b/primitives/rpc/src/list.rs @@ -23,7 +23,7 @@ use serde::{Serialize, Deserialize}; /// For some RPCs it's convenient to call them with either /// a single value or a whole list of values to get a proper response. /// In theory you could do a batch query, but it's: -/// 1. Less convient in client libraries +/// 1. Less convenient in client libraries /// 2. If the response value is small, the protocol overhead might be dominant. /// /// Also it's nice to be able to maintain backward compatibility for methods that 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..4e99c0f06c1991bef86444641958bfb01059d2f4 100644 --- a/primitives/runtime-interface/test-wasm/src/lib.rs +++ b/primitives/runtime-interface/test-wasm/src/lib.rs @@ -25,7 +25,7 @@ use sp_std::{vec, vec::Vec, mem, convert::TryFrom}; use sp_core::{sr25519::Public, wasm_export_functions}; -// Inlucde the WASM binary +// Include the WASM binary #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.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..e04ce77fb2cbb7dc1d5ed7d028e621799138153c 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()); @@ -65,7 +65,7 @@ impl<'a> PiecewiseLinear<'a> { next.0.deconstruct().saturating_sub(prev.0.deconstruct()), ); - // If both substration are same sign then result is positive + // If both subtractions are same sign then result is positive if (n > prev.0 * d.clone()) == (next.1.deconstruct() > prev.1.deconstruct()) { (prev.1 * d).saturating_add(delta_y) // Otherwise result is negative @@ -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..dad3e1fc26b7caedacd99d454bb486588eab3428 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**. /// @@ -358,7 +365,7 @@ impl<'a, Hash> DigestItemRef<'a, Hash> { } /// Try to match this digest item to the given opaque item identifier; if it matches, then - /// try to cast to the given datatype; if that works, return it. + /// try to cast to the given data type; if that works, return it. pub fn try_to(&self, id: OpaqueDigestItemId) -> Option { self.try_as_raw(id).and_then(|mut x| Decode::decode(&mut x).ok()) } 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/mod.rs b/primitives/runtime/src/generic/mod.rs index 5e9928ba1909ab2aa114babaadcc1cc774131e60..f6399fff1382ade73804f5b105625b82dc379ec7 100644 --- a/primitives/runtime/src/generic/mod.rs +++ b/primitives/runtime/src/generic/mod.rs @@ -39,6 +39,15 @@ pub use self::digest::{ use crate::codec::Encode; use sp_std::prelude::*; +/// Perform singature check. +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum CheckSignature { + /// Perform. + Yes, + /// Don't perform. + No, +} + fn encode_with_vec_prefix)>(encoder: F) -> Vec { let size = ::sp_std::mem::size_of::(); let reserve = match size { diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index 0a435242485cbacc52941c0ebd0ec12698f227e3..0db60e32a6e5e3684eaebd915a9949b4840fa45d 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -20,8 +20,12 @@ 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}, - generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction}, + traits::{ + self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, ExtrinsicMetadata, + IdentifyAccount, + }, + generic::{CheckSignature, CheckedExtrinsic}, + transaction_validity::{TransactionValidityError, InvalidTransaction}, }; const TRANSACTION_VERSION: u8 = 4; @@ -41,6 +45,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 { @@ -105,18 +121,26 @@ where { type Checked = CheckedExtrinsic; - fn check(self, lookup: &Lookup) -> Result { + fn check(self, check_signature: CheckSignature, lookup: &Lookup) -> Result { Ok(match self.signature { Some((signed, signature, extra)) => { let signed = lookup.lookup(signed)?; - let raw_payload = SignedPayload::new(self.function, extra)?; - if !raw_payload.using_encoded(|payload| { - signature.verify(payload, &signed) - }) { - return Err(InvalidTransaction::BadProof.into()) - } - let (function, extra, _) = raw_payload.deconstruct(); + let (function, extra) = if let CheckSignature::No = check_signature { + (self.function, extra) + } else { + let raw_payload = SignedPayload::new(self.function, extra)?; + + if !raw_payload.using_encoded(|payload| { + signature.verify(payload, &signed) + }) { + return Err(InvalidTransaction::BadProof.into()) + } + let (function, extra, _) = raw_payload.deconstruct(); + + (function, extra) + }; + CheckedExtrinsic { signed: Some((signed, extra)), function, @@ -130,6 +154,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 +295,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 @@ -285,6 +331,7 @@ mod tests { use sp_io::hashing::blake2_256; use crate::codec::{Encode, Decode}; use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup}; + use crate::generic::CheckSignature; use serde::{Serialize, Deserialize}; type TestContext = IdentityLookup; @@ -316,6 +363,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 = (); @@ -364,7 +412,7 @@ mod tests { fn unsigned_check_should_work() { let ux = Ex::new_unsigned(vec![0u8; 0]); assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &Default::default()).is_ok()); + assert!(>::check(ux, CheckSignature::Yes, &Default::default()).is_ok()); } #[test] @@ -377,7 +425,7 @@ mod tests { ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!( - >::check(ux, &Default::default()), + >::check(ux, CheckSignature::Yes, &Default::default()), Err(InvalidTransaction::BadProof.into()), ); } @@ -392,7 +440,7 @@ mod tests { ); assert!(ux.is_signed().unwrap_or(false)); assert_eq!( - >::check(ux, &Default::default()), + >::check(ux, CheckSignature::Yes, &Default::default()), Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }), ); } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 46930c35e8e8ddb8217cd967b4002ed675e6cee9..4d6739bb134e8058871d5ad811a5b4a8bc839986 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. @@ -214,7 +214,7 @@ impl Default for MultiSigner { } } -/// NOTE: This implementations is required by `SimpleAddressDeterminator`, +/// NOTE: This implementations is required by `SimpleAddressDeterminer`, /// we convert the hash into some AccountId, it's fine to use any scheme. impl> crypto::UncheckedFrom for MultiSigner { fn unchecked_from(x: T) -> Self { @@ -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"), } } } @@ -458,7 +458,7 @@ pub type DispatchOutcome = Result<(), DispatchError>; /// /// Examples of reasons preventing inclusion in a block: /// - More block weight is required to process the extrinsic than is left in the block being built. -/// This doesn't neccessarily mean that the extrinsic is invalid, since it can still be +/// This doesn't necessarily mean that the extrinsic is invalid, since it can still be /// included in the next block if it has enough spare weight available. /// - The sender doesn't have enough funds to pay the transaction inclusion fee. Including such /// a transaction in the block doesn't make sense. @@ -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/http.rs b/primitives/runtime/src/offchain/http.rs index 50c4a5eae6c1c9be460cf54f4f663ec997284c09..88f4323ad7e9ed60e382535756edfa17d96b78bc 100644 --- a/primitives/runtime/src/offchain/http.rs +++ b/primitives/runtime/src/offchain/http.rs @@ -257,7 +257,7 @@ pub enum Error { DeadlineReached, /// Request had timed out. IoError, - /// Unknown error has been ecountered. + /// Unknown error has been encountered. Unknown, } 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..681bc14451e116e52c71f1f239d7ff539ded9c8e --- /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 atomically. + /// + /// 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 successfully 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..be0e36b2d1a6b76ce7ec61382856c70b6fddffba 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -25,11 +25,10 @@ use crate::traits::{ }; #[allow(deprecated)] use crate::traits::ValidateUnsigned; -use crate::{generic, KeyTypeId, ApplyExtrinsicResult}; +use crate::{generic::{self, CheckSignature}, KeyTypeId, ApplyExtrinsicResult}; pub use sp_core::{H256, sr25519}; use sp_core::{crypto::{CryptoType, Dummy, key_types, Public}, U256}; -use crate::transaction_validity::{TransactionValidity, TransactionValidityError}; - +use crate::transaction_validity::{TransactionValidity, TransactionValidityError, InvalidTransaction}; /// Authority Id #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub struct UintAuthorityId(pub u64); @@ -115,6 +114,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 +147,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 +219,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 +254,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, @@ -289,12 +294,72 @@ impl<'a, Xt> Deserialize<'a> for Block where Block: Decode { } } -/// Test transaction, tuple of (sender, call, signed_extra) -/// with index only used if sender is some. +/// Test validity. +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +pub enum TestValidity { + /// Valid variant that will pass all checks. + Valid, + /// Variant with invalid signature. + /// + /// Will fail signature check. + SignatureInvalid(TransactionValidityError), + /// Variant with invalid logic. + /// + /// Will fail all checks. + OtherInvalid(TransactionValidityError), +} + +/// Test transaction. /// -/// If sender is some then the transaction is signed otherwise it is unsigned. +/// Used to mock actual transaction. #[derive(PartialEq, Eq, Clone, Encode, Decode)] -pub struct TestXt(pub Option<(u64, Extra)>, pub Call); +pub struct TestXt { + /// Signature with extra. + /// + /// if some, then the transaction is signed. Transaction is unsigned otherwise. + pub signature: Option<(u64, Extra)>, + /// Validity. + /// + /// Instantiate invalid variant and transaction will fail correpsonding checks. + pub validity: TestValidity, + /// Call. + pub call: Call, +} + +impl TestXt { + /// New signed test `TextXt`. + pub fn new_signed(signature: (u64, Extra), call: Call) -> Self { + TestXt { + signature: Some(signature), + validity: TestValidity::Valid, + call, + } + } + + /// New unsigned test `TextXt`. + pub fn new_unsigned(call: Call) -> Self { + TestXt { + signature: None, + validity: TestValidity::Valid, + call, + } + } + + /// Build invalid variant of `TestXt`. + pub fn invalid(mut self, err: TransactionValidityError) -> Self { + self.validity = TestValidity::OtherInvalid(err); + self + } + + /// Build badly signed variant of `TestXt`. + pub fn badly_signed(mut self) -> Self { + self.validity = TestValidity::SignatureInvalid(TransactionValidityError::Invalid(InvalidTransaction::BadProof)); + self + } + } + +// 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 { @@ -304,24 +369,39 @@ impl Serialize for TestXt where TestXt: E impl Debug for TestXt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TestXt({:?}, ...)", self.0.as_ref().map(|x| &x.0)) + write!(f, "TestXt({:?}, {}, ...)", + self.signature.as_ref().map(|x| &x.0), + if let TestValidity::Valid = self.validity { "valid" } else { "invalid" } + ) } } impl Checkable for TestXt { type Checked = Self; - fn check(self, _: &Context) -> Result { Ok(self) } + fn check(self, signature: CheckSignature, _: &Context) -> Result { + match self.validity { + TestValidity::Valid => Ok(self), + TestValidity::SignatureInvalid(e) => + if let CheckSignature::No = signature { + Ok(self) + } else { + Err(e) + }, + TestValidity::OtherInvalid(e) => Err(e), + } + } } + impl traits::Extrinsic for TestXt { type Call = Call; type SignaturePayload = (u64, Extra); fn is_signed(&self) -> Option { - Some(self.0.is_some()) + Some(self.signature.is_some()) } - fn new(c: Call, sig: Option) -> Option { - Some(TestXt(sig, c)) + fn new(call: Call, signature: Option) -> Option { + Some(TestXt { signature, call, validity: TestValidity::Valid }) } } @@ -335,7 +415,7 @@ impl Applyable for TestXt where type Call = Call; type DispatchInfo = Info; - fn sender(&self) -> Option<&Self::AccountId> { self.0.as_ref().map(|x| &x.0) } + fn sender(&self) -> Option<&Self::AccountId> { self.signature.as_ref().map(|x| &x.0) } /// Checks to see if this is a valid *transaction*. It returns information on it if so. #[allow(deprecated)] // Allow ValidateUnsigned @@ -355,14 +435,14 @@ impl Applyable for TestXt where info: Self::DispatchInfo, len: usize, ) -> ApplyExtrinsicResult { - let maybe_who = if let Some((who, extra)) = self.0 { - Extra::pre_dispatch(extra, &who, &self.1, info, len)?; + let maybe_who = if let Some((who, extra)) = self.signature { + Extra::pre_dispatch(extra, &who, &self.call, info, len)?; Some(who) } else { - Extra::pre_dispatch_unsigned(&self.1, info, len)?; + Extra::pre_dispatch_unsigned(&self.call, info, len)?; None }; - Ok(self.1.dispatch(maybe_who.into()).map_err(Into::into)) + Ok(self.call.dispatch(maybe_who.into()).map_err(Into::into)) } } diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 2547ce1072185d07a3404740f75d5b87ef302ab0..183df08ab8b0f0b327a43c4d35cafcf08fd2f728 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}; +use crate::generic::{Digest, DigestItem, CheckSignature}; 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. @@ -646,7 +637,7 @@ pub trait Checkable: Sized { type Checked; /// Check self, given an instance of Context. - fn check(self, c: &Context) -> Result; + fn check(self, signature: CheckSignature, c: &Context) -> Result; } /// A "checkable" piece of information, used by the standard Substrate Executive in order to @@ -658,15 +649,15 @@ pub trait BlindCheckable: Sized { type Checked; /// Check self. - fn check(self) -> Result; + fn check(self, signature: CheckSignature) -> Result; } // Every `BlindCheckable` is also a `StaticCheckable` for arbitrary `Context`. impl Checkable for T { type Checked = ::Checked; - fn check(self, _c: &Context) -> Result { - BlindCheckable::check(self) + fn check(self, signature: CheckSignature, _c: &Context) -> Result { + BlindCheckable::check(self, signature) } } @@ -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(()) } } @@ -939,7 +955,7 @@ pub trait ValidateUnsigned { fn validate_unsigned(call: &Self::Call) -> TransactionValidity; } -/// Opaque datatype that may be destructured into a series of raw byte slices (which represent +/// Opaque data type that may be destructured into a series of raw byte slices (which represent /// individual keys). pub trait OpaqueKeys: Clone { /// Types bound to this opaque keys that provide the key type ids returned. @@ -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..1ef30ca5dbd8e64a4b091b2a770adf0823ee460c 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, @@ -196,13 +193,20 @@ impl Instance { /// - This module doesn't have an exported function with the given name, /// - If types of the arguments passed to the function doesn't match function signature /// then trap occurs (as if the exported function was called via call_indirect), - /// - Trap occured at the execution time. + /// - Trap occurred at the execution time. 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..ca6612a5e92ddcace4fdc0fb69bc9e6e9c1c43dd 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -209,10 +209,20 @@ pub trait Backend: std::fmt::Debug { /// Query backend usage statistics (i/o, memory) /// /// Not all implementations are expected to be able to do this. In the - /// case when thay don't, empty statistics is returned. + /// case when they don't, empty statistics is returned. 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/changes_trie/mod.rs b/primitives/state-machine/src/changes_trie/mod.rs index 12074b7261aa58cde63c57891f4728f1a2603d7c..d614992df303320cefabadf80585ae47f2123206 100644 --- a/primitives/state-machine/src/changes_trie/mod.rs +++ b/primitives/state-machine/src/changes_trie/mod.rs @@ -16,7 +16,7 @@ //! Changes trie related structures and functions. //! -//! Changes trie is a trie built of { storage key => extrinsiscs } pairs +//! Changes trie is a trie built of { storage key => extrinsics } pairs //! at the end of each block. For every changed storage key it contains //! a pair, mapping key to the set of extrinsics where it has been changed. //! @@ -130,7 +130,7 @@ pub struct AnchorBlockId { pub struct State<'a, H, Number> { /// Configuration that is active at given block. pub config: Configuration, - /// Configuration activation block number. Zero if it is the first coonfiguration on the chain, + /// Configuration activation block number. Zero if it is the first configuration on the chain, /// or number of the block that have emit NewConfiguration signal (thus activating configuration /// starting from the **next** block). pub zero: Number, 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/lib.rs b/primitives/state-machine/src/lib.rs index bb62df6da49059c2edaa3a51d863fbac3e3358f1..bb2bb2c52c86f153c47e3a32b23aced7d3c4d5d2 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -377,7 +377,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where /// Note: changes to code will be in place if this call is made again. For running partial /// blocks (e.g. a transaction at a time), ensure a different method is used. /// - /// Returns the result of the executed function either in native reprensentation `R` or + /// Returns the result of the executed function either in native representation `R` or /// in SCALE encoded representation. pub fn execute_using_consensus_failure_handler( &mut self, diff --git a/primitives/state-machine/src/overlayed_changes.rs b/primitives/state-machine/src/overlayed_changes.rs index ed6f30a4f596bfbe705951d2761abb289f5ebab1..4eb44de7c58624a56c14ecd932edab5ca7ed3c74 100644 --- a/primitives/state-machine/src/overlayed_changes.rs +++ b/primitives/state-machine/src/overlayed_changes.rs @@ -55,7 +55,7 @@ pub struct OverlayedChanges { pub(crate) prospective: OverlayedChangeSet, /// Committed changes. pub(crate) committed: OverlayedChangeSet, - /// True if extrinsiscs stats must be collected. + /// True if extrinsics stats must be collected. pub(crate) collect_extrinsics: bool, } @@ -65,7 +65,7 @@ pub struct OverlayedChanges { pub struct OverlayedValue { /// Current value. None if value has been deleted. pub value: Option, - /// The set of extinsic indices where the values has been changed. + /// The set of extrinsic indices where the values has been changed. /// Is filled only if runtime has announced changes trie support. pub extrinsics: Option>, } @@ -371,7 +371,7 @@ impl OverlayedChanges { } if let Some((child_committed, _child_info)) = self.committed.children.get(storage_key) { - // Then do the same with keys from commited changes. + // Then do the same with keys from committed changes. // NOTE that we are making changes in the prospective change set. for key in child_committed.keys() { if key.starts_with(prefix) { @@ -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/proving_backend.rs b/primitives/state-machine/src/proving_backend.rs index 70124927fdd2ea17505bdbc73e380b14d2561fd1..672ec6ea794e4e42771d3089b69bb97016dfeb94 100644 --- a/primitives/state-machine/src/proving_backend.rs +++ b/primitives/state-machine/src/proving_backend.rs @@ -436,7 +436,7 @@ mod tests { } #[test] - fn passes_throgh_backend_calls() { + fn passes_through_backend_calls() { let trie_backend = test_trie(); let proving_backend = test_proving(&trie_backend); assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap()); 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..6b28f66b43bff2fb8dc691ad3f8f1ac397467453 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; @@ -36,7 +35,7 @@ pub use core::ops; pub use core::ptr; pub use core::result; pub use core::slice; -// Allow intepreting vectors of bytes as strings, but not constructing them. +// Allow interpreting vectors of bytes as strings, but not constructing them. pub use core::str; // We are trying to avoid certain things here, such as `core::string` // (if you need `String` you are probably doing something wrong, since 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/error.rs b/primitives/transaction-pool/src/error.rs index ecce202d7d87973aa0fdcd743e3179d776325540..1a456ca4fd03c6c7eba9a067ed14384a2f1fe73d 100644 --- a/primitives/transaction-pool/src/error.rs +++ b/primitives/transaction-pool/src/error.rs @@ -52,7 +52,7 @@ pub enum Error { /// Transaction entering the pool. new: Priority }, - /// Deps cycle etected and we couldn't import transaction. + /// Deps cycle detected and we couldn't import transaction. #[display(fmt="Cycle Detected")] CycleDetected, /// Transaction was dropped immediately after it got inserted. diff --git a/primitives/transaction-pool/src/pool.rs b/primitives/transaction-pool/src/pool.rs index e67a9890755d74a1f3f9afcbe7fe88df51ffe4b7..89f327a523b5e5a8ad1c7fee3e9ef0394fb812e2 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, @@ -70,12 +71,18 @@ impl PoolStatus { /// - `Invalid` /// - `Usurped` /// - `Dropped` +/// 4. Re-entering the pool: +/// - `Retracted` +/// 5. Block finalized: +/// - `Finalized` +/// - `FinalityTimeout` /// /// The events will always be received in the order described above, however /// there might be cases where transactions alternate between `Future` and `Ready` /// pool, and are `Broadcast` in the meantime. /// /// There is also only single event causing the transaction to leave the pool. +/// I.e. only one of the listed ones should be triggered. /// /// Note that there are conditions that may cause transactions to reappear in the pool. /// 1. Due to possible forks, the transaction that ends up being in included @@ -85,8 +92,15 @@ impl PoolStatus { /// 3. `Invalid` transaction may become valid at some point in the future. /// (Note that runtimes are encouraged to use `UnknownValidity` to inform the pool about /// such case). +/// 4. `Retracted` transactions might be included in some next block. /// -/// However the user needs to re-subscribe to receive such notifications. +/// The stream is considered finished only when either `Finalized` or `FinalityTimeout` +/// event is triggered. You are however free to unsubscribe from notifications at any point. +/// The first one will be emitted when the block, in which transaction was included gets +/// finalized. The `FinalityTimeout` event will be emitted when the block did not reach finality +/// within 512 blocks. This either indicates that finality is not available for your chain, +/// or that finality gadget is lagging behind. If you choose to wait for finality longer, you can +/// re-subscribe for a particular transaction hash manually again. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum TransactionStatus { @@ -97,8 +111,14 @@ pub enum TransactionStatus { /// The transaction has been broadcast to the given peers. Broadcast(Vec), /// Transaction has been included in block with given hash. - #[serde(rename = "finalized")] // See #4438 InBlock(BlockHash), + /// The block this transaction was included in has been retracted. + Retracted(BlockHash), + /// Maximum number of finality watchers has been reached, + /// old watchers are being removed. + FinalityTimeout(BlockHash), + /// Transaction has been finalized by a finality-gadget, e.g GRANDPA + Finalized(BlockHash), /// Transaction has been replaced in the pool, by another transaction /// that provides the same tags. (e.g. same (sender, nonce)). Usurped(Hash), @@ -112,7 +132,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 +143,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. @@ -146,7 +169,7 @@ pub trait InPoolTransaction { /// Get tags that transaction provides. fn provides(&self) -> &[TransactionTag]; /// Return a flag indicating if the transaction should be propagated to other peers. - fn is_propagateable(&self) -> bool; + fn is_propagable(&self) -> bool; } /// Transaction pool interface. @@ -163,66 +186,80 @@ 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>; +} + +/// Events that the transaction pool listens for. +pub enum ChainEvent { + /// New blocks have been added to the chain + NewBlock { + /// Is this the new best block. + is_new_best: bool, + /// Id of the just imported block. + id: BlockId, + /// Header of the just imported block + header: B::Header, + /// List of retracted blocks ordered by block number. + retracted: Vec, + }, + /// An existing block has been finalized. + Finalized { + /// Hash of just finalized block + hash: B::Hash, + }, +} + +/// Trait for transaction pool maintenance. +pub trait MaintainedTransactionPool: TransactionPool { + /// Perform maintenance + fn maintain(&self, event: ChainEvent) -> Pin + Send>>; } /// An abstraction for transaction pool. @@ -265,108 +302,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..f6131c8ed5eed6015c0a4fa8c18ffbd99469afba 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; @@ -91,7 +91,6 @@ pub type PlainDB<'a, K> = dyn hash_db::PlainDB + 'a; /// key conflict for non random keys). pub type PrefixedMemoryDB = memory_db::MemoryDB, trie_db::DBValue>; /// Reexport from `hash_db`, with genericity set for `Hasher` trait. -/// This uses the `KeyFunction` for prefixing keys internally (avoiding /// This uses a noops `KeyFunction` (key addressing must be hashed or using /// an encoding scheme that avoid key conflict). pub type MemoryDB = memory_db::MemoryDB, trie_db::DBValue>; diff --git a/primitives/trie/src/node_header.rs b/primitives/trie/src/node_header.rs index edcd28a568bcd3568c3fd22e6b0326c3f8a1361d..7aa16292549ede64686a0fe6dfd09c14fe912dc1 100644 --- a/primitives/trie/src/node_header.rs +++ b/primitives/trie/src/node_header.rs @@ -69,7 +69,7 @@ impl Decode for NodeHeader { } /// Returns an iterator over encoded bytes for node header and size. -/// Size encoding allows unlimited, length unefficient, representation, but +/// Size encoding allows unlimited, length inefficient, representation, but /// is bounded to 16 bit maximum value to avoid possible DOS. pub(crate) fn size_and_prefix_iterator(size: usize, prefix: u8) -> impl Iterator { let size = sp_std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, size); 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..6d6b539483e3999ae79a9f78f3b7fdc6381230b1 100644 --- a/test-utils/client/src/client_ext.rs +++ b/test-utils/client/src/client_ext.rs @@ -87,57 +87,28 @@ impl ClientBlockImportExt for std::sync::A { fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: None, - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.body = Some(extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: None, - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::Custom(true), - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.body = Some(extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } fn import_as_final(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: None, - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: true, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::Custom(true), - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.body = Some(extrinsics); + import.finalized = true; + import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } @@ -149,19 +120,11 @@ impl ClientBlockImportExt for std::sync::A justification: Justification, ) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: Some(justification), - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: true, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.justification = Some(justification); + import.body = Some(extrinsics); + import.finalized = true; + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } @@ -173,57 +136,28 @@ impl ClientBlockImportExt for Client Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: None, - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.body = Some(extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: None, - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: false, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::Custom(true), - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.body = Some(extrinsics); + import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } fn import_as_final(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: None, - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: true, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::Custom(true), - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.body = Some(extrinsics); + import.finalized = true; + import.fork_choice = Some(ForkChoiceStrategy::Custom(true)); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } @@ -235,19 +169,11 @@ impl ClientBlockImportExt for Client Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); - let import = BlockImportParams { - origin, - header, - justification: Some(justification), - post_digests: vec![], - body: Some(extrinsics), - storage_changes: None, - finalized: true, - auxiliary: Vec::new(), - fork_choice: ForkChoiceStrategy::LongestChain, - allow_missing_state: false, - import_existing: false, - }; + let mut import = BlockImportParams::new(origin, header); + import.justification = Some(justification); + import.body = Some(extrinsics); + import.finalized = true; + import.fork_choice = Some(ForkChoiceStrategy::LongestChain); BlockImport::import_block(self, import, HashMap::new()).map(|_| ()) } diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index 8603b26d50a58ba239d1ea848c9b86191659c277..afe11903d5be969471ea39a4e6eda477b2d35e89 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -21,7 +21,10 @@ pub mod client_ext; pub use sc_client::{blockchain, self}; -pub use sc_client_api::execution_extensions::{ExecutionStrategies, ExecutionExtensions}; +pub use sc_client_api::{ + execution_extensions::{ExecutionStrategies, ExecutionExtensions}, + ForkBlocks, BadBlocks, +}; pub use sc_client_db::{Backend, self}; pub use sp_consensus; pub use sc_executor::{NativeExecutor, WasmExecutionMethod, self}; @@ -33,7 +36,6 @@ pub use sp_keyring::{ pub use sp_core::{Blake2Hasher, traits::BareCryptoStorePtr}; pub use sp_runtime::{Storage, StorageChild}; pub use sp_state_machine::ExecutionStrategy; - pub use self::client_ext::{ClientExt, ClientBlockImportExt}; use std::sync::Arc; @@ -48,7 +50,7 @@ pub type LightBackend = sc_client::light::backend::Backend< Blake2Hasher, >; -/// A genesis storage initialisation trait. +/// A genesis storage initialization trait. pub trait GenesisInit: Default { /// Construct genesis storage. fn genesis_storage(&self) -> Storage; @@ -61,23 +63,25 @@ impl GenesisInit for () { } /// A builder for creating a test client instance. -pub struct TestClientBuilder { +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..138e79cdd5c591c8a6c2535de1192c86e27afb7b 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -41,6 +41,7 @@ use sp_runtime::{ BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, GetNodeBlockType, GetRuntimeBlockType, Verify, IdentityLookup, }, + generic::CheckSignature, }; use sp_version::RuntimeVersion; pub use sp_core::{hash::H256}; @@ -52,10 +53,10 @@ use cfg_if::cfg_if; use sp_core::storage::ChildType; // Ensure Babe and Aura use the same crypto to simplify things a bit. -pub use sp_consensus_babe::AuthorityId; +pub use sp_consensus_babe::{AuthorityId, SlotNumber}; pub type AuraId = sp_consensus_aura::sr25519::AuthorityId; -// Inlucde the WASM binary +// Include the WASM binary #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -114,6 +115,8 @@ pub enum Extrinsic { ChangesTrieConfigUpdate(Option), } +parity_util_mem::malloc_size_of_is_0!(Extrinsic); // non-opaque extrinsic does not need this + #[cfg(feature = "std")] impl serde::Serialize for Extrinsic { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { @@ -124,7 +127,7 @@ impl serde::Serialize for Extrinsic { impl BlindCheckable for Extrinsic { type Checked = Self; - fn check(self) -> Result { + fn check(self, _signature: CheckSignature) -> Result { match self { Extrinsic::AuthoritiesChange(new_auth) => Ok(Extrinsic::AuthoritiesChange(new_auth)), Extrinsic::Transfer(transfer, signature) => { @@ -255,8 +258,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 +300,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 +341,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 +372,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 +450,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! { @@ -497,6 +494,10 @@ cfg_if! { system::execute_transaction(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + system::execute_transaction(extrinsic) + } + fn finalize_block() -> ::Header { system::finalize_block() } @@ -558,14 +559,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"); } @@ -618,6 +611,10 @@ cfg_if! { secondary_slots: true, } } + + fn current_epoch_start() -> SlotNumber { + >::current_epoch_start() + } } impl sp_offchain::OffchainWorkerApi for Runtime { @@ -631,6 +628,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 { @@ -682,6 +685,10 @@ cfg_if! { system::execute_transaction(extrinsic) } + fn apply_trusted_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + system::execute_transaction(extrinsic) + } + fn finalize_block() -> ::Header { system::finalize_block() } @@ -747,41 +754,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) } @@ -834,6 +806,10 @@ cfg_if! { secondary_slots: true, } } + + fn current_epoch_start() -> SlotNumber { + >::current_epoch_start() + } } impl sp_offchain::OffchainWorkerApi for Runtime { @@ -847,6 +823,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 +940,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..b410d317a1babaeb52443bb7de7e3bfaf01586c0 100644 --- a/test-utils/runtime/src/system.rs +++ b/test-utils/runtime/src/system.rs @@ -29,6 +29,7 @@ use sp_runtime::{ transaction_validity::{ TransactionValidity, ValidTransaction, InvalidTransaction, TransactionValidityError, }, + generic::CheckSignature, }; use codec::{KeyedVec, Encode, Decode}; use frame_system::Trait; @@ -46,7 +47,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; @@ -243,7 +244,7 @@ pub fn finalize_block() -> Header { #[inline(always)] fn check_signature(utx: &Extrinsic) -> Result<(), TransactionValidityError> { use sp_runtime::traits::BlindCheckable; - utx.clone().check().map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ()) + utx.clone().check(CheckSignature::Yes).map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ()) } fn execute_transaction_backend(utx: &Extrinsic) -> ApplyExtrinsicResult { diff --git a/test-utils/runtime/transaction-pool/Cargo.toml b/test-utils/runtime/transaction-pool/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..72e81f5f19afa891e5e805b5057be41ac5cf05ee --- /dev/null +++ b/test-utils/runtime/transaction-pool/Cargo.toml @@ -0,0 +1,17 @@ +[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-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +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..aedc7dc4c37577cd32e345d8c37d30c36742559d --- /dev/null +++ b/test-utils/runtime/transaction-pool/src/lib.rs @@ -0,0 +1,271 @@ +// 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)] +pub 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) -> Header { + let mut chain = self.chain.write(); + chain.block_by_number.insert(block_number, xts.clone()); + let header = Header { + number: block_number, + digest: Default::default(), + extrinsics_root: Default::default(), + parent_hash: block_number + .checked_sub(1) + .and_then(|num| { + chain.header_by_number.get(&num) + .cloned().map(|h| h.hash()) + }).unwrap_or_default(), + state_root: Default::default(), + }; + chain.block_by_hash.insert(header.hash(), xts); + chain.header_by_number.insert(block_number, header.clone()); + header + } + + /// 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); + } + + pub fn push_fork_block_with_parent(&self, parent: Hash, xts: Vec) -> Header { + let mut chain = self.chain.write(); + let blocknum = chain.block_by_number.keys().max().expect("block_by_number shouldn't be empty"); + let header = Header { + number: *blocknum, + digest: Default::default(), + extrinsics_root: Default::default(), + parent_hash: parent, + state_root: Default::default(), + }; + chain.block_by_hash.insert(header.hash(), xts); + header + } + + 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() + } + + /// get a reference to the chain state + pub fn chain(&self) -> &RwLock { + &self.chain + } + + /// 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()), + generic::BlockId::Number(num) => { + self.chain.read() + .header_by_number.get(num) + .map(|h| h.hash()) + .or_else(|| 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(match id { + BlockId::Number(num) => self.chain.read().block_by_number.get(num).cloned(), + BlockId::Hash(hash) => self.chain.read().block_by_hash.get(hash).cloned(), + })) + } +} + +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..7e101c438a881a5df54fbb73f10b8b632640708d 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.16.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..d0cf9c9f37ecef0cac625d5df83702eafd815e38 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -23,7 +23,7 @@ use std::cmp::Reverse; use std::fmt; use codec::{Decode, Encode}; -/// Error occured when iterating with the tree. +/// Error occurred when iterating with the tree. #[derive(Clone, Debug, PartialEq)] pub enum Error { /// Adding duplicate node to tree. @@ -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. @@ -258,7 +283,7 @@ impl ForkTree where .map(|position| self.finalize_root_at(position)) } - /// Finalize root at given positiion. See `finalize_root` comment for details. + /// Finalize root at given position. See `finalize_root` comment for details. fn finalize_root_at(&mut self, position: usize) -> V { let node = self.roots.swap_remove(position); self.roots = node.children; @@ -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..46204082becda907dca0bfc1f898c5b2bd2cb67c 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()))).0 + ); let new_transaction = |nonce: u64| { let t = Transfer { diff --git a/utils/grafana-data-source/Cargo.toml b/utils/grafana-data-source/Cargo.toml index 4487b5359fd0cd700c04110e3d7f3472b18ee489..c49ee963f0c87165a24e28b5d9a85bbbca0f3807 100644 --- a/utils/grafana-data-source/Cargo.toml +++ b/utils/grafana-data-source/Cargo.toml @@ -8,16 +8,16 @@ edition = "2018" [dependencies] log = "0.4.8" -hyper = { version = "0.13.1", default-features = false, features = ["stream"] } -tokio = "0.2" futures-util = { version = "0.3.1", default-features = false, features = ["io"] } serde_json = "1" serde = { version = "1", features = ["derive"] } chrono = { version = "0.4", features = ["serde"] } lazy_static = "1.4" -parking_lot = "0.9" -futures-timer = "2.0" +parking_lot = "0.10.0" +futures-timer = "3.0.1" derive_more = "0.99" [target.'cfg(not(target_os = "unknown"))'.dependencies] async-std = { version = "1.0.1", features = ["unstable"] } +hyper = { version = "0.13.1", default-features = false, features = ["stream"] } +tokio = "0.2" diff --git a/utils/grafana-data-source/src/lib.rs b/utils/grafana-data-source/src/lib.rs index fbba064706e0a548740c54e7667add2df7f9c1fd..bc40fc39bbed6be0478b64ce1e637c112e650619 100644 --- a/utils/grafana-data-source/src/lib.rs +++ b/utils/grafana-data-source/src/lib.rs @@ -72,11 +72,13 @@ pub fn record_metrics_slice(metrics: &[(&str, f32)]) -> Result<(), Error> { #[derive(Debug, derive_more::Display, derive_more::From)] pub enum Error { /// Hyper internal error. + #[cfg(not(target_os = "unknown"))] Hyper(hyper::Error), - /// Serialization/deserialization error. - Serde(serde_json::Error), /// Http request error. + #[cfg(not(target_os = "unknown"))] Http(hyper::http::Error), + /// Serialization/deserialization error. + Serde(serde_json::Error), /// Timestamp error. Timestamp(TryFromIntError), /// i/o error. @@ -86,9 +88,11 @@ pub enum Error { impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { + #[cfg(not(target_os = "unknown"))] Error::Hyper(error) => Some(error), - Error::Serde(error) => Some(error), + #[cfg(not(target_os = "unknown"))] Error::Http(error) => Some(error), + Error::Serde(error) => Some(error), Error::Timestamp(error) => Some(error), Error::Io(error) => Some(error) } diff --git a/utils/grafana-data-source/src/server.rs b/utils/grafana-data-source/src/server.rs index 8ef5e0378478cc0ef1f53585ad518826e8150d37..f2f06f76888703199ff1e1906558d193c89654da 100644 --- a/utils/grafana-data-source/src/server.rs +++ b/utils/grafana-data-source/src/server.rs @@ -15,12 +15,15 @@ // along with Substrate. If not, see . use serde::{Serialize, de::DeserializeOwned}; -use hyper::{Body, Request, Response, header, service::{service_fn, make_service_fn}, Server}; use chrono::{Duration, Utc}; use futures_util::{FutureExt, TryStreamExt, future::{Future, select, Either}}; use futures_timer::Delay; use crate::{DATABASE, Error, types::{Target, Query, TimeseriesData, Range}}; +#[cfg(not(target_os = "unknown"))] +use hyper::{Body, Request, Response, header, service::{service_fn, make_service_fn}, Server}; + +#[cfg(not(target_os = "unknown"))] async fn api_response(req: Request) -> Result, Error> { match req.uri().path() { "/search" => { @@ -57,6 +60,7 @@ async fn api_response(req: Request) -> Result, Error> { } } +#[cfg(not(target_os = "unknown"))] async fn map_request_to_response(req: Request, transformation: T) -> Result, Error> where Req: DeserializeOwned, diff --git a/utils/grafana-data-source/test/Cargo.toml b/utils/grafana-data-source/test/Cargo.toml index 9575866fa8988c08aa2cd7a742638f30d0778b15..18c080c8d1f71468e6baaa328300f4d80f2d5f1f 100644 --- a/utils/grafana-data-source/test/Cargo.toml +++ b/utils/grafana-data-source/test/Cargo.toml @@ -9,5 +9,5 @@ edition = "2018" [dependencies] grafana-data-source = { version = "0.8", path = ".." } futures = "0.3" -futures-timer = "2.0" +futures-timer = "3.0.1" rand = "0.7" 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"); diff --git a/utils/wasm-builder/README.md b/utils/wasm-builder/README.md index b21c588ac383c0b72857e35ef18635d011b46702..2fd9a6ab4cf80c9886a687474eacafbd2d6d5bd7 100644 --- a/utils/wasm-builder/README.md +++ b/utils/wasm-builder/README.md @@ -57,7 +57,7 @@ be `NODE_RUNTIME`. ## Prerequisites: -WASM builder requires the following prerequisities for building the WASM binary: +WASM builder requires the following prerequisites for building the WASM binary: - rust nightly + `wasm32-unknown-unknown` toolchain diff --git a/utils/wasm-builder/src/lib.rs b/utils/wasm-builder/src/lib.rs index 80192eb3613ceca4f035b9a18bd944d2267be054..8500eba4a01f45146ef8a7c74143544bb369333d 100644 --- a/utils/wasm-builder/src/lib.rs +++ b/utils/wasm-builder/src/lib.rs @@ -73,7 +73,7 @@ //! //! ## Prerequisites: //! -//! WASM builder requires the following prerequisities for building the WASM binary: +//! WASM builder requires the following prerequisites for building the WASM binary: //! //! - rust nightly + `wasm32-unknown-unknown` toolchain //! diff --git a/utils/wasm-builder/src/wasm_project.rs b/utils/wasm-builder/src/wasm_project.rs index c9b573ff1940ff7dce935a4cb27c0656b2101e5f..60be4684ba893c34527faba2cb889478354d2840 100644 --- a/utils/wasm-builder/src/wasm_project.rs +++ b/utils/wasm-builder/src/wasm_project.rs @@ -431,7 +431,7 @@ fn generate_rerun_if_changed_instructions( .exec() .expect("`cargo metadata` can not fail!"); - // Make sure that if any file/folder of a depedency change, we need to rerun the `build.rs` + // Make sure that if any file/folder of a dependency change, we need to rerun the `build.rs` metadata.packages.into_iter() .filter(|package| !package.manifest_path.starts_with(wasm_workspace)) .for_each(|package| {