diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 60a75d5db4ff5951615a68ce022a4e556e25cdee..7a0fdb9954921809896527a3734b59fc9f1bd22d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,6 +49,12 @@ variables: - schedules - web - /^[0-9]+$/ # PRs + retry: + max: 2 + when: + - runner_system_failure + - unknown_failure + - api_failure tags: - linux-docker @@ -200,20 +206,9 @@ check-web-wasm: - time cargo web build -p substrate-consensus-aura - time cargo web build -p substrate-consensus-babe - time cargo web build -p substrate-consensus-common - - time cargo web build -p substrate-keyring - - time cargo web build -p substrate-keystore - - time cargo web build -p substrate-executor - - time cargo web build -p substrate-network - - time cargo web build -p substrate-panic-handler - - time cargo web build -p substrate-peerset - - time cargo web build -p substrate-primitives - # TODO: we can't use cargo web until https://github.com/paritytech/jsonrpc/pull/436 is deployed - - time cargo build -p substrate-rpc-servers --target wasm32-unknown-unknown - - time cargo web build -p substrate-serializer - - time cargo web build -p substrate-state-db - - time cargo web build -p substrate-state-machine - time cargo web build -p substrate-telemetry - - time cargo web build -p substrate-trie + # Note: the command below is a bit weird because several Cargo issues prevent us from compiling the node in a more straight-forward way. + - time cargo build --manifest-path=node/cli/Cargo.toml --no-default-features --features "browser" --target=wasm32-unknown-unknown - sccache -s node-exits: @@ -288,7 +283,7 @@ build-rust-doc-release: <<: *build-only script: - rm -f ./crate-docs/index.html # use it as an indicator if the job succeeds - - BUILD_DUMMY_WASM_BINARY=1 time cargo +nightly doc --release --all --verbose + - BUILD_DUMMY_WASM_BINARY=1 RUSTDOCFLAGS="--html-in-header $(pwd)/rustdoc-header.html" time cargo +nightly doc --release --all --verbose - cp -R ./target/doc ./crate-docs - echo "" > ./crate-docs/index.html - sccache -s diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 1de4820a1170673b1aa0741305860d92dd64e26d..817e1d7489f3968c2a851e78b1728949776b544d 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -26,6 +26,7 @@ Merging pull requests once CI is successful: - it is an urgent fix with no large change to logic, then it may be merged after a non-author contributor has approved the review once CI is complete. . Once a PR is ready for review please add the https://github.com/paritytech/substrate/pulls?q=is%3Apr+is%3Aopen+label%3AA0-pleasereview[`pleasereview`] label. Generally PRs should sit with this label for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. +. If the first review is not an approval, swap `A0-pleasereview` to any label `[A3, A7]` to indicate that the PR has received some feedback, but needs further work. For example. https://github.com/paritytech/substrate/labels/A3-inprogress[`A3-inprogress`] is a general indicator that the PR is work in progress and https://github.com/paritytech/substrate/labels/A4-gotissues[`A4-gotissues`] means that it has significant problems that need fixing. Once the work is done, change the label back to `A0-pleasereview`. You might end up swapping a few times back and forth to climb up the A label group. Once a PR is https://github.com/paritytech/substrate/labels/A8-looksgood[`A8-looksgood`], it is ready to merge. . PRs that break the external API must be tagged with https://github.com/paritytech/substrate/labels/B2-breaksapi[`breaksapi`], when it changes the SRML or consensus of running system with https://github.com/paritytech/substrate/labels/B3-breaksconsensus[`breaksconsensus`] . No PR should be merged until all reviews' comments are addressed. diff --git a/Cargo.lock b/Cargo.lock index 9d80227547e012067b9ef28c1c7b2a7f86338b6e..897a0701f9d6ffdea11665d2db1b306418b4e21e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ dependencies = [ [[package]] name = "ahash" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -104,27 +104,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arrayvec" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "asn1_der" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "asn1_der_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "asn1_der_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "asn1_der_derive" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "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)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -188,6 +188,16 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bincode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bindgen" version = "0.47.3" @@ -197,7 +207,7 @@ dependencies = [ "cexpr 0.3.5 (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.26.4 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.32.0 (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)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -240,7 +250,17 @@ name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", +] + +[[package]] +name = "blake2b_simd" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", ] @@ -341,7 +361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" 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.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -366,7 +386,7 @@ dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -400,10 +420,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "chain-spec-builder" version = "2.0.0" dependencies = [ - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", + "structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-keystore 2.0.0", "substrate-primitives 2.0.0", - "substrate-service 2.0.0", ] [[package]] @@ -429,14 +451,14 @@ dependencies = [ [[package]] name = "clap" -version = "2.32.0" +version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.10.0 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -466,6 +488,24 @@ dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +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)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "console_log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "const-random" version = "0.1.6" @@ -503,6 +543,89 @@ name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cranelift-bforest" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-bforest 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-meta 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cranelift-entity" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-frontend" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-native" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cranelift-wasm" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crc32fast" version = "1.2.0" @@ -518,7 +641,7 @@ 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.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.32.0 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -532,7 +655,31 @@ dependencies = [ "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (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)", +] + +[[package]] +name = "criterion" +version = "0.3.0" +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.2 (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.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-traits 0.2.8 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (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)", ] @@ -547,6 +694,15 @@ dependencies = [ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "criterion-plot" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-channel" version = "0.3.9" @@ -569,7 +725,7 @@ name = "crossbeam-epoch" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (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.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -625,7 +781,7 @@ 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.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -643,7 +799,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -724,6 +880,26 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "directories" +version = "2.0.2" +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)", + "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +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)", + "libc 0.2.62 (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)", +] + [[package]] name = "dns-parser" version = "0.8.0" @@ -745,19 +921,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" 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)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ed25519-dalek" -version = "1.0.0-pre.1" +version = "1.0.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (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)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -786,6 +963,18 @@ dependencies = [ "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "env_logger" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + [[package]] name = "environmental" version = "1.0.2" @@ -799,6 +988,25 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "errno" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "error-chain" version = "0.12.1" @@ -817,24 +1025,38 @@ dependencies = [ "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "faerie" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.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.9.2 (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.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.6" 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)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -842,6 +1064,11 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fdlimit" version = "0.1.1" @@ -850,6 +1077,15 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "file-per-thread-logger" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + [[package]] name = "finality-grandpa" version = "0.9.0" @@ -875,6 +1111,17 @@ dependencies = [ "static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fixed-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fixedbitset" version = "0.1.9" @@ -890,7 +1137,7 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1054,7 +1301,7 @@ name = "generic-array" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1094,6 +1341,19 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gimli" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.2.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)", ] [[package]] @@ -1118,6 +1378,16 @@ dependencies = [ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "goblin" +version = "0.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.9.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "h2" version = "0.1.26" @@ -1159,10 +1429,10 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ahash 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1312,7 +1582,7 @@ dependencies = [ "http 0.1.18 (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.2 (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)", @@ -1371,7 +1641,7 @@ dependencies = [ [[package]] name = "impl-serde" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1379,12 +1649,12 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1404,16 +1674,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ipnet" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1452,14 +1721,14 @@ name = "jsonrpc-client-transports" version = "13.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", "jsonrpc-core 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-pubsub 13.2.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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (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.23.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1474,7 +1743,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1672,7 +1941,7 @@ dependencies = [ "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1680,11 +1949,11 @@ name = "libp2p-core" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "asn1_der 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "asn1_der 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.2 (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.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)", @@ -1703,10 +1972,10 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (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.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.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.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1774,9 +2043,9 @@ dependencies = [ "smallvec 0.6.10 (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)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (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.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1784,7 +2053,7 @@ name = "libp2p-kad" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1801,9 +2070,9 @@ dependencies = [ "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)", "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (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.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1825,7 +2094,7 @@ dependencies = [ "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.5 (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.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1841,7 +2110,7 @@ dependencies = [ "parking_lot 0.8.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)", - "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1878,7 +2147,7 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (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.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1943,7 +2212,7 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (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.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1954,7 +2223,7 @@ 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)", "get_if_addrs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnet 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ipnet 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2043,7 +2312,7 @@ dependencies = [ [[package]] name = "libsecp256k1" -version = "0.3.0" +version = "0.3.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)", @@ -2051,6 +2320,7 @@ dependencies = [ "hmac-drbg 0.2.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)", + "subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2127,6 +2397,14 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mach" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "malloc_size_of_derive" version = "0.1.0" @@ -2164,7 +2442,7 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-util-mem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2194,7 +2472,7 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2207,7 +2485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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.2 (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.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2233,7 +2511,7 @@ name = "mio-uds" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "iovec 0.1.2 (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.62 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2264,7 +2542,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (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.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2283,9 +2561,9 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.25 (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.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2318,22 +2596,29 @@ dependencies = [ name = "node-cli" version = "2.0.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)", + "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "exit-future 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb-memorydb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", + "libp2p 0.12.0 (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", "parity-scale-codec 1.0.6 (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)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", - "srml-authority-discovery 0.1.0", "srml-balances 2.0.0", "srml-contracts 2.0.0", "srml-finality-tracker 2.0.0", @@ -2342,8 +2627,8 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", - "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-authority-discovery 2.0.0", + "srml-transaction-payment 2.0.0", + "structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-basic-authorship 2.0.0", "substrate-chain-spec 2.0.0", "substrate-cli 2.0.0", @@ -2368,12 +2653,16 @@ dependencies = [ "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)", "transaction-factory 0.0.1", + "vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "node-executor" version = "2.0.0" dependencies = [ + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 2.0.0", "node-runtime 2.0.0", "node-testing 2.0.0", @@ -2388,6 +2677,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "srml-treasury 2.0.0", "substrate-executor 2.0.0", "substrate-primitives 2.0.0", @@ -2402,12 +2692,8 @@ dependencies = [ name = "node-primitives" version = "2.0.0" dependencies = [ - "parity-scale-codec 1.0.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.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", - "sr-std 2.0.0", - "substrate-client 2.0.0", "substrate-primitives 2.0.0", "substrate-serializer 2.0.0", ] @@ -2416,23 +2702,14 @@ dependencies = [ name = "node-rpc" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core-client 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 13.2.0 (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", "node-runtime 2.0.0", - "node-testing 2.0.0", - "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", + "srml-contracts-rpc 2.0.0", + "srml-system-rpc 2.0.0", + "srml-transaction-payment-rpc 2.0.0", "substrate-client 2.0.0", - "substrate-keyring 2.0.0", - "substrate-primitives 2.0.0", - "substrate-rpc-primitives 2.0.0", "substrate-transaction-pool 2.0.0", ] @@ -2440,7 +2717,7 @@ dependencies = [ name = "node-rpc-client" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.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)", "jsonrpc-core-client 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2459,41 +2736,47 @@ dependencies = [ "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-staking-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", - "srml-authority-discovery 0.1.0", "srml-authorship 0.1.0", "srml-babe 2.0.0", "srml-balances 2.0.0", "srml-collective 2.0.0", "srml-contracts 2.0.0", + "srml-contracts-rpc-runtime-api 2.0.0", "srml-democracy 2.0.0", - "srml-elections 2.0.0", + "srml-elections-phragmen 2.0.0", "srml-executive 2.0.0", "srml-finality-tracker 2.0.0", "srml-grandpa 2.0.0", "srml-im-online 0.1.0", "srml-indices 2.0.0", "srml-membership 2.0.0", + "srml-nicks 2.0.0", "srml-offences 1.0.0", + "srml-randomness-collective-flip 2.0.0", "srml-session 2.0.0", "srml-staking 2.0.0", "srml-staking-reward-curve 2.0.0", "srml-sudo 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "srml-system-rpc-runtime-api 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", + "srml-transaction-payment-rpc-runtime-api 2.0.0", "srml-treasury 2.0.0", - "substrate-authority-discovery-primitives 2.0.0", + "srml-utility 2.0.0", "substrate-client 2.0.0", "substrate-consensus-babe-primitives 2.0.0", "substrate-keyring 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", "substrate-session 2.0.0", - "substrate-wasm-builder-runner 1.0.3", + "substrate-wasm-builder-runner 1.0.4", ] [[package]] @@ -2509,11 +2792,12 @@ dependencies = [ "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", + "sr-primitives 2.0.0", "substrate-basic-authorship 2.0.0", "substrate-cli 2.0.0", "substrate-client 2.0.0", - "substrate-consensus-babe 2.0.0", - "substrate-consensus-babe-primitives 2.0.0", + "substrate-consensus-aura 2.0.0", + "substrate-consensus-aura-primitives 2.0.0", "substrate-executor 2.0.0", "substrate-finality-grandpa 2.0.0", "substrate-finality-grandpa-primitives 2.0.0", @@ -2538,21 +2822,23 @@ dependencies = [ "sr-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", - "srml-babe 2.0.0", + "srml-aura 2.0.0", "srml-balances 2.0.0", "srml-executive 2.0.0", "srml-grandpa 2.0.0", "srml-indices 2.0.0", + "srml-randomness-collective-flip 2.0.0", "srml-sudo 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-client 2.0.0", - "substrate-consensus-babe-primitives 2.0.0", + "substrate-consensus-aura-primitives 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", "substrate-session 2.0.0", - "substrate-wasm-builder-runner 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-wasm-builder-runner 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2574,6 +2860,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "srml-treasury 2.0.0", "substrate-client 2.0.0", "substrate-executor 2.0.0", @@ -2585,7 +2872,7 @@ dependencies = [ [[package]] name = "nodrop" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2677,7 +2964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.24" +version = "0.10.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2685,7 +2972,7 @@ dependencies = [ "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.62 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2695,7 +2982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.49" +version = "0.9.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2739,7 +3026,7 @@ dependencies = [ "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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "unsigned-varint 0.2.2 (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)", ] @@ -2754,7 +3041,7 @@ dependencies = [ "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.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2762,7 +3049,7 @@ name = "parity-scale-codec" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "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.2 (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)", @@ -2795,6 +3082,14 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-wasm" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-wasm" version = "0.40.3" @@ -2906,9 +3201,9 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2958,6 +3253,11 @@ name = "pkg-config" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ppv-lite86" version = "0.2.5" @@ -2981,7 +3281,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-serde 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.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "primitive-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2993,14 +3303,24 @@ dependencies = [ "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro-error" +version = "0.2.6" +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)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro-hack" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3013,7 +3333,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.4" +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)", @@ -3051,7 +3371,7 @@ name = "prost-derive" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (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)", @@ -3116,7 +3436,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3265,9 +3585,19 @@ dependencies = [ "libc 0.2.62 (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.51 (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.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_pcg" version = "0.1.2" @@ -3294,6 +3624,24 @@ dependencies = [ "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)", +] + +[[package]] +name = "raw-cpuid" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rayon" version = "1.2.0" @@ -3329,6 +3677,17 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_users" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + [[package]] name = "ref_thread_local" version = "0.0.0" @@ -3358,6 +3717,17 @@ name = "regex-syntax" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "region" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (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)", +] + [[package]] name = "remove_dir_all" version = "0.5.2" @@ -3408,6 +3778,16 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rust-argon2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -3441,12 +3821,12 @@ dependencies = [ [[package]] name = "rustversion" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3461,7 +3841,7 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -3500,7 +3880,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "merlin 1.2.1 (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)", @@ -3520,6 +3900,25 @@ name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scroll" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.9.5" +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)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sct" version = "0.5.0" @@ -3588,18 +3987,18 @@ name = "serde_derive" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3695,7 +4094,7 @@ dependencies = [ "chrono 0.4.9 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3731,8 +4130,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3775,12 +4174,12 @@ name = "sr-api-macros" version = "2.0.0" dependencies = [ "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (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 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)", - "rustversion 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-version 2.0.0", "substrate-client 2.0.0", @@ -3789,19 +4188,35 @@ dependencies = [ "substrate-state-machine 2.0.0", "substrate-test-runtime-client 2.0.0", "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "trybuild 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sr-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.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)", + "primitive-types 0.6.0 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-std 2.0.0", + "substrate-debug-derive 2.0.0", ] [[package]] name = "sr-io" version = "2.0.0" dependencies = [ - "environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", + "substrate-externalities 2.0.0", "substrate-primitives 2.0.0", "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", @@ -3812,16 +4227,14 @@ dependencies = [ name = "sr-primitives" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "integer-sqrt 0.1.2 (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)", "log 0.4.8 (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)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.5.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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-arithmetic 2.0.0", "sr-io 2.0.0", "sr-std 2.0.0", "substrate-application-crypto 2.0.0", @@ -3862,7 +4275,7 @@ dependencies = [ name = "sr-version" version = "2.0.0" dependencies = [ - "impl-serde 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)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", @@ -3926,7 +4339,7 @@ dependencies = [ name = "srml-authorship" version = "0.1.0" dependencies = [ - "impl-trait-for-tuples 0.1.2 (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-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", @@ -3973,6 +4386,7 @@ dependencies = [ "sr-std 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-keyring 2.0.0", "substrate-primitives 2.0.0", ] @@ -4010,6 +4424,7 @@ dependencies = [ "sr-sandbox 2.0.0", "sr-std 2.0.0", "srml-balances 2.0.0", + "srml-randomness-collective-flip 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", @@ -4018,6 +4433,33 @@ dependencies = [ "wasmi-validation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "srml-contracts-rpc" +version = "2.0.0" +dependencies = [ + "jsonrpc-core 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core-client 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-derive 13.2.0 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "srml-contracts-rpc-runtime-api 2.0.0", + "substrate-client 2.0.0", + "substrate-primitives 2.0.0", + "substrate-rpc-primitives 2.0.0", +] + +[[package]] +name = "srml-contracts-rpc-runtime-api" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "substrate-client 2.0.0", +] + [[package]] name = "srml-democracy" version = "2.0.0" @@ -4097,6 +4539,7 @@ dependencies = [ "srml-indices 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-primitives 2.0.0", ] @@ -4104,7 +4547,7 @@ dependencies = [ name = "srml-finality-tracker" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.2 (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-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", @@ -4158,6 +4601,7 @@ dependencies = [ "sr-primitives 2.0.0", "sr-staking-primitives 2.0.0", "sr-std 2.0.0", + "srml-authorship 0.1.0", "srml-session 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", @@ -4207,6 +4651,21 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-nicks" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-balances 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-offences" version = "1.0.0" @@ -4223,6 +4682,20 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-randomness-collective-flip" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-scored-pool" version = "1.0.0" @@ -4242,7 +4715,7 @@ dependencies = [ name = "srml-session" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.2 (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)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4287,10 +4760,10 @@ name = "srml-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.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)", "sr-primitives 2.0.0", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4312,7 +4785,8 @@ name = "srml-support" version = "2.0.0" dependencies = [ "bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-trait-for-tuples 0.1.2 (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)", + "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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4367,10 +4841,11 @@ dependencies = [ "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", + "sr-primitives 2.0.0", "srml-support 2.0.0", "substrate-inherents 2.0.0", "substrate-primitives 2.0.0", - "trybuild 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4378,7 +4853,7 @@ name = "srml-system" version = "2.0.0" dependencies = [ "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "impl-trait-for-tuples 0.1.2 (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-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4390,11 +4865,39 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-system-rpc" +version = "2.0.0" +dependencies = [ + "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core-client 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-derive 13.2.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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "srml-system-rpc-runtime-api 2.0.0", + "substrate-client 2.0.0", + "substrate-primitives 2.0.0", + "substrate-test-runtime-client 2.0.0", + "substrate-transaction-pool 2.0.0", +] + +[[package]] +name = "srml-system-rpc-runtime-api" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-client 2.0.0", +] + [[package]] name = "srml-timestamp" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.2 (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-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", @@ -4406,6 +4909,48 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-transaction-payment" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-balances 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "srml-transaction-payment-rpc-runtime-api 2.0.0", + "substrate-primitives 2.0.0", +] + +[[package]] +name = "srml-transaction-payment-rpc" +version = "2.0.0" +dependencies = [ + "jsonrpc-core 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core-client 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-derive 13.2.0 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "srml-transaction-payment-rpc-runtime-api 2.0.0", + "substrate-client 2.0.0", + "substrate-primitives 2.0.0", + "substrate-rpc-primitives 2.0.0", +] + +[[package]] +name = "srml-transaction-payment-rpc-runtime-api" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "substrate-client 2.0.0", +] + [[package]] name = "srml-treasury" version = "2.0.0" @@ -4421,6 +4966,21 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-utility" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-balances 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "stable_deref_trait" version = "1.1.1" @@ -4452,29 +5012,38 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "string-interner" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.2.18" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.2.18" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (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-error 0.2.6 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4497,7 +5066,7 @@ dependencies = [ name = "subkey" version = "2.0.0" dependencies = [ - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 2.0.0", @@ -4508,21 +5077,12 @@ dependencies = [ "sr-primitives 2.0.0", "srml-balances 2.0.0", "srml-system 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 2.0.0", "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "substrate" -version = "2.0.0" -dependencies = [ - "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "node-cli 2.0.0", - "vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "substrate-application-crypto" version = "2.0.0" @@ -4542,14 +5102,15 @@ version = "2.0.0" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.19 (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.12.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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "prost-build 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-authority-discovery-primitives 2.0.0", "substrate-client 2.0.0", @@ -4557,8 +5118,6 @@ dependencies = [ "substrate-peerset 2.0.0", "substrate-primitives 2.0.0", "substrate-test-runtime-client 2.0.0", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4603,9 +5162,9 @@ dependencies = [ name = "substrate-chain-spec" version = "2.0.0" dependencies = [ - "impl-trait-for-tuples 0.1.2 (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)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-chain-spec-derive 2.0.0", "substrate-network 2.0.0", @@ -4618,9 +5177,9 @@ name = "substrate-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.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4630,9 +5189,9 @@ 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.32.0 (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.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "exit-future 0.1.4 (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)", @@ -4642,9 +5201,9 @@ dependencies = [ "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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", - "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-client 2.0.0", "substrate-header-metadata 2.0.0", "substrate-keyring 2.0.0", @@ -4664,7 +5223,7 @@ name = "substrate-client" version = "2.0.0" dependencies = [ "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (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-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4679,6 +5238,7 @@ dependencies = [ "sr-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", + "substrate-client-db 2.0.0", "substrate-consensus-common 2.0.0", "substrate-executor 2.0.0", "substrate-header-metadata 2.0.0", @@ -4697,7 +5257,7 @@ dependencies = [ name = "substrate-client-db" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (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.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", "kvdb-memorydb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", @@ -4724,7 +5284,7 @@ name = "substrate-consensus-aura" version = "2.0.0" dependencies = [ "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4769,7 +5329,8 @@ dependencies = [ name = "substrate-consensus-babe" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (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-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4845,6 +5406,7 @@ dependencies = [ name = "substrate-consensus-pow" version = "2.0.0" dependencies = [ + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.19 (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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4861,6 +5423,7 @@ dependencies = [ name = "substrate-consensus-pow-primitives" version = "2.0.0" dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "substrate-client 2.0.0", @@ -4922,15 +5485,29 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "substrate-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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-executor" version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-native 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libsecp256k1 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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4938,6 +5515,7 @@ dependencies = [ "sr-io 2.0.0", "sr-version 2.0.0", "substrate-client 2.0.0", + "substrate-externalities 2.0.0", "substrate-offchain 2.0.0", "substrate-panic-handler 2.0.0", "substrate-primitives 2.0.0", @@ -4946,16 +5524,30 @@ dependencies = [ "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", "substrate-wasm-interface 2.0.0", + "test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-jit 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", +] + +[[package]] +name = "substrate-externalities" +version = "2.0.0" +dependencies = [ + "environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-std 2.0.0", + "substrate-primitives-storage 2.0.0", ] [[package]] name = "substrate-finality-grandpa" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "finality-grandpa 0.9.0 (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)", @@ -4964,7 +5556,7 @@ dependencies = [ "parity-scale-codec 1.0.6 (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.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "srml-finality-tracker 2.0.0", "substrate-client 2.0.0", @@ -4977,6 +5569,7 @@ dependencies = [ "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-primitives 2.0.0", + "substrate-state-machine 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5035,7 +5628,7 @@ dependencies = [ "hex 0.3.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)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-application-crypto 2.0.0", "substrate-primitives 2.0.0", "subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5050,7 +5643,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (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.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (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", @@ -5068,7 +5661,7 @@ dependencies = [ "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)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (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.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5086,7 +5679,7 @@ dependencies = [ "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (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.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5096,7 +5689,7 @@ name = "substrate-offchain" version = "2.0.0" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (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-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5104,6 +5697,7 @@ dependencies = [ "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)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (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)", @@ -5116,6 +5710,7 @@ dependencies = [ "substrate-primitives 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-transaction-pool 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)", ] @@ -5145,7 +5740,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (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.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5153,6 +5748,7 @@ name = "substrate-phragmen" version = "2.0.0" dependencies = [ "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -5172,8 +5768,9 @@ dependencies = [ "hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (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.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.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.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)", @@ -5188,13 +5785,27 @@ dependencies = [ "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-debug-derive 2.0.0", + "substrate-externalities 2.0.0", + "substrate-primitives-storage 2.0.0", "substrate-serializer 2.0.0", "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.5.0 (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.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-primitives-storage" +version = "2.0.0" +dependencies = [ + "impl-serde 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-std 2.0.0", + "substrate-debug-derive 2.0.0", +] + [[package]] name = "substrate-rpc" version = "2.0.0" @@ -5207,8 +5818,9 @@ dependencies = [ "jsonrpc-pubsub 13.2.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.0.6 (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.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-version 2.0.0", @@ -5240,7 +5852,7 @@ dependencies = [ "parity-scale-codec 1.0.6 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-version 2.0.0", "substrate-primitives 2.0.0", "substrate-rpc-primitives 2.0.0", @@ -5265,7 +5877,7 @@ dependencies = [ "jsonrpc-ws-server 13.2.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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", ] @@ -5278,7 +5890,7 @@ dependencies = [ "sr-sandbox 2.0.0", "sr-std 2.0.0", "substrate-primitives 2.0.0", - "substrate-wasm-builder-runner 1.0.3", + "substrate-wasm-builder-runner 1.0.4", ] [[package]] @@ -5286,7 +5898,7 @@ name = "substrate-serializer" version = "2.0.0" dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5306,13 +5918,11 @@ dependencies = [ "parity-scale-codec 1.0.6 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "substrate-application-crypto 2.0.0", - "substrate-authority-discovery 2.0.0", - "substrate-authority-discovery-primitives 2.0.0", "substrate-chain-spec 2.0.0", "substrate-client 2.0.0", "substrate-client-db 2.0.0", @@ -5333,6 +5943,7 @@ dependencies = [ "substrate-transaction-pool 2.0.0", "sysinfo 0.9.5 (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.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5341,7 +5952,7 @@ dependencies = [ name = "substrate-service-test" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (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-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5370,7 +5981,7 @@ dependencies = [ name = "substrate-state-db" version = "2.0.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5388,6 +5999,7 @@ dependencies = [ "parity-scale-codec 1.0.6 (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)", + "substrate-externalities 2.0.0", "substrate-panic-handler 2.0.0", "substrate-primitives 2.0.0", "substrate-trie 2.0.0", @@ -5450,6 +6062,7 @@ dependencies = [ "srml-executive 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "srml-system-rpc-runtime-api 2.0.0", "srml-timestamp 2.0.0", "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", @@ -5464,7 +6077,7 @@ dependencies = [ "substrate-state-machine 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-trie 2.0.0", - "substrate-wasm-builder-runner 1.0.3", + "substrate-wasm-builder-runner 1.0.4", "trie-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5484,8 +6097,9 @@ name = "substrate-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.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.19 (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.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5533,7 +6147,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "build-helper 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5541,15 +6155,16 @@ dependencies = [ "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (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)", ] [[package]] name = "substrate-wasm-builder-runner" -version = "1.0.3" +version = "1.0.4" [[package]] name = "substrate-wasm-builder-runner" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -5581,10 +6196,10 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.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)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5600,6 +6215,17 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synstructure" +version = "0.12.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)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sysinfo" version = "0.9.5" @@ -5617,6 +6243,16 @@ name = "take_mut" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "target-lexicon" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "target_info" version = "0.1.0" @@ -5652,9 +6288,21 @@ dependencies = [ "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "test-case" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "textwrap" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5668,6 +6316,14 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "threadpool" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" version = "0.1.42" @@ -5683,7 +6339,7 @@ name = "tiny-bip39" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", @@ -5706,7 +6362,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5724,7 +6380,7 @@ dependencies = [ "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.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (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.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5816,7 +6472,7 @@ dependencies = [ "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5826,7 +6482,7 @@ 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)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.15.2 (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.19.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5834,7 +6490,7 @@ dependencies = [ [[package]] name = "tokio-sync" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5848,7 +6504,7 @@ 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)", - "iovec 0.1.2 (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.19 (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.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5912,7 +6568,7 @@ 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)", - "iovec 0.1.2 (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.62 (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.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5971,7 +6627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "elastic-array 0.10.2 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.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)", ] @@ -6000,13 +6656,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "trybuild" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" 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.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (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.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -6103,7 +6759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unsigned-varint" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6152,7 +6808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6160,6 +6816,11 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -6172,7 +6833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -6228,9 +6889,9 @@ dependencies = [ "bumpalo 2.6.0 (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.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -6260,9 +6921,9 @@ name = "wasm-bindgen-macro-support" version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -6277,19 +6938,29 @@ name = "wasm-bindgen-webidl" version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (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.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasm-gc-api" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + [[package]] name = "wasm-timer" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6321,12 +6992,101 @@ dependencies = [ "parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasmparser" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasmtime-debug" +version = "0.2.0" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", +] + +[[package]] +name = "wasmtime-environ" +version = "0.2.0" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (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)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (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.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)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (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)", + "toml 0.5.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.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmtime-jit" +version = "0.2.0" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" +dependencies = [ + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (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.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-debug 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", +] + +[[package]] +name = "wasmtime-runtime" +version = "0.2.0" +source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.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)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "web-sys" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys 0.3.28 (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.51 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6387,7 +7147,7 @@ name = "which" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -6523,12 +7283,39 @@ dependencies = [ "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "zstd" +version = "0.4.28+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-safe" +version = "1.4.13+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-sys" +version = "1.4.13+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (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.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "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.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b58aeefd9396419a4f4f2b9778f2d832a11851b55010e231c5390cf2b1c416b4" +"checksum ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "b35dfc96a657c1842b4eb73180b65e37152d4b94d0eb5cb51708aee7826950b4" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum aio-limited 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c4dddf55b0b2da9acb7512f21c0a4f1c0871522ec4ab7fb919d0da807d1e32b3" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -6536,9 +7323,9 @@ dependencies = [ "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" -"checksum asn1_der 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bea40e881533b1fe23afca9cd1c1ca022219a10fce604099ecfc96bfa26eaf1a" -"checksum asn1_der_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7f92edafad155aff997fa5b727c6429b91e996b5a5d62a2b0adbae1306b5fe" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"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 atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" @@ -6547,12 +7334,14 @@ dependencies = [ "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.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92" "checksum bindgen 0.47.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df683a55b54b41d5ea8ebfaebb5aa7e6b84e3f3006a78f010dadc9ca88469260" "checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" "checksum bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9633b74910e1870f50f5af189b08487195cdb83c0e27a71d6f64d5e09dd0538b" "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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" "checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" "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" @@ -6576,18 +7365,30 @@ dependencies = [ "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ef0c1bcf2e99c649104bd7a7012d8f8802684400e03db0ec0af48583c6fa0e4" -"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"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.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18c97588946d3e5fe11f8e34ebf8cc65fd3fda50f3ffa2e80c98b2748058f00f" +"checksum cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3255935da50302bcb0f7109f2fef27f44b46f1c797dfa7db971379261023adcd" +"checksum cranelift-codegen-meta 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd57265ef5e6ff253c378b6261ed8c2e6cb1b15e91624540dbd09b1e5a40e9ca" +"checksum cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c093398d21f9493ab29445191362592ef621f497e56a8efb15bdf80471978b7a" +"checksum cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e915fa58d2a75e3c4b768b7e4760282889915c3fcd9ccb2ad2b3ebec99654a78" +"checksum cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46963952cda267bd0177b3f036e50038cd56e7b4c5b09a455b02df727e0f2a16" +"checksum cranelift-native 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7ba8a2d69ddd4729199a321bc2f4020e1969a088b468ed6a29dc7a69350be76e" +"checksum cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5a802357a6a016bf4c1dcdc6d73a650640eb3b613cc098a1a044a6c3731ca264" "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.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" @@ -6608,23 +7409,32 @@ dependencies = [ "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" "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 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d07e8b8a8386c3b89a7a4b329fdfa4cb545de2545e9e2ebbc3dd3929253e426" -"checksum ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" +"checksum ed25519-dalek 1.0.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)" = "845aaacc16f01178f33349e7c992ecd0cee095aa5e577f0f4dee35971bd36455" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +"checksum env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39ecdb7dd54465526f0a56d666e3b2dd5f3a218665a030b6e4ad9e70fa95d8fa" "checksum environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34f8467a0284de039e6bd0e25c14519538462ba5beb548bb1f03e645097837a8" "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 error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" "checksum exit-future 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d8013f441e38e31c670e7f34ec8f1d5d3a2bd9d303c1ff83976ca886005e8f48" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "875d78b92b2a4d9e1e2c7eeccfa30a327d2ee6434db3beb8fd6fd92f41898bc4" +"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.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9681c1f75941ea47584573dd2bc10558b2067d460612945887e00744e43393be" "checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e" +"checksum fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6357b15872f8126e4ea7cf79d579473f132ccd2de239494ad1bf4aa892faea68" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" @@ -6651,14 +7461,16 @@ dependencies = [ "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.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"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.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "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.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bcea5b597dd98e6d1f1ec171744cc5dee1a30d1c23c5b98e3cf9d4fbdf8a526" +"checksum hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6587d09be37fb98a11cb08b9000a3f592451c1b1b613ca69d949160e313a430a" "checksum hashmap_core 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6852e5a86250521973b0c1d39677166d8a9c0047c908d7e04f1aa04177973c" "checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" @@ -6679,13 +7491,13 @@ dependencies = [ "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.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa0086251524c50fd53b32e7b05eb6d79e2f97221eaf0c53c0ca9c3096f21d3" -"checksum impl-serde 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbb1ea6188aca47a0eaeeb330d8a82f16cd500f30b897062d23922568727333a" -"checksum impl-trait-for-tuples 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6947b372790f8948f439bb6aaa6baabdf80be1a207a477ff072f83fb793e428f" +"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.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" "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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum ipnet 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e61c2da0d0f700c77d2d313dbf4f93e41d235fa12c6681fee06621036df4c2af" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum ipnet 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc15ac2e0886d62ba078989ef6920ab23997ab0b04ca5687f1a9a7484296a48" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "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" @@ -6732,7 +7544,7 @@ dependencies = [ "checksum libp2p-yamux 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a37bed07c8ee0ceeecdfb90d703aa6b1cec99a69b4157e5f7f2c03acacbfca15" "checksum librocksdb-sys 5.18.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d19778314deaa7048f2ea7d07b8aa12e1c227acebe975a37eeab6d2f8c74e41b" "checksum libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "688e8d65e495567c2c35ea0001b26b9debf0b4ea11f8cccc954233b75fc3428a" -"checksum libsecp256k1 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf0a4113e7b18b72b9b65d5b35335d99865ef059034426e4b85ad63adddf996" +"checksum libsecp256k1 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63cc09b49bf0cc55885982347b174ad89855e97a12284d2c9dcc6da2e20c28f5" "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" @@ -6742,6 +7554,7 @@ dependencies = [ "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-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +"checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" "checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" @@ -6750,7 +7563,7 @@ dependencies = [ "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum merlin 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "de2d16d3b15fec5943d1144f861f61f279d165fdd60998ca262913b9bf1c8adb" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10" +"checksum miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "304f66c19be2afa56530fa7c39796192eef38618da8d19df725ad7c6d6b2aaae" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" @@ -6761,7 +7574,7 @@ dependencies = [ "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.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"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" @@ -6773,9 +7586,9 @@ dependencies = [ "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 opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" +"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884" +"checksum openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ba24190c8f0805d3bd2ce028f439fe5af1d55882bbe6261bed1dbc93b50dd6b1" "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.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)" = "" @@ -6785,6 +7598,7 @@ dependencies = [ "checksum parity-scale-codec-derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42af752f59119656fa3cb31e8852ed24e895b968c0bdb41847da7f0cea6d155f" "checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" "checksum parity-util-mem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2005637ccf93dbb60c85081ccaaf3f945f573da48dcc79f27f9646caa3ec1dc" +"checksum parity-wasm 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16ad52817c4d343339b3bc2e26861bd21478eda0b7509acf83505727000512ac" "checksum parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1e39faaa292a687ea15120b1ac31899b13586446521df6c149e46f1584671e0f" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" @@ -6804,13 +7618,16 @@ dependencies = [ "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" +"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83ef7b3b965c0eadcb6838f34f827e1dfb2939bdd5ebd43f9647e009b12b0371" +"checksum primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b5a08dda18910f056e5c2060c034e77cab18e0bd7d895e44f03207af4c71d5" "checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" +"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" "checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" +"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-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" @@ -6837,29 +7654,35 @@ dependencies = [ "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 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "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 ref_thread_local 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6" "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 rhododendron 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36542aafc2429a4c010fafa079a20dee953b663cb2427f51d86cf1d436846b4d" "checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" "checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e" "checksum rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f072d931f11a96546efd97642e1e75e807345aced86b947f9239102f262d0fcd" +"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.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f271e3552cd835fa28c541c34a7e8fdd8cdff09d77fe4eb8f6c42e87a11b096e" -"checksum rustversion 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b48139cfc215c6cc70d43c6c555a59e723c3b5adb26a4cfa09f815a5ae5871e8" +"checksum rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c48f91977f4ef3be5358c15d131d3f663f6b4d7a112555bf3bf52ad23b6659e5" "checksum rw-stream-sink 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9cbe61c20455d3015b2bb7be39e1872310283b8e5a52f5b242b0ac7581fe78" -"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19d2271fa48eaf61e53cc88b4ad9adcbafa2d512c531e7fadb6dc11a4d3656c5" "checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" "checksum safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b08423011dae9a5ca23f07cf57dac3857f5c885d352b76f6d95f4aea9434d0" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" @@ -6867,6 +7690,8 @@ dependencies = [ "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.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" "checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" @@ -6876,7 +7701,7 @@ dependencies = [ "checksum send_wrapper 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" "checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" -"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "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.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" @@ -6899,26 +7724,31 @@ dependencies = [ "checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362" "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 strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" -"checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" +"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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4f66a4c0ddf7aee4677995697366de0749b0139057342eccbb609b12d0affc" +"checksum structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fe0c13e476b4e21ff7f5c4ace3818b6d7bdc16897c31c73862471bc1663acae" "checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" "checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" "checksum substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" -"checksum substrate-wasm-builder-runner 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af21b27fad38b212c1919f700cb0def33c88cde14d22e0d1b17d4521f4eb8b40" +"checksum substrate-wasm-builder-runner 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bd48273fe9d7f92c1f7d6c1c537bb01c8068f925b47ad2cd8367e11dc32f8550" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum sysinfo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bd3b813d94552a8033c650691645f8dd5a63d614dddd62428a95d3931ef7b6" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +"checksum target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7975cb2c6f37d77b190bc5004a2bb015971464756fde9514651a525ada2a741a" "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 textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"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 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" @@ -6933,7 +7763,7 @@ dependencies = [ "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" "checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" "checksum tokio-rustls 0.10.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3e5cebc3ca33110e460c4d2e7c5e863b159fadcbf125449d896720695b2af709" -"checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" +"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" "checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" "checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" @@ -6947,7 +7777,7 @@ dependencies = [ "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.15 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc69705e261edf3b9a87012b2353a49f49c0465186b5ab96e95f6983d6984aa" +"checksum trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "e6851bf8351876984fbab8a2391de6378947b898410d8714edd12164d2137127" "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" @@ -6961,7 +7791,7 @@ dependencies = [ "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2c64cdf40b4a9645534a943668681bcb219faf51874d4b65d2e0abda1b10a2ab" +"checksum unsigned-varint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f0023a96687fe169081e8adce3f65e3874426b7886e9234d490af2dc077959" "checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" "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" @@ -6969,6 +7799,7 @@ dependencies = [ "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" @@ -6982,9 +7813,15 @@ dependencies = [ "checksum wasm-bindgen-macro-support 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9c075d27b7991c68ca0f77fe628c3513e64f8c477d422b859e03f28751b46fc5" "checksum wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "83d61fe986a7af038dd8b5ec660e5849cbd9f38e7492b9404cc48b2b4df731d1" "checksum wasm-bindgen-webidl 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9b979afb0535fe4749906a674082db1211de8aef466331d43232f63accb7c07c" -"checksum wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6101df9a5987df809216bdda7289f52b58128e6b6a6546e9ee3e6b632b4921" +"checksum wasm-gc-api 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c32691b6c7e6c14e7f8fd55361a9088b507aa49620fcd06c09b3a1082186b9" +"checksum wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac" "checksum wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f31d26deb2d9a37e6cfed420edce3ed604eab49735ba89035e13c98f9a528313" "checksum wasmi-validation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc0356e3df56e639fc7f7d8a99741915531e27ed735d911ed83d7e1339c8188" +"checksum wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5083b449454f7de0b15f131eee17de54b5a71dcb9adcf11df2b2f78fad0cd82" +"checksum wasmtime-debug 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" +"checksum wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" +"checksum wasmtime-jit 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" +"checksum wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "" "checksum web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "c84440699cd02ca23bed6f045ffb1497bc18a3c2628bd13e2093186faaaacf6b" "checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" "checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" @@ -7007,3 +7844,6 @@ dependencies = [ "checksum zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4090487fa66630f7b166fba2bbb525e247a5449f41c468cc1d98f8ae6ac03120" "checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" "checksum zeroize_derive 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e" +"checksum zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4e716acaad66f2daf2526f37a1321674a8814c0b37a366ebe6c97a699f85ddc" +"checksum zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfe4d3b26a0790201848865663e8ffabf091e126e548bc9710ccfa95621ece48" +"checksum zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fadc8ebe858f056ab82dffb9d93850b841603bdf663db7cf5e3dbd7f34cc55b2" diff --git a/Cargo.toml b/Cargo.toml index a345b880da7ad716d8271a3ab551cd2274717fef..5e1238866b87632f5b78cf7feaac0daf023a8d1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,3 @@ -[[bin]] -name = "substrate" -path = "node/src/main.rs" - -[package] -name = "substrate" -version = "2.0.0" -authors = ["Parity Technologies "] -build = "build.rs" -edition = "2018" - -[dependencies] -cli = { package = "node-cli", path = "node/cli" } -futures = "0.1.29" -ctrlc = { version = "3.1.3", features = ["termination"] } - -[build-dependencies] -vergen = "3.0.4" - [workspace] members = [ "core/authority-discovery", @@ -36,6 +17,7 @@ members = [ "core/consensus/pow", "core/executor", "core/executor/runtime-test", + "core/externalities", "core/finality-grandpa", "core/finality-grandpa/primitives", "core/inherents", @@ -52,6 +34,7 @@ members = [ "core/service/test", "core/session", "core/sr-api-macros", + "core/sr-arithmetic", "core/sr-io", "core/sr-primitives", "core/sr-staking-primitives", @@ -80,6 +63,7 @@ members = [ "srml/aura", "srml/balances", "srml/contracts", + "srml/contracts/rpc", "srml/collective", "srml/democracy", "srml/elections", @@ -94,15 +78,21 @@ members = [ "srml/indices", "srml/membership", "srml/metadata", + "srml/nicks", "srml/offences", + "srml/randomness-collective-flip", "srml/scored-pool", "srml/session", "srml/staking", "srml/staking/reward-curve", "srml/sudo", "srml/system", + "srml/system/rpc", "srml/timestamp", "srml/treasury", + "srml/transaction-payment", + "srml/transaction-payment/rpc", + "srml/utility", "node/cli", "node/executor", "node/primitives", @@ -115,12 +105,7 @@ members = [ "test-utils/chain-spec-builder", ] -[badges] -travis-ci = { repository = "paritytech/substrate", branch = "master" } -maintenance = { status = "actively-developed" } -is-it-maintained-issue-resolution = { repository = "paritytech/substrate" } -is-it-maintained-open-issues = { repository = "paritytech/substrate" } - [profile.release] # Substrate runtime requires unwinding. panic = "unwind" + diff --git a/Dockerfile b/Dockerfile index 0271db8d1461d7a8c58fb52f9f6306e5ca833e2e..7cba85c544afc2c8cc1ff56401b2172a01d30364 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,20 +5,21 @@ FROM phusion/baseimage:0.10.2 as builder LABEL maintainer="chevdor@gmail.com" LABEL description="This is the build stage for Substrate. Here we create the binary." +ENV DEBIAN_FRONTEND=noninteractive + ARG PROFILE=release WORKDIR /substrate COPY . /substrate RUN apt-get update && \ - apt-get dist-upgrade -y && \ + apt-get dist-upgrade -y -o Dpkg::Options::="--force-confold" && \ apt-get install -y cmake pkg-config libssl-dev git clang RUN curl https://sh.rustup.rs -sSf | sh -s -- -y && \ export PATH="$PATH:$HOME/.cargo/bin" && \ rustup toolchain install nightly && \ rustup target add wasm32-unknown-unknown --toolchain nightly && \ - cargo install --git https://github.com/alexcrichton/wasm-gc && \ rustup default nightly && \ rustup default stable && \ cargo build "--$PROFILE" diff --git a/README.adoc b/README.adoc index 39b0912b7d3cbbb723d1ec81f683cdd825279b26..bb23958bced1e0fb2166a28bba738eb9468381cc 100644 --- a/README.adoc +++ b/README.adoc @@ -20,13 +20,13 @@ See also https://www.parity.io/what-is-substrate/. Substrate is still an early stage project, and while it has already been used as the basis of major projects like Polkadot, using it is still a significant undertaking. In particular, you should have a good knowledge of blockchain concepts and basic cryptography. Terminology like header, block, client, hash, transaction and signature should be familiar. At present you will need a working knowledge of Rust to be able to do anything interesting (though eventually, we aim for this not to be the case). -Substrate is designed to be used in one of three ways: +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 customisability, primarily allowing you to change the genesis parameters of the various included runtime modules such as balances, staking, block-period, fees and governance. +**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 modules from the Substrate Runtime Module Library 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 own 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 modules from the Substrate Runtime Module Library (SRML) 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. -3. Generic: The entire Substrate Runtime Module Library can be ignored and the entire runtime designed and implemented from scratch. If desired, this can be done in a language other than Rust, providing it can target WebAssembly. If the runtime can be made to be 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. +**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 @@ -184,7 +184,6 @@ curl https://sh.rustup.rs -sSf | sh rustup update nightly rustup target add wasm32-unknown-unknown --toolchain nightly rustup update stable -cargo install --git https://github.com/alexcrichton/wasm-gc ---- You will also need to install the following packages: @@ -231,13 +230,9 @@ If you are trying to set up Substrate on Windows, you should do the following: rustup update stable rustup target add wasm32-unknown-unknown --toolchain nightly -4. Next, you install wasm-gc, which is used to slim down Wasm files: +4. Then, you need to install LLVM: https://releases.llvm.org/download.html - cargo install --git https://github.com/alexcrichton/wasm-gc --force - -5. Then, you need to install LLVM: https://releases.llvm.org/download.html - -6. Next, you need to install OpenSSL, which we will do with `vcpkg`: +5. Next, you need to install OpenSSL, which we will do with `vcpkg`: mkdir \Tools cd \Tools @@ -246,14 +241,14 @@ If you are trying to set up Substrate on Windows, you should do the following: .\bootstrap-vcpkg.bat .\vcpkg.exe install openssl:x64-windows-static -7. After, you need to add OpenSSL to your System Variables: +6. After, you need to add OpenSSL to your System Variables. Note that in order for the following commands to work, you need to use Windows Powershell: $env:OPENSSL_DIR = 'C:\Tools\vcpkg\installed\x64-windows-static' $env:OPENSSL_STATIC = 'Yes' [System.Environment]::SetEnvironmentVariable('OPENSSL_DIR', $env:OPENSSL_DIR, [System.EnvironmentVariableTarget]::User) [System.Environment]::SetEnvironmentVariable('OPENSSL_STATIC', $env:OPENSSL_STATIC, [System.EnvironmentVariableTarget]::User) -8. Finally, you need to install `cmake`: https://cmake.org/download/ +7. Finally, you need to install `cmake`: https://cmake.org/download/ ==== Shared Steps @@ -423,7 +418,7 @@ curl -H 'Content-Type: application/json' --data '{ "jsonrpc":"2.0", "method":"au === Viewing documentation for Substrate packages You can generate documentation for a Substrate Rust package and have it automatically open in your web browser using https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html#using-rustdoc-with-cargo[rustdoc with Cargo], -(of the The Rustdoc Book), by running the the following command: +(of the The Rustdoc Book), by running the following command: ``` cargo doc --package --open diff --git a/core/application-crypto/src/ed25519.rs b/core/application-crypto/src/ed25519.rs index d5c4fd7badabf161dfba2619a82096bdfcb58c40..ac01cef61b1c5d469b23129eb591567d7f122b68 100644 --- a/core/application-crypto/src/ed25519.rs +++ b/core/application-crypto/src/ed25519.rs @@ -23,6 +23,10 @@ pub use primitives::ed25519::*; mod app { use primitives::testing::ED25519; crate::app_crypto!(super, ED25519); + + impl crate::traits::BoundToRuntimeAppPublic for Public { + type Public = Self; + } } pub use app::Public as AppPublic; @@ -53,14 +57,7 @@ impl RuntimePublic for Public { #[cfg(test)] mod tests { use sr_primitives::{generic::BlockId, traits::ProvideRuntimeApi}; - use primitives::{ - testing::{ - KeyStore, - ED25519, - }, - crypto::Pair, - traits::BareCryptoStore as _, - }; + use primitives::{testing::{KeyStore, ED25519}, crypto::Pair}; use test_client::{ TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, runtime::{TestAPI, app_crypto::ed25519::{AppPair, AppPublic}}, diff --git a/core/application-crypto/src/lib.rs b/core/application-crypto/src/lib.rs index e3366a461a064ceac0d26ea3485b0ec69e5e2aba..b4ada5425b40042184c63c3b327febdea46a759b 100644 --- a/core/application-crypto/src/lib.rs +++ b/core/application-crypto/src/lib.rs @@ -21,7 +21,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #[doc(hidden)] -pub use primitives::{self, crypto::{CryptoType, Public, Derive, IsWrappedBy, Wraps}}; +pub use primitives::{self, crypto::{CryptoType, Public, Derive, IsWrappedBy, Wraps}, RuntimeDebug}; #[doc(hidden)] #[cfg(feature = "std")] pub use primitives::crypto::{SecretStringError, DeriveJunction, Ss58Codec, Pair}; @@ -88,22 +88,13 @@ macro_rules! app_crypto { } fn derive< Iter: Iterator - >(&self, path: Iter) -> Result { - self.0.derive(path).map(Self) + >(&self, path: Iter, seed: Option) -> Result<(Self, Option), Self::DeriveError> { + self.0.derive(path, seed).map(|x| (Self(x.0), x.1)) } fn from_seed(seed: &Self::Seed) -> Self { Self(<$pair>::from_seed(seed)) } fn from_seed_slice(seed: &[u8]) -> Result { <$pair>::from_seed_slice(seed).map(Self) } - fn from_standard_components< - I: Iterator - >( - seed: &str, - password: Option<&str>, - path: I, - ) -> Result { - <$pair>::from_standard_components::(seed, password, path).map(Self) - } fn sign(&self, msg: &[u8]) -> Self::Signature { Signature(self.0.sign(msg)) } @@ -139,10 +130,12 @@ macro_rules! app_crypto { $crate::wrap!{ /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. #[derive( - Clone, Default, Eq, PartialEq, Ord, PartialOrd, $crate::codec::Encode, + Clone, Default, Eq, PartialEq, Ord, PartialOrd, + $crate::codec::Encode, $crate::codec::Decode, + $crate::RuntimeDebug, )] - #[cfg_attr(feature = "std", derive(Debug, Hash))] + #[cfg_attr(feature = "std", derive(Hash))] pub struct Public($public); } @@ -239,8 +232,12 @@ macro_rules! app_crypto { $crate::wrap! { /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. - #[derive(Clone, Default, Eq, PartialEq, $crate::codec::Encode, $crate::codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug, Hash))] + #[derive(Clone, Default, Eq, PartialEq, + $crate::codec::Encode, + $crate::codec::Decode, + $crate::RuntimeDebug, + )] + #[cfg_attr(feature = "std", derive(Hash))] pub struct Signature($sig); } diff --git a/core/application-crypto/src/sr25519.rs b/core/application-crypto/src/sr25519.rs index 93565a628ffb60577a5cb3289a9dff335e60ea93..c343a2e1676b536eb05165ac70898639a4628c03 100644 --- a/core/application-crypto/src/sr25519.rs +++ b/core/application-crypto/src/sr25519.rs @@ -23,6 +23,10 @@ pub use primitives::sr25519::*; mod app { use primitives::testing::SR25519; crate::app_crypto!(super, SR25519); + + impl crate::traits::BoundToRuntimeAppPublic for Public { + type Public = Self; + } } pub use app::Public as AppPublic; @@ -53,14 +57,7 @@ impl RuntimePublic for Public { #[cfg(test)] mod tests { use sr_primitives::{generic::BlockId, traits::ProvideRuntimeApi}; - use primitives::{ - testing::{ - KeyStore, - SR25519, - }, - crypto::Pair, - traits::BareCryptoStore as _, - }; + use primitives::{testing::{KeyStore, SR25519}, crypto::Pair}; use test_client::{ TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, runtime::{TestAPI, app_crypto::sr25519::{AppPair, AppPublic}}, diff --git a/core/application-crypto/src/traits.rs b/core/application-crypto/src/traits.rs index aad076bd90012e1ba4e1ebccd47f32101085648d..db71f3072af5841ca2c1309c77b31875f0021838 100644 --- a/core/application-crypto/src/traits.rs +++ b/core/application-crypto/src/traits.rs @@ -14,10 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use primitives::crypto::{KeyTypeId, CryptoType, IsWrappedBy, Public}; #[cfg(feature = "std")] use primitives::crypto::Pair; + use codec::Codec; +use primitives::crypto::{KeyTypeId, CryptoType, IsWrappedBy, Public}; +use rstd::fmt::Debug; /// An application-specific key. pub trait AppKey: 'static + Send + Sync + Sized + CryptoType + Clone { @@ -38,23 +40,25 @@ pub trait AppKey: 'static + Send + Sync + Sized + CryptoType + Clone { const ID: KeyTypeId; } -/// Type which implements Debug and Hash in std, not when no-std (std variant). +/// Type which implements Hash in std, not when no-std (std variant). #[cfg(feature = "std")] -pub trait MaybeDebugHash: std::fmt::Debug + std::hash::Hash {} +pub trait MaybeHash: std::hash::Hash {} #[cfg(feature = "std")] -impl MaybeDebugHash for T {} +impl MaybeHash for T {} -/// Type which implements Debug and Hash in std, not when no-std (no-std variant). +/// Type which implements Hash in std, not when no-std (no-std variant). #[cfg(not(feature = "std"))] -pub trait MaybeDebugHash {} +pub trait MaybeHash {} #[cfg(not(feature = "std"))] -impl MaybeDebugHash for T {} +impl MaybeHash for T {} /// A application's public key. -pub trait AppPublic: AppKey + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec { +pub trait AppPublic: + AppKey + Public + Ord + PartialOrd + Eq + PartialEq + Debug + MaybeHash + codec::Codec +{ /// The wrapped type which is just a plain instance of `Public`. type Generic: - IsWrappedBy + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec; + IsWrappedBy + Public + Ord + PartialOrd + Eq + PartialEq + Debug + MaybeHash + codec::Codec; } /// A application's key pair. @@ -65,15 +69,15 @@ pub trait AppPair: AppKey + Pair::Public> { } /// A application's signature. -pub trait AppSignature: AppKey + Eq + PartialEq + MaybeDebugHash { +pub trait AppSignature: AppKey + Eq + PartialEq + Debug + MaybeHash { /// The wrapped type which is just a plain instance of `Signature`. - type Generic: IsWrappedBy + Eq + PartialEq + MaybeDebugHash; + type Generic: IsWrappedBy + Eq + PartialEq + Debug + MaybeHash; } /// A runtime interface for a public key. pub trait RuntimePublic: Sized { /// The signature that will be generated when signing with the corresponding private key. - type Signature: Codec + MaybeDebugHash + Eq + PartialEq + Clone; + type Signature: Codec + Debug + MaybeHash + Eq + PartialEq + Clone; /// Returns all public keys for the given key type in the keystore. fn all(key_type: KeyTypeId) -> crate::Vec; @@ -101,7 +105,7 @@ pub trait RuntimeAppPublic: Sized { const ID: KeyTypeId; /// The signature that will be generated when signing with the corresponding private key. - type Signature: Codec + MaybeDebugHash + Eq + PartialEq + Clone; + type Signature: Codec + Debug + MaybeHash + Eq + PartialEq + Clone; /// Returns all public keys for this application in the keystore. fn all() -> crate::Vec; @@ -122,3 +126,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; } + +/// Something that bound to a fixed `RuntimeAppPublic`. +pub trait BoundToRuntimeAppPublic { + /// The `RuntimeAppPublic` this type is bound to. + type Public: RuntimeAppPublic; +} diff --git a/core/authority-discovery/Cargo.toml b/core/authority-discovery/Cargo.toml index a74549eb456ada7359d0422c256ffa3841325770..c4cab438f400720a1b41ef5ce45075b44e0702dc 100644 --- a/core/authority-discovery/Cargo.toml +++ b/core/authority-discovery/Cargo.toml @@ -14,18 +14,17 @@ bytes = "0.4.12" client = { package = "substrate-client", path = "../../core/client" } codec = { package = "parity-scale-codec", default-features = false, version = "1.0.3" } derive_more = "0.15.0" -futures = "0.1.29" +futures-preview = "0.3.0-alpha.19" libp2p = { version = "0.12.0", default-features = false, features = ["secp256k1", "libp2p-websocket"] } log = "0.4.8" network = { package = "substrate-network", path = "../../core/network" } primitives = { package = "substrate-primitives", path = "../primitives" } prost = "0.5.0" -serde_json = "1.0.40" +serde_json = "1.0.41" sr-primitives = { path = "../../core/sr-primitives" } -tokio-timer = "0.2.11" +futures-timer = "0.4" [dev-dependencies] parking_lot = "0.9.0" peerset = { package = "substrate-peerset", path = "../../core/peerset" } test-client = { package = "substrate-test-runtime-client", path = "../../core/test-runtime/client" } -tokio = "0.1.22" diff --git a/core/authority-discovery/primitives/src/lib.rs b/core/authority-discovery/primitives/src/lib.rs index 13da4de0204664c4b8aa94a6991b6793945dd922..7c56dc6ca4ca6b26c97be661b36370a174a3916a 100644 --- a/core/authority-discovery/primitives/src/lib.rs +++ b/core/authority-discovery/primitives/src/lib.rs @@ -20,12 +20,13 @@ use client::decl_runtime_apis; use rstd::vec::Vec; +use sr_primitives::RuntimeDebug; -#[derive(codec::Encode, codec::Decode, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Hash))] +#[derive(codec::Encode, codec::Decode, Eq, PartialEq, Clone, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] pub struct Signature(pub Vec); -#[derive(codec::Encode, codec::Decode, Eq, PartialEq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Hash))] +#[derive(codec::Encode, codec::Decode, Eq, PartialEq, Clone, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] pub struct AuthorityId(pub Vec); decl_runtime_apis! { diff --git a/core/authority-discovery/src/error.rs b/core/authority-discovery/src/error.rs index e8c1ad9705f0c6accbf8edc3b699be5ad017c5f6..dca50cc0beb9eeaf8094bbfb2c35d612500497b0 100644 --- a/core/authority-discovery/src/error.rs +++ b/core/authority-discovery/src/error.rs @@ -42,6 +42,4 @@ pub enum Error { Decoding(prost::DecodeError), /// Failed to parse a libp2p multi address. ParsingMultiaddress(libp2p::core::multiaddr::Error), - /// Tokio timer error. - PollingTokioTimer(tokio_timer::Error) } diff --git a/core/authority-discovery/src/lib.rs b/core/authority-discovery/src/lib.rs index 987169ead90b11077f8d6a3d43af1a7a475e66eb..ce6445d08fb126df4c241e9eda727548f59a2577 100644 --- a/core/authority-discovery/src/lib.rs +++ b/core/authority-discovery/src/lib.rs @@ -42,23 +42,29 @@ //! 3. Validates the signatures of the retrieved key value pairs. //! //! 4. Adds the retrieved external addresses as priority nodes to the peerset. +use std::collections::{HashMap, HashSet}; +use std::convert::TryInto; +use std::iter::FromIterator; +use std::marker::PhantomData; +use std::pin::Pin; +use std::sync::Arc; +use std::time::Duration; + +use futures::channel::mpsc::Receiver; +use futures::stream::StreamExt; +use futures::task::{Context, Poll}; +use futures::Future; +use futures_timer::Interval; use authority_discovery_primitives::{AuthorityDiscoveryApi, AuthorityId, Signature}; -use client::blockchain::HeaderBackend; +use client::{blockchain::HeaderBackend, runtime_api::StorageProof}; use error::{Error, Result}; -use futures::{prelude::*, sync::mpsc::Receiver}; use log::{debug, error, log_enabled, warn}; use network::specialization::NetworkSpecialization; use network::{DhtEvent, ExHashT}; use prost::Message; use sr_primitives::generic::BlockId; use sr_primitives::traits::{Block as BlockT, ProvideRuntimeApi}; -use std::collections::{HashMap, HashSet}; -use std::convert::TryInto; -use std::iter::FromIterator; -use std::marker::PhantomData; -use std::sync::Arc; -use std::time::{Duration, Instant}; mod error; /// Dht payload schemas generated from Protobuf definitions via Prost crate in build.rs. @@ -81,9 +87,9 @@ where dht_event_rx: Receiver, /// Interval to be proactive, publishing own addresses. - publish_interval: tokio_timer::Interval, + publish_interval: Interval, /// Interval on which to query for addresses of other authorities. - query_interval: tokio_timer::Interval, + query_interval: Interval, /// The network peerset interface for priority groups lets us only set an entire group, but we retrieve the /// addresses of other authorities one by one from the network. To use the peerset interface we need to cache the @@ -96,27 +102,26 @@ where impl AuthorityDiscovery where - Block: BlockT + 'static, + Block: BlockT + Unpin + 'static, Network: NetworkProvider, Client: ProvideRuntimeApi + Send + Sync + 'static + HeaderBackend, ::Api: AuthorityDiscoveryApi, + Self: Future, { /// Return a new authority discovery. pub fn new( client: Arc, network: Arc, - dht_event_rx: futures::sync::mpsc::Receiver, - ) -> AuthorityDiscovery { + dht_event_rx: Receiver, + ) -> Self { // Kademlia's default time-to-live for Dht records is 36h, republishing records every 24h. Given that a node // could restart at any point in time, one can not depend on the republishing process, thus publishing own // external addresses should happen on an interval < 36h. - let publish_interval = - tokio_timer::Interval::new(Instant::now(), Duration::from_secs(12 * 60 * 60)); + let publish_interval = Interval::new(Duration::from_secs(12 * 60 * 60)); // External addresses of other authorities can change at any given point in time. The interval on which to query // for external addresses of other authorities is a trade off between efficiency and performance. - let query_interval = - tokio_timer::Interval::new(Instant::now(), Duration::from_secs(10 * 60)); + let query_interval = Interval::new(Duration::from_secs(10 * 60)); let address_cache = HashMap::new(); @@ -191,8 +196,8 @@ where Ok(()) } - fn handle_dht_events(&mut self) -> Result<()> { - while let Ok(Async::Ready(Some(event))) = self.dht_event_rx.poll() { + fn handle_dht_events(&mut self, cx: &mut Context) -> Result<()> { + while let Poll::Ready(Some(event)) = self.dht_event_rx.poll_next_unpin(cx) { match event { DhtEvent::ValueFound(v) => { if log_enabled!(log::Level::Debug) { @@ -202,15 +207,17 @@ where self.handle_dht_value_found_event(v)?; } - DhtEvent::ValueNotFound(hash) => { - warn!(target: "sub-authority-discovery", "Value for hash '{:?}' not found on Dht.", hash) - } - DhtEvent::ValuePut(hash) => { - debug!(target: "sub-authority-discovery", "Successfully put hash '{:?}' on Dht.", hash) - } - DhtEvent::ValuePutFailed(hash) => { - warn!(target: "sub-authority-discovery", "Failed to put hash '{:?}' on Dht.", hash) - } + DhtEvent::ValueNotFound(hash) => warn!( + target: "sub-authority-discovery", + "Value for hash '{:?}' not found on Dht.", hash + ), + DhtEvent::ValuePut(hash) => debug!( + target: "sub-authority-discovery", + "Successfully put hash '{:?}' on Dht.", hash), + DhtEvent::ValuePutFailed(hash) => warn!( + target: "sub-authority-discovery", + "Failed to put hash '{:?}' on Dht.", hash + ), } } @@ -291,53 +298,36 @@ where } } -impl futures::Future for AuthorityDiscovery +impl Future for AuthorityDiscovery where - Block: BlockT + 'static, + Block: BlockT + Unpin + 'static, Network: NetworkProvider, Client: ProvideRuntimeApi + Send + Sync + 'static + HeaderBackend, ::Api: AuthorityDiscoveryApi, { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> futures::Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut inner = || -> Result<()> { // Process incoming events before triggering new ones. - self.handle_dht_events()?; + self.handle_dht_events(cx)?; - if let Async::Ready(_) = self - .publish_interval - .poll() - .map_err(Error::PollingTokioTimer)? - { + if let Poll::Ready(_) = self.publish_interval.poll_next_unpin(cx) { // Make sure to call interval.poll until it returns Async::NotReady once. Otherwise, in case one of the // function calls within this block do a `return`, we don't call `interval.poll` again and thereby the // underlying Tokio task is never registered with Tokio's Reactor to be woken up on the next interval // tick. - while let Async::Ready(_) = self - .publish_interval - .poll() - .map_err(Error::PollingTokioTimer)? - {} + while let Poll::Ready(_) = self.publish_interval.poll_next_unpin(cx) {} self.publish_own_ext_addresses()?; } - if let Async::Ready(_) = self - .query_interval - .poll() - .map_err(Error::PollingTokioTimer)? - { + if let Poll::Ready(_) = self.query_interval.poll_next_unpin(cx) { // Make sure to call interval.poll until it returns Async::NotReady once. Otherwise, in case one of the // function calls within this block do a `return`, we don't call `interval.poll` again and thereby the // underlying Tokio task is never registered with Tokio's Reactor to be woken up on the next interval // tick. - while let Async::Ready(_) = self - .query_interval - .poll() - .map_err(Error::PollingTokioTimer)? - {} + while let Poll::Ready(_) = self.query_interval.poll_next_unpin(cx) {} self.request_addresses_of_others()?; } @@ -351,7 +341,7 @@ where }; // Make sure to always return NotReady as this is a long running task with the same lifetime as the node itself. - Ok(futures::Async::NotReady) + Poll::Pending } } @@ -415,13 +405,14 @@ fn hash_authority_id(id: &[u8]) -> Result { mod tests { use super::*; use client::runtime_api::{ApiExt, Core, RuntimeVersion}; + use futures::channel::mpsc::channel; + use futures::executor::block_on; use futures::future::poll_fn; use primitives::{ExecutionContext, NativeOrEncoded}; use sr_primitives::traits::Zero; use sr_primitives::traits::{ApiRef, Block as BlockT, NumberFor, ProvideRuntimeApi}; use std::sync::{Arc, Mutex}; use test_client::runtime::Block; - use tokio::runtime::current_thread; #[derive(Clone)] struct TestApi {} @@ -528,7 +519,7 @@ mod tests { unimplemented!("Not required for testing!") } - fn extract_proof(&mut self) -> Option>> { + fn extract_proof(&mut self) -> Option { unimplemented!("Not required for testing!") } } @@ -611,7 +602,7 @@ mod tests { #[test] fn publish_own_ext_addresses_puts_record_on_dht() { - let (_dht_event_tx, dht_event_rx) = futures::sync::mpsc::channel(1000); + let (_dht_event_tx, dht_event_rx) = channel(1000); let test_api = Arc::new(TestApi {}); let network: Arc = Arc::new(Default::default()); @@ -626,7 +617,7 @@ mod tests { #[test] fn request_addresses_of_others_triggers_dht_get_query() { - let (_dht_event_tx, dht_event_rx) = futures::sync::mpsc::channel(1000); + let (_dht_event_tx, dht_event_rx) = channel(1000); let test_api = Arc::new(TestApi {}); let network: Arc = Arc::new(Default::default()); @@ -643,7 +634,7 @@ mod tests { fn handle_dht_events_with_value_found_should_call_set_priority_group() { // Create authority discovery. - let (mut dht_event_tx, dht_event_rx) = futures::sync::mpsc::channel(1000); + let (mut dht_event_tx, dht_event_rx) = channel(1000); let test_api = Arc::new(TestApi {}); let network: Arc = Arc::new(Default::default()); @@ -674,9 +665,8 @@ mod tests { dht_event_tx.try_send(dht_event).unwrap(); // Make authority discovery handle the event. - - let f = || { - authority_discovery.handle_dht_events().unwrap(); + let f = |cx: &mut Context<'_>| -> Poll<()> { + authority_discovery.handle_dht_events(cx).unwrap(); // Expect authority discovery to set the priority set. assert_eq!(network.set_priority_group_call.lock().unwrap().len(), 1); @@ -689,10 +679,9 @@ mod tests { ) ); - Ok(Async::Ready(())) + Poll::Ready(()) }; - let mut runtime = current_thread::Runtime::new().unwrap(); - runtime.block_on(poll_fn::<(), (), _>(f)).unwrap(); + let _ = block_on(poll_fn(f)); } } diff --git a/core/chain-spec/Cargo.toml b/core/chain-spec/Cargo.toml index bff5999ef4f974b3777a121c27a52266180af2ae..1ab78a18dd13c690eb939f92cfb3b65dd35fd701 100644 --- a/core/chain-spec/Cargo.toml +++ b/core/chain-spec/Cargo.toml @@ -5,12 +5,12 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -chain-spec-derive = { package = "substrate-chain-spec-derive", path = "./derive" } -impl-trait-for-tuples = "0.1.2" +substrate-chain-spec-derive = { path = "./derive" } +impl-trait-for-tuples = "0.1.3" network = { package = "substrate-network", path = "../../core/network" } primitives = { package = "substrate-primitives", path = "../primitives" } serde = { version = "1.0.101", features = ["derive"] } -serde_json = "1.0.40" +serde_json = "1.0.41" sr-primitives = { path = "../../core/sr-primitives" } tel = { package = "substrate-telemetry", path = "../../core/telemetry" } diff --git a/core/chain-spec/src/chain_spec.rs b/core/chain-spec/src/chain_spec.rs index b4c57f1e0365c8e5d1d0367ee5111ed59530373d..0f69654b9e8f6cf217e1d54c8dbf8d0fb0ef03b3 100644 --- a/core/chain-spec/src/chain_spec.rs +++ b/core/chain-spec/src/chain_spec.rs @@ -20,6 +20,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; +use std::rc::Rc; use serde::{Serialize, Deserialize}; use primitives::storage::{StorageKey, StorageData}; use sr_primitives::{BuildStorage, StorageOverlay, ChildrenStorageOverlay}; @@ -31,7 +32,7 @@ use tel::TelemetryEndpoints; enum GenesisSource { File(PathBuf), Binary(Cow<'static, [u8]>), - Factory(fn() -> G), + Factory(Rc G>), } impl Clone for GenesisSource { @@ -39,7 +40,7 @@ impl Clone for GenesisSource { match *self { GenesisSource::File(ref path) => GenesisSource::File(path.clone()), GenesisSource::Binary(ref d) => GenesisSource::Binary(d.clone()), - GenesisSource::Factory(f) => GenesisSource::Factory(f), + GenesisSource::Factory(ref f) => GenesisSource::Factory(f.clone()), } } } @@ -70,7 +71,7 @@ impl GenesisSource { } impl<'a, G: RuntimeGenesis, E> BuildStorage for &'a ChainSpec { - fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { + fn build_storage(&self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { match self.genesis.resolve()? { Genesis::Runtime(gc) => gc.build_storage(), Genesis::Raw(map, children_map) => Ok(( @@ -84,7 +85,7 @@ impl<'a, G: RuntimeGenesis, E> BuildStorage for &'a ChainSpec { } fn assimilate_storage( - self, + &self, _: &mut (StorageOverlay, ChildrenStorageOverlay) ) -> Result<(), String> { Err("`assimilate_storage` not implemented for `ChainSpec`.".into()) @@ -187,10 +188,10 @@ impl ChainSpec { } /// Create hardcoded spec. - pub fn from_genesis( + pub fn from_genesis G + 'static>( name: &str, id: &str, - constructor: fn() -> G, + constructor: F, boot_nodes: Vec, telemetry_endpoints: Option, protocol_id: Option<&str>, @@ -211,7 +212,7 @@ impl ChainSpec { ChainSpec { spec, - genesis: GenesisSource::Factory(constructor), + genesis: GenesisSource::Factory(Rc::new(constructor)), } } } @@ -288,11 +289,11 @@ mod tests { impl BuildStorage for Genesis { fn assimilate_storage( - self, + &self, storage: &mut (StorageOverlay, ChildrenStorageOverlay), ) -> Result<(), String> { storage.0.extend( - self.0.into_iter().map(|(a, b)| (a.into_bytes(), b.into_bytes())) + self.0.iter().map(|(a, b)| (a.clone().into_bytes(), b.clone().into_bytes())) ); Ok(()) } diff --git a/core/chain-spec/src/extension.rs b/core/chain-spec/src/extension.rs index bf98ced04d63fc6e124920c523070acfd3604768..e5110a77d5a2301fadcdea8943e01aeb501c1ed4 100644 --- a/core/chain-spec/src/extension.rs +++ b/core/chain-spec/src/extension.rs @@ -253,7 +253,7 @@ impl Extension for Forks where #[cfg(test)] mod tests { use super::*; - use chain_spec_derive::{ChainSpecGroup, ChainSpecExtension}; + use substrate_chain_spec_derive::{ChainSpecGroup, ChainSpecExtension}; // Make the proc macro work for tests and doc tests. use crate as substrate_chain_spec; diff --git a/core/chain-spec/src/lib.rs b/core/chain-spec/src/lib.rs index 705be998b7967c8a9ee9e99125213491273847bf..8c35c22d9b2b34959d8dbf05226fffb9e971011f 100644 --- a/core/chain-spec/src/lib.rs +++ b/core/chain-spec/src/lib.rs @@ -113,7 +113,7 @@ mod extension; pub use chain_spec::{ChainSpec, Properties, NoExtension}; pub use extension::{Group, Fork, Forks, Extension}; -pub use chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; +pub use substrate_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; use serde::{Serialize, de::DeserializeOwned}; use sr_primitives::BuildStorage; diff --git a/core/cli/Cargo.toml b/core/cli/Cargo.toml index e3b33746d580b6e0271b786cd0b5fc15ffe6d319..9fb528e66cf567c3827805e43b35b552e57c9f49 100644 --- a/core/cli/Cargo.toml +++ b/core/cli/Cargo.toml @@ -6,9 +6,9 @@ description = "Substrate CLI interface." edition = "2018" [dependencies] -clap = "~2.32.0" +clap = "2.33.0" derive_more = "0.15.0" -env_logger = "0.6.2" +env_logger = "0.7.0" log = "0.4.8" atty = "0.2.13" regex = "1.3.1" @@ -21,20 +21,25 @@ futures = "0.1.29" futures03 = { package = "futures-preview", version = "=0.3.0-alpha.19", features = ["compat"] } fdlimit = "0.1.1" exit-future = "0.1.4" -serde_json = "1.0.40" +serde_json = "1.0.41" panic-handler = { package = "substrate-panic-handler", path = "../../core/panic-handler" } client = { package = "substrate-client", path = "../../core/client" } header-metadata = { package = "substrate-header-metadata", path = "../../core/client/header-metadata" } network = { package = "substrate-network", path = "../../core/network" } sr-primitives = { path = "../../core/sr-primitives" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } -service = { package = "substrate-service", path = "../../core/service" } +service = { package = "substrate-service", path = "../../core/service", default-features = false } state-machine = { package = "substrate-state-machine", path = "../../core/state-machine" } substrate-telemetry = { path = "../../core/telemetry" } keyring = { package = "substrate-keyring", path = "../keyring" } names = "0.11.0" -structopt = "0.2.0" +structopt = "0.3.3" rpassword = "4.0.1" [dev-dependencies] tempdir = "0.3.7" + +[features] +wasmtime = [ + "service/wasmtime", +] diff --git a/core/cli/src/execution_strategy.rs b/core/cli/src/execution_strategy.rs index bd3030906ec09fdea7d3707d0387926055cce96f..93236227e5d640983c58139f316afc81f003ab30 100644 --- a/core/cli/src/execution_strategy.rs +++ b/core/cli/src/execution_strategy.rs @@ -16,7 +16,7 @@ #![allow(missing_docs)] -use structopt::clap::{arg_enum, _clap_count_exprs}; +use structopt::clap::arg_enum; arg_enum! { /// How to execute blocks diff --git a/core/cli/src/informant.rs b/core/cli/src/informant.rs index 9d7f04b8f6d4f5162204107d5d4ee96f0e2971d8..f7c23ba47918a42363c960ec1a3d4bef0f97accf 100644 --- a/core/cli/src/informant.rs +++ b/core/cli/src/informant.rs @@ -22,6 +22,7 @@ use futures03::{StreamExt as _, TryStreamExt as _}; use log::{info, warn}; use sr_primitives::traits::Header; use service::AbstractService; +use std::time::Duration; mod display; @@ -31,11 +32,13 @@ pub fn build(service: &impl AbstractService) -> impl Future(config: &Configuration) -> String { + if config.sentry_mode { + "SENTRY".to_string() + } else { + format!("{:?}", config.roles) + } +} + /// Output of calling `parse_and_prepare`. #[must_use] pub enum ParseAndPrepare<'a, CC, RP> { @@ -268,22 +282,24 @@ pub struct ParseAndPrepareRun<'a, RP> { impl<'a, RP> ParseAndPrepareRun<'a, RP> { /// Runs the command and runs the main client. - pub fn run( + pub fn run( self, spec_factory: S, exit: Exit, run_service: RS, ) -> error::Result<()> - where S: FnOnce(&str) -> Result>, String>, + where + S: FnOnce(&str) -> Result>, String>, + E: Into, RP: StructOpt + Clone, C: Default, G: RuntimeGenesis, - E: ChainSpecExtension, + CE: ChainSpecExtension, Exit: IntoExit, - RS: FnOnce(Exit, RunCmd, RP, Configuration) -> Result<(), String> + 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 + self.params.left.clone(), spec_factory, self.impl_name, self.version, )?; run_service(exit, self.params.left, self.params.right, config).map_err(Into::into) @@ -298,18 +314,36 @@ pub struct ParseAndPrepareBuildSpec<'a> { impl<'a> ParseAndPrepareBuildSpec<'a> { /// Runs the command and build the chain specs. - pub fn run( + 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)?; - with_default_boot_node(&mut spec, self.params, self.version)?; + + if spec.boot_nodes().is_empty() && !self.params.disable_default_bootnode { + let base_path = base_path(&self.params.shared_params, self.version); + let cfg = service::Configuration::::default_with_spec_and_base_path(spec.clone(), Some(base_path)); + 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 = service::chain_ops::build_spec(spec, raw_output)?; print!("{}", json); @@ -342,7 +376,9 @@ impl<'a> ParseAndPrepareExport<'a> { { let config = create_config_with_db_path(spec_factory, &self.params.shared_params, self.version)?; - info!("DB path: {}", config.database_path.display()); + if let DatabaseConfig::Path { ref path, .. } = &config.database { + info!("DB path: {}", path.display()); + } let from = self.params.from.unwrap_or(1); let to = self.params.to; let json = self.params.json; @@ -380,6 +416,7 @@ impl<'a> ParseAndPrepareImport<'a> { Exit: IntoExit { let mut config = create_config_with_db_path(spec_factory, &self.params.shared_params, self.version)?; + config.wasm_method = self.params.wasm_method.into(); config.execution_strategies = ExecutionStrategies { importing: self.params.execution.into(), other: self.params.execution.into(), @@ -420,10 +457,16 @@ impl<'a> ParseAndPreparePurge<'a> { let config = create_config_with_db_path::<(), _, _, _>( spec_factory, &self.params.shared_params, self.version )?; - let db_path = config.database_path; + 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); + print!("Are you sure to remove {:?}? [y/N]: ", &db_path); stdout().flush().expect("failed to flush stdout"); let mut input = String::new(); @@ -445,7 +488,7 @@ impl<'a> ParseAndPreparePurge<'a> { Ok(()) }, Result::Err(ref err) if err.kind() == ErrorKind::NotFound => { - println!("{:?} did not exist.", &db_path); + eprintln!("{:?} did not exist.", &db_path); Ok(()) }, Result::Err(err) => Result::Err(err.into()) @@ -490,14 +533,6 @@ where P: AsRef { match params.node_key_type { - NodeKeyType::Secp256k1 => - params.node_key.as_ref().map(parse_secp256k1_secret).unwrap_or_else(|| - Ok(params.node_key_file - .or_else(|| net_config_file(net_config_dir, NODE_KEY_SECP256K1_FILE)) - .map(network::config::Secret::File) - .unwrap_or(network::config::Secret::New))) - .map(NodeKeyConfig::Secp256k1), - NodeKeyType::Ed25519 => params.node_key.as_ref().map(parse_ed25519_secret).unwrap_or_else(|| Ok(params.node_key_file @@ -520,14 +555,6 @@ fn invalid_node_key(e: impl std::fmt::Display) -> error::Error { error::Error::Input(format!("Invalid node key: {}", e)) } -/// Parse a Secp256k1 secret key from a hex string into a `network::Secret`. -fn parse_secp256k1_secret(hex: &String) -> error::Result { - H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes| - network::config::identity::secp256k1::SecretKey::from_bytes(bytes) - .map(network::config::Secret::Input) - .map_err(invalid_node_key)) -} - /// Parse a Ed25519 secret key from a hex string into a `network::Secret`. fn parse_ed25519_secret(hex: &String) -> error::Result { H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes| @@ -556,16 +583,13 @@ fn fill_transaction_pool_configuration( /// Fill the given `NetworkConfiguration` by looking at the cli parameters. fn fill_network_configuration( cli: NetworkConfigurationParams, - base_path: &Path, - chain_spec_id: &str, + config_path: PathBuf, config: &mut NetworkConfiguration, client_id: String, is_dev: bool, ) -> error::Result<()> { config.boot_nodes.extend(cli.bootnodes.into_iter()); - config.config_path = Some( - network_path(&base_path, chain_spec_id).to_string_lossy().into() - ); + config.config_path = Some(config_path.to_string_lossy().into()); config.net_config_path = config.config_path.clone(); config.reserved_nodes.extend(cli.reserved_nodes.into_iter()); @@ -631,7 +655,7 @@ fn fill_config_keystore_password( } fn create_run_node_config( - cli: RunCmd, spec_factory: S, impl_name: &'static str, version: &VersionInfo + cli: RunCmd, spec_factory: S, impl_name: &'static str, version: &VersionInfo, ) -> error::Result> where C: Default, @@ -640,7 +664,8 @@ where S: FnOnce(&str) -> Result>, String>, { let spec = load_spec(&cli.shared_params, spec_factory)?; - let mut config = service::Configuration::default_with_spec(spec.clone()); + let base_path = base_path(&cli.shared_params, &version); + let mut config = service::Configuration::default_with_spec_and_base_path(spec.clone(), Some(base_path)); fill_config_keystore_password(&mut config, &cli)?; @@ -664,34 +689,53 @@ where )? } - let base_path = base_path(&cli.shared_params, version); - - config.keystore_path = cli.keystore_path.unwrap_or_else( - || keystore_path(&base_path, config.chain_spec.id()) - ); + config.keystore_path = cli.keystore_path.or_else(|| config.in_chain_config_dir(DEFAULT_KEYSTORE_CONFIG_PATH)); - config.database_path = db_path(&base_path, config.chain_spec.id()); - config.database_cache_size = cli.database_cache_size; - config.state_cache_size = cli.state_cache_size; - config.pruning = match cli.pruning { - Some(ref s) if s == "archive" => PruningMode::ArchiveAll, - None => PruningMode::default(), - Some(s) => PruningMode::keep_blocks(s.parse() - .map_err(|_| error::Error::Input("Invalid pruning mode specified".to_string()))? - ), + config.database = DatabaseConfig::Path { + path: config.in_chain_config_dir(DEFAULT_DB_CONFIG_PATH).expect("We provided a base_path."), + cache_size: cli.database_cache_size, }; + config.state_cache_size = cli.state_cache_size; let is_dev = cli.shared_params.dev; + let is_authority = cli.validator || cli.sentry || is_dev || cli.keyring.account.is_some(); let role = if cli.light { service::Roles::LIGHT - } else if cli.validator || is_dev || cli.keyring.account.is_some() { + } else if is_authority { service::Roles::AUTHORITY } else { service::Roles::FULL }; + // set sentry mode (i.e. act as an authority but **never** actively participate) + config.sentry_mode = cli.sentry; + + // by default we disable pruning if the node is an authority (i.e. + // `ArchiveAll`), otherwise we keep state for the last 256 blocks. if the + // node is an authority and pruning is enabled explicitly, then we error + // unless `unsafe_pruning` is set. + config.pruning = match cli.pruning { + Some(ref s) if s == "archive" => PruningMode::ArchiveAll, + None if role == service::Roles::AUTHORITY => PruningMode::ArchiveAll, + None => PruningMode::default(), + Some(s) => { + if role == service::Roles::AUTHORITY && !cli.unsafe_pruning { + return Err(error::Error::Input( + "Validators should run with state pruning disabled (i.e. archive). \ + You can ignore this check with `--unsafe-pruning`.".to_string() + )); + } + + PruningMode::keep_blocks(s.parse() + .map_err(|_| error::Error::Input("Invalid pruning mode specified".to_string()))? + ) + }, + }; + + config.wasm_method = cli.wasm_method.into(); + let exec = cli.execution_strategies; let exec_all_or = |strat: params::ExecutionStrategy| exec.execution.unwrap_or(strat).into(); config.execution_strategies = ExecutionStrategies { @@ -715,8 +759,7 @@ where let client_id = config.client_id(); fill_network_configuration( cli.network_config, - &base_path, - spec.id(), + config.in_chain_config_dir(DEFAULT_NETWORK_CONFIG_PATH).expect("We provided a basepath"), &mut config.network, client_id, is_dev, @@ -767,39 +810,6 @@ where Ok(config) } -// -// IANA unassigned port ranges that we could use: -// 6717-6766 Unassigned -// 8504-8553 Unassigned -// 9556-9591 Unassigned -// 9803-9874 Unassigned -// 9926-9949 Unassigned - -fn with_default_boot_node( - spec: &mut ChainSpec, - cli: BuildSpecCmd, - version: &VersionInfo, -) -> error::Result<()> -where - G: RuntimeGenesis, - E: ChainSpecExtension, -{ - if spec.boot_nodes().is_empty() { - let base_path = base_path(&cli.shared_params, version); - let storage_path = network_path(&base_path, spec.id()); - let node_key = node_key_config(cli.node_key_params, &Some(storage_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) - } - Ok(()) -} - /// Creates a configuration including the database path. pub fn create_config_with_db_path( spec_factory: S, cli: &SharedParams, version: &VersionInfo, @@ -813,8 +823,11 @@ where let spec = load_spec(cli, spec_factory)?; let base_path = base_path(cli, version); - let mut config = service::Configuration::default_with_spec(spec.clone()); - config.database_path = db_path(&base_path, spec.id()); + let mut config = 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) } @@ -838,30 +851,6 @@ fn parse_address( Ok(address) } -fn keystore_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("keystore"); - path -} - -fn db_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("db"); - path -} - -fn network_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("network"); - path -} - fn init_logger(pattern: &str) { use ansi_term::Colour; @@ -869,6 +858,7 @@ fn init_logger(pattern: &str) { // Disable info logging by default for some modules: builder.filter(Some("ws"), log::LevelFilter::Off); builder.filter(Some("hyper"), log::LevelFilter::Warn); + builder.filter(Some("cranelift_wasm"), log::LevelFilter::Warn); // Enable info for others. builder.filter(None, log::LevelFilter::Info); @@ -915,7 +905,9 @@ fn init_logger(pattern: &str) { writeln!(buf, "{}", output) }); - builder.init(); + if builder.try_init().is_err() { + info!("Not registering Substrate logger, as there is already a global logger registered!"); + } } fn kill_color(s: &str) -> String { @@ -929,7 +921,7 @@ fn kill_color(s: &str) -> String { mod tests { use super::*; use tempdir::TempDir; - use network::config::identity::{secp256k1, ed25519}; + use network::config::identity::ed25519; #[test] fn tests_node_name_good() { @@ -952,7 +944,6 @@ mod tests { NodeKeyType::variants().into_iter().try_for_each(|t| { let node_key_type = NodeKeyType::from_str(t).unwrap(); let sk = match node_key_type { - NodeKeyType::Secp256k1 => secp256k1::SecretKey::generate().to_bytes().to_vec(), NodeKeyType::Ed25519 => ed25519::SecretKey::generate().as_ref().to_vec() }; let params = NodeKeyParams { @@ -961,9 +952,6 @@ mod tests { node_key_file: None }; node_key_config(params, &net_config_dir).and_then(|c| match c { - NodeKeyConfig::Secp256k1(network::config::Secret::Input(ref ski)) - if node_key_type == NodeKeyType::Secp256k1 && - &sk[..] == ski.to_bytes() => Ok(()), NodeKeyConfig::Ed25519(network::config::Secret::Input(ref ski)) if node_key_type == NodeKeyType::Ed25519 && &sk[..] == ski.as_ref() => Ok(()), @@ -989,8 +977,6 @@ mod tests { node_key_file: Some(file.clone()) }; node_key_config(params, &net_config_dir).and_then(|c| match c { - NodeKeyConfig::Secp256k1(network::config::Secret::File(ref f)) - if node_key_type == NodeKeyType::Secp256k1 && f == &file => Ok(()), NodeKeyConfig::Ed25519(network::config::Secret::File(ref f)) if node_key_type == NodeKeyType::Ed25519 && f == &file => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())) @@ -1023,8 +1009,6 @@ mod tests { let typ = params.node_key_type; node_key_config::(params, &None) .and_then(|c| match c { - NodeKeyConfig::Secp256k1(network::config::Secret::New) - if typ == NodeKeyType::Secp256k1 => Ok(()), NodeKeyConfig::Ed25519(network::config::Secret::New) if typ == NodeKeyType::Ed25519 => Ok(()), _ => Err(error::Error::Input("Unexpected node key config".into())) @@ -1038,9 +1022,6 @@ mod tests { let typ = params.node_key_type; node_key_config(params, &Some(net_config_dir.clone())) .and_then(move |c| match c { - NodeKeyConfig::Secp256k1(network::config::Secret::File(ref f)) - if typ == NodeKeyType::Secp256k1 && - f == &dir.join(NODE_KEY_SECP256K1_FILE) => Ok(()), NodeKeyConfig::Ed25519(network::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()), diff --git a/core/cli/src/params.rs b/core/cli/src/params.rs index 6656e1653fc986b3c17fce5428382741b092ee17..d0a235241667c827a025904f940f08b7555bba1b 100644 --- a/core/cli/src/params.rs +++ b/core/cli/src/params.rs @@ -17,8 +17,7 @@ use crate::traits::{AugmentClap, GetLogFilter}; use std::path::PathBuf; -use structopt::{StructOpt, clap::{arg_enum, App, AppSettings, _clap_count_exprs, SubCommand, Arg}}; -use client; +use structopt::{StructOpt, clap::{arg_enum, App, AppSettings, SubCommand, Arg}}; pub use crate::execution_strategy::ExecutionStrategy; @@ -44,6 +43,43 @@ impl Into for ExecutionStrategy { } } +arg_enum! { + /// How to execute Wasm runtime code + #[allow(missing_docs)] + #[derive(Debug, Clone)] + pub enum WasmExecutionMethod { + // Uses an interpreter. + Interpreted, + // Uses a compiled runtime. + Compiled, + } +} + +impl WasmExecutionMethod { + /// Returns list of variants that are not disabled by feature flags. + fn enabled_variants() -> Vec<&'static str> { + Self::variants() + .iter() + .cloned() + .filter(|&name| cfg!(feature = "wasmtime") || name != "Compiled") + .collect() + } +} + +impl Into for WasmExecutionMethod { + fn into(self) -> service::config::WasmExecutionMethod { + match self { + WasmExecutionMethod::Interpreted => service::config::WasmExecutionMethod::Interpreted, + #[cfg(feature = "wasmtime")] + WasmExecutionMethod::Compiled => service::config::WasmExecutionMethod::Compiled, + #[cfg(not(feature = "wasmtime"))] + WasmExecutionMethod::Compiled => panic!( + "Substrate must be compiled with \"wasmtime\" feature for compiled Wasm execution" + ), + } + } +} + arg_enum! { /// Whether off-chain workers are enabled. #[allow(missing_docs)] @@ -133,7 +169,6 @@ arg_enum! { #[allow(missing_docs)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum NodeKeyType { - Secp256k1, Ed25519 } } @@ -147,10 +182,6 @@ pub struct NodeKeyParams { /// The value is a string that is parsed according to the choice of /// `--node-key-type` as follows: /// - /// `secp256k1`: - /// The value is parsed as a hex-encoded Secp256k1 32 bytes secret key, - /// i.e. 64 hex characters. - /// /// `ed25519`: /// The value is parsed as a hex-encoded Ed25519 32 bytes secret key, /// i.e. 64 hex characters. @@ -181,18 +212,12 @@ pub struct NodeKeyParams { /// /// The node's secret key determines the corresponding public key and hence the /// node's peer ID in the context of libp2p. - /// - /// NOTE: The current default key type is `secp256k1` for a transition period only - /// but will eventually change to `ed25519` in a future release. To continue using - /// `secp256k1` keys, use `--node-key-type=secp256k1`. #[structopt( long = "node-key-type", value_name = "TYPE", - raw( - possible_values = "&NodeKeyType::variants()", - case_insensitive = "true", - default_value = r#""Ed25519""# - ) + possible_values = &NodeKeyType::variants(), + case_insensitive = true, + default_value = "Ed25519" )] pub node_key_type: NodeKeyType, @@ -201,9 +226,6 @@ pub struct NodeKeyParams { /// The contents of the file are parsed according to the choice of `--node-key-type` /// as follows: /// - /// `secp256k1`: - /// The file must contain an unencoded 32 bytes Secp256k1 secret key. - /// /// `ed25519`: /// The file must contain an unencoded 32 bytes Ed25519 secret key. /// @@ -231,11 +253,9 @@ pub struct ExecutionStrategies { #[structopt( long = "execution-syncing", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - default_value = r#""NativeElseWasm""# - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + default_value = "NativeElseWasm" )] pub execution_syncing: ExecutionStrategy, @@ -243,11 +263,9 @@ pub struct ExecutionStrategies { #[structopt( long = "execution-import-block", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - default_value = r#""NativeElseWasm""# - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + default_value = "NativeElseWasm" )] pub execution_import_block: ExecutionStrategy, @@ -255,11 +273,9 @@ pub struct ExecutionStrategies { #[structopt( long = "execution-block-construction", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - default_value = r#""Wasm""# - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + default_value = "Wasm" )] pub execution_block_construction: ExecutionStrategy, @@ -267,11 +283,9 @@ pub struct ExecutionStrategies { #[structopt( long = "execution-offchain-worker", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - default_value = r#""Native""# - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + default_value = "Native" )] pub execution_offchain_worker: ExecutionStrategy, @@ -279,11 +293,9 @@ pub struct ExecutionStrategies { #[structopt( long = "execution-other", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - default_value = r#""Native""# - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + default_value = "Native" )] pub execution_other: ExecutionStrategy, @@ -291,17 +303,15 @@ pub struct ExecutionStrategies { #[structopt( long = "execution", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - conflicts_with_all = "&[ - \"execution_other\", - \"execution_offchain_worker\", - \"execution_block_construction\", - \"execution_import_block\", - \"execution_syncing\", - ]" - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + conflicts_with_all = &[ + "execution-other", + "execution-offchain-worker", + "execution-block-construction", + "execution-import-block", + "execution-syncing", + ] )] pub execution: Option, } @@ -310,9 +320,31 @@ pub struct ExecutionStrategies { #[derive(Debug, StructOpt, Clone)] pub struct RunCmd { /// Enable validator mode. - #[structopt(long = "validator")] + /// + /// The node will be started with the authority role and actively + /// participate in any consensus task that it can (e.g. depending on + /// availability of local keys). + #[structopt( + long = "validator", + conflicts_with_all = &[ "sentry" ] + )] pub validator: bool, + /// Enable sentry mode. + /// + /// The node will be started with the authority role and participate in + /// consensus tasks as an "observer", it will never actively participate + /// regardless of whether it could (e.g. keys are available locally). This + /// mode is useful as a secure proxy for validators (which would run + /// detached from the network), since we want this node to participate in + /// the full consensus protocols in order to have all needed consensus data + /// available to relay to private nodes. + #[structopt( + long = "sentry", + conflicts_with_all = &[ "validator" ] + )] + pub sentry: bool, + /// Disable GRANDPA voter when running in validator mode, otherwise disables the GRANDPA observer. #[structopt(long = "no-grandpa")] pub no_grandpa: bool, @@ -360,15 +392,25 @@ pub struct RunCmd { /// allow localhost, https://polkadot.js.org and /// https://substrate-ui.parity.io origins. When running in --dev mode the /// default is to allow all origins. - #[structopt(long = "rpc-cors", value_name = "ORIGINS", parse(try_from_str = "parse_cors"))] + #[structopt(long = "rpc-cors", value_name = "ORIGINS", parse(try_from_str = parse_cors))] pub rpc_cors: Option, - /// Specify the pruning mode, a number of blocks to keep or 'archive'. + /// Specify the state pruning mode, a number of blocks to keep or 'archive'. /// - /// Default is 256. + /// Default is to keep all block states if the node is running as a + /// validator (i.e. 'archive'), otherwise state is only kept for the last + /// 256 blocks. #[structopt(long = "pruning", value_name = "PRUNING_MODE")] pub pruning: Option, + /// Force start with unsafe pruning settings. + /// + /// When running as a validator it is highly recommended to disable state + /// pruning (i.e. 'archive') which is the default. The node will refuse to + /// start as a validator if pruning is enabled unless this option is set. + #[structopt(long = "unsafe-pruning")] + pub unsafe_pruning: bool, + /// The human-readable name for this node. /// /// The node name will be reported to the telemetry server, if enabled. @@ -387,7 +429,7 @@ pub struct RunCmd { /// telemetry endpoints. Verbosity levels range from 0-9, with 0 denoting /// the least verbosity. If no verbosity level is specified the default is /// 0. - #[structopt(long = "telemetry-url", value_name = "URL VERBOSITY", parse(try_from_str = "parse_telemetry_endpoints"))] + #[structopt(long = "telemetry-url", value_name = "URL VERBOSITY", parse(try_from_str = parse_telemetry_endpoints))] pub telemetry_endpoints: Vec<(String, u8)>, /// Should execute offchain workers on every block. @@ -396,14 +438,22 @@ pub struct RunCmd { #[structopt( long = "offchain-worker", value_name = "ENABLED", - raw( - possible_values = "&OffchainWorkerEnabled::variants()", - case_insensitive = "true", - default_value = r#""WhenValidating""# - ) + possible_values = &OffchainWorkerEnabled::variants(), + case_insensitive = true, + default_value = "WhenValidating" )] pub offchain_worker: OffchainWorkerEnabled, + /// 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, + #[allow(missing_docs)] #[structopt(flatten)] pub execution_strategies: ExecutionStrategies, @@ -435,14 +485,14 @@ pub struct RunCmd { /// Use interactive shell for entering the password used by the keystore. #[structopt( long = "password-interactive", - raw(conflicts_with_all = "&[ \"password\", \"password_filename\" ]") + conflicts_with_all = &[ "password", "password-filename" ] )] pub password_interactive: bool, /// Password used by the keystore. #[structopt( long = "password", - raw(conflicts_with_all = "&[ \"password_interactive\", \"password_filename\" ]") + conflicts_with_all = &[ "password-interactive", "password-filename" ] )] pub password: Option, @@ -451,7 +501,7 @@ pub struct RunCmd { long = "password-filename", value_name = "PATH", parse(from_os_str), - raw(conflicts_with_all = "&[ \"password_interactive\", \"password\" ]") + conflicts_with_all = &[ "password-interactive", "password" ] )] pub password_filename: Option } @@ -593,6 +643,13 @@ pub struct BuildSpecCmd { #[structopt(long = "raw")] pub raw: bool, + /// Disable adding the default bootnode to the specification. + /// + /// By default the `/ip4/127.0.0.1/tcp/30333/p2p/NODE_PEER_ID` bootnode is added to the + /// specification when no bootnode exists. + #[structopt(long = "disable-default-bootnode")] + pub disable_default_bootnode: bool, + #[allow(missing_docs)] #[structopt(flatten)] pub shared_params: SharedParams, @@ -651,15 +708,23 @@ pub struct ImportBlocksCmd { #[structopt(flatten)] pub shared_params: SharedParams, + /// Method for executing Wasm runtime code. + #[structopt( + long = "wasm-execution", + value_name = "METHOD", + possible_values = &WasmExecutionMethod::variants(), + case_insensitive = true, + default_value = "Interpreted" + )] + pub wasm_method: WasmExecutionMethod, + /// The means of execution used when calling into the runtime while importing blocks. #[structopt( long = "execution", value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategy::variants()", - case_insensitive = "true", - default_value = r#""NativeElseWasm""# - ) + possible_values = &ExecutionStrategy::variants(), + case_insensitive = true, + default_value = "NativeElseWasm" )] pub execution: ExecutionStrategy, } @@ -734,7 +799,7 @@ impl StructOpt for CoreParams where ) ).subcommand( BuildSpecCmd::augment_clap(SubCommand::with_name("build-spec")) - .about("Build a spec.json file, outputing to stdout.") + .about("Build a spec.json file, outputting to stdout.") ) .subcommand( ExportBlocksCmd::augment_clap(SubCommand::with_name("export-blocks")) @@ -789,7 +854,7 @@ impl GetLogFilter for CoreParams where CC: GetLogFilter { /// 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)] +#[derive(Clone, Debug, Default)] pub struct NoCustom {} impl StructOpt for NoCustom { diff --git a/core/client/Cargo.toml b/core/client/Cargo.toml index 8852988cb11e1c61e862dac266c7dfc907d916b8..761b2a9da21b0076bb45d2ff1f5a7cadbd5d7b53 100644 --- a/core/client/Cargo.toml +++ b/core/client/Cargo.toml @@ -30,8 +30,9 @@ sr-api-macros = { path = "../sr-api-macros" } header-metadata = { package = "substrate-header-metadata", path = "header-metadata", optional = true } [dev-dependencies] -env_logger = "0.6.2" +env_logger = "0.7.0" tempfile = "3.1.0" +client-db = { package = "substrate-client-db", path = "./db", features = ["kvdb-rocksdb"] } test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client" } kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" } diff --git a/core/client/db/Cargo.toml b/core/client/db/Cargo.toml index 891255cb77abb285de21a9c27d003f037f052565..7d88c39d7fd7ed18635cd5c6253202c65ba127e9 100644 --- a/core/client/db/Cargo.toml +++ b/core/client/db/Cargo.toml @@ -27,7 +27,7 @@ header_metadata = { package = "substrate-header-metadata", path = "../header-met [dev-dependencies] substrate-keyring = { path = "../../keyring" } test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } -env_logger = "0.6.2" +env_logger = "0.7.0" [features] default = [] diff --git a/core/client/db/src/cache/list_cache.rs b/core/client/db/src/cache/list_cache.rs index 9095b80fb672824fc14d3a73f86417305bfd6ea1..7a4fcc448e0e55970141b294e31fe387ef69c89b 100644 --- a/core/client/db/src/cache/list_cache.rs +++ b/core/client/db/src/cache/list_cache.rs @@ -39,7 +39,7 @@ //! Finalized entry E1 is pruned when block B is finalized so that: //! EntryAt(B.number - prune_depth).points_to(E1) -use std::collections::BTreeSet; +use std::collections::{BTreeSet, BTreeMap}; use log::warn; @@ -89,6 +89,9 @@ pub enum CommitOperation { /// - new entry is finalized AND/OR /// - some forks are destroyed BlockFinalized(ComplexBlockId, Option>, BTreeSet), + /// When best block is reverted - contains the forks that have to be updated + /// (they're either destroyed, or their best entry is updated to earlier block). + BlockReverted(BTreeMap>>), } /// Single fork of list-based cache. @@ -333,6 +336,44 @@ impl> ListCache Ok(Some(operation)) } + /// When block is reverted. + pub fn on_block_revert>( + &self, + tx: &mut Tx, + reverted_block: &ComplexBlockId, + ) -> ClientResult> { + // can't revert finalized blocks + debug_assert!(self.best_finalized_block.number < reverted_block.number); + + // iterate all unfinalized forks and truncate/destroy if required + let mut updated = BTreeMap::new(); + for (index, fork) in self.unfinalized.iter().enumerate() { + // we only need to truncate fork if its head is ancestor of truncated block + if fork.head.valid_from.number < reverted_block.number { + continue; + } + + // we only need to truncate fork if its head is connected to truncated block + if !chain::is_connected_to_block(&self.storage, reverted_block, &fork.head.valid_from)? { + continue; + } + + let updated_fork = fork.truncate( + &self.storage, + tx, + reverted_block.number, + self.best_finalized_block.number, + )?; + updated.insert(index, updated_fork); + } + + // schedule commit operation and update meta + let operation = CommitOperation::BlockReverted(updated); + tx.update_meta(self.best_finalized_entry.as_ref(), &self.unfinalized, &operation); + + Ok(operation) + } + /// When transaction is committed. pub fn on_transaction_commit(&mut self, op: CommitOperation) { match op { @@ -366,6 +407,14 @@ impl> ListCache self.unfinalized.remove(*fork_index); } }, + CommitOperation::BlockReverted(forks) => { + for (fork_index, updated_fork) in forks.into_iter().rev() { + match updated_fork { + Some(updated_fork) => self.unfinalized[fork_index] = updated_fork, + None => { self.unfinalized.remove(fork_index); }, + } + } + }, } } @@ -533,6 +582,43 @@ impl Fork { best_finalized_block, ) } + + /// Truncate fork by deleting all entries that are descendants of given block. + pub fn truncate, Tx: StorageTransaction>( + &self, + storage: &S, + tx: &mut Tx, + reverting_block: NumberFor, + best_finalized_block: NumberFor, + ) -> ClientResult>> { + let mut current = self.head.valid_from.clone(); + loop { + // read pointer to previous entry + let entry = storage.require_entry(¤t)?; + + // truncation stops when we have reached the ancestor of truncated block + if current.number < reverting_block { + // if we have reached finalized block => destroy fork + if chain::is_finalized_block(storage, ¤t, best_finalized_block)? { + return Ok(None); + } + + // else fork needs to be updated + return Ok(Some(Fork { + best_block: None, + head: entry.into_entry(current), + })); + } + + tx.remove_storage_entry(¤t); + + // truncation also stops when there are no more entries in the list + current = match entry.prev_valid_from { + Some(prev_valid_from) => prev_valid_from, + None => return Ok(None), + }; + } + } } /// Destroy fork by deleting all unfinalized entries. @@ -1400,4 +1486,57 @@ pub mod tests { do_test(PruningStrategy::ByDepth(10)); do_test(PruningStrategy::NeverPrune) } + + #[test] + fn revert_block_works() { + // 1 -> (2) -> 3 -> 4 -> 5 + // \ + // -> 5'' + // \ + // -> (3') -> 4' -> 5' + let mut cache = ListCache::new( + DummyStorage::new() + .with_meta(Some(correct_id(1)), vec![correct_id(5), fork_id(1, 2, 5), fork_id(2, 4, 5)]) + .with_id(1, correct_id(1).hash) + .with_entry(correct_id(1), StorageEntry { prev_valid_from: None, value: 1 }) + .with_entry(correct_id(3), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 3 }) + .with_entry(correct_id(4), StorageEntry { prev_valid_from: Some(correct_id(3)), value: 4 }) + .with_entry(correct_id(5), StorageEntry { prev_valid_from: Some(correct_id(4)), value: 5 }) + .with_entry(fork_id(1, 2, 4), StorageEntry { prev_valid_from: Some(correct_id(1)), value: 14 }) + .with_entry(fork_id(1, 2, 5), StorageEntry { prev_valid_from: Some(fork_id(1, 2, 4)), value: 15 }) + .with_entry(fork_id(2, 4, 5), StorageEntry { prev_valid_from: Some(correct_id(4)), value: 25 }) + .with_header(test_header(1)) + .with_header(test_header(2)) + .with_header(test_header(3)) + .with_header(test_header(4)) + .with_header(test_header(5)) + .with_header(fork_header(1, 2, 3)) + .with_header(fork_header(1, 2, 4)) + .with_header(fork_header(1, 2, 5)) + .with_header(fork_header(2, 4, 5)), + PruningStrategy::ByDepth(1024), correct_id(1) + ); + + // when 5 is reverted: entry 5 is truncated + let op = cache.on_block_revert(&mut DummyTransaction::new(), &correct_id(5)).unwrap(); + assert_eq!(op, CommitOperation::BlockReverted(vec![ + (0, Some(Fork { best_block: None, head: Entry { valid_from: correct_id(4), value: 4 } })), + ].into_iter().collect())); + cache.on_transaction_commit(op); + + // when 3 is reverted: entries 4+5' are truncated + let op = cache.on_block_revert(&mut DummyTransaction::new(), &correct_id(3)).unwrap(); + assert_eq!(op, CommitOperation::BlockReverted(vec![ + (0, None), + (2, None), + ].into_iter().collect())); + cache.on_transaction_commit(op); + + // when 2 is reverted: entries 4'+5' are truncated + let op = cache.on_block_revert(&mut DummyTransaction::new(), &correct_id(2)).unwrap(); + assert_eq!(op, CommitOperation::BlockReverted(vec![ + (0, None), + ].into_iter().collect())); + cache.on_transaction_commit(op); + } } diff --git a/core/client/db/src/cache/list_storage.rs b/core/client/db/src/cache/list_storage.rs index a7bfc4dd5856f63934409f6b0abf631e582d8a62..4fd9ecaced25e7afc699fdf0f5c6f05aa4e199a6 100644 --- a/core/client/db/src/cache/list_storage.rs +++ b/core/client/db/src/cache/list_storage.rs @@ -227,6 +227,14 @@ mod meta { unfinalized.remove(*fork_index); } }, + CommitOperation::BlockReverted(ref forks) => { + for (fork_index, updated_fork) in forks.iter().rev() { + match updated_fork { + Some(updated_fork) => unfinalized[*fork_index] = &updated_fork.head().valid_from, + None => { unfinalized.remove(*fork_index); }, + } + } + }, } (finalized, unfinalized).encode() diff --git a/core/client/db/src/cache/mod.rs b/core/client/db/src/cache/mod.rs index 6f7d1bf47d7cf34e22c8d38fbd5efa0e362b4335..9aaea57b0cdb240ed650221fb1554ad5412e63a3 100644 --- a/core/client/db/src/cache/mod.rs +++ b/core/client/db/src/cache/mod.rs @@ -207,7 +207,7 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> { // prepare list of caches that are not update // (we might still need to do some cache maintenance in this case) let missed_caches = self.cache.cache_at.keys() - .filter(|cache| !data_at.contains_key(cache.clone())) + .filter(|cache| !data_at.contains_key(*cache)) .cloned() .collect::>(); @@ -268,6 +268,27 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> { Ok(self) } + + /// When block is reverted. + pub fn on_block_revert( + mut self, + reverted_block: &ComplexBlockId, + ) -> ClientResult { + for (name, cache) in self.cache.cache_at.iter() { + let op = cache.on_block_revert( + &mut self::list_storage::DbStorageTransaction::new( + cache.storage(), + &mut self.tx + ), + reverted_block, + )?; + + assert!(!self.cache_at_op.contains_key(name)); + self.cache_at_op.insert(name.to_owned(), op); + } + + Ok(self) + } } /// Synchronous implementation of database-backed blockchain data cache. diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index 126622364a76a25a2bcb4f9d1ddbab5d40a0e615..8bd0001981611453574f8910046d3efc336f9814 100644 --- a/core/client/db/src/lib.rs +++ b/core/client/db/src/lib.rs @@ -42,7 +42,7 @@ use client::backend::NewBlockState; use client::blockchain::{well_known_cache_keys, HeaderBackend}; use client::{ForkBlocks, ExecutionStrategies}; use client::backend::{StorageCollection, ChildStorageCollection}; -use client::error::Result as ClientResult; +use client::error::{Result as ClientResult, Error as ClientError}; use codec::{Decode, Encode}; use hash_db::{Hasher, Prefix}; use kvdb::{KeyValueDB, DBTransaction}; @@ -83,6 +83,9 @@ const DEFAULT_CHILD_RATIO: (usize, usize) = (1, 10); /// DB-backed patricia trie state, transaction type is an overlay of changes to commit. pub type DbState = state_machine::TrieBackend>, Blake2Hasher>; +/// Re-export the KVDB trait so that one can pass an implementation of it. +pub use kvdb; + /// A reference tracking state. /// /// It makes sure that the hash we are using stays pinned in storage @@ -191,16 +194,28 @@ impl StateBackend for RefTrackingState { /// Database settings. pub struct DatabaseSettings { - /// Cache size in bytes. If `None` default is used. - pub cache_size: Option, /// State cache size. pub state_cache_size: usize, /// Ratio of cache size dedicated to child tries. pub state_cache_child_ratio: Option<(usize, usize)>, - /// Path to the database. - pub path: PathBuf, /// Pruning mode. pub pruning: PruningMode, + /// Where to find the database. + pub source: DatabaseSettingsSrc, +} + +/// Where to find the database.. +pub enum DatabaseSettingsSrc { + /// Load a database from a given path. Recommended for most uses. + Path { + /// Path to the database. + path: PathBuf, + /// Cache size in bytes. If `None` default is used. + cache_size: Option, + }, + + /// Use a custom already-open database. + Custom(Arc), } /// Create an instance of db-backed client. @@ -224,7 +239,7 @@ pub fn new_client( > where Block: BlockT, - E: CodeExecutor + RuntimeInfo, + E: CodeExecutor + RuntimeInfo, S: BuildStorage, { let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?); @@ -417,7 +432,7 @@ impl HeaderMetadata for BlockchainDb { header_metadata.clone(), ); header_metadata - }).ok_or(client::error::Error::UnknownBlock("header not found in db".to_owned())) + }).ok_or(ClientError::UnknownBlock(format!("header not found in db: {}", hash))) }) } @@ -456,8 +471,7 @@ impl BlockImportOperation { } impl client::backend::BlockImportOperation -for BlockImportOperation -where Block: BlockT, + for BlockImportOperation where Block: BlockT, { type State = CachingState, Block>; @@ -776,17 +790,7 @@ impl> Backend { /// /// The pruning window is how old a block must be before the state is pruned. pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult { - Self::new_inner(config, canonicalization_delay) - } - - fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult { - #[cfg(feature = "kvdb-rocksdb")] let db = crate::utils::open_database(&config, columns::META, "full")?; - #[cfg(not(feature = "kvdb-rocksdb"))] - let db = { - log::warn!("Running without the RocksDB feature. The database will NOT be saved."); - Arc::new(kvdb_memorydb::create(crate::utils::NUM_COLUMNS)) - }; Self::from_kvdb(db as Arc<_>, canonicalization_delay, &config) } @@ -794,25 +798,14 @@ impl> Backend { #[cfg(any(test, feature = "test-helpers"))] pub fn new_test(keep_blocks: u32, canonicalization_delay: u64) -> Self { let db = Arc::new(kvdb_memorydb::create(crate::utils::NUM_COLUMNS)); - Self::new_test_db(keep_blocks, canonicalization_delay, db as Arc<_>) - } - - /// Creates a client backend with test settings. - #[cfg(any(test, feature = "test-helpers"))] - pub fn new_test_db(keep_blocks: u32, canonicalization_delay: u64, db: Arc) -> Self { - let db_setting = DatabaseSettings { - cache_size: None, state_cache_size: 16777216, state_cache_child_ratio: Some((50, 100)), - path: Default::default(), pruning: PruningMode::keep_blocks(keep_blocks), + source: DatabaseSettingsSrc::Custom(db), }; - Self::from_kvdb( - db, - canonicalization_delay, - &db_setting, - ).expect("failed to create test-db") + + Self::new(db_setting, canonicalization_delay).expect("failed to create test-db") } fn from_kvdb( @@ -1519,6 +1512,12 @@ impl client::backend::Backend for Backend whe impl client::backend::LocalBackend for Backend where Block: BlockT {} +/// TODO: remove me in #3201 +pub fn unused_sink(cache_tx: crate::cache::DbCacheTransaction) { + cache_tx.on_block_revert(&crate::cache::ComplexBlockId::new(Default::default(), 0.into())).unwrap(); + unimplemented!() +} + #[cfg(test)] mod tests { use hash_db::{HashDB, EMPTY_PREFIX}; @@ -1631,7 +1630,12 @@ mod tests { db.storage.db.clone() }; - let backend = Backend::::new_test_db(1, 0, backing); + let backend = Backend::::new(DatabaseSettings { + state_cache_size: 16777216, + state_cache_child_ratio: Some((50, 100)), + pruning: PruningMode::keep_blocks(1), + source: DatabaseSettingsSrc::Custom(backing), + }, 0).unwrap(); assert_eq!(backend.blockchain().info().best_number, 9); for i in 0..10 { assert!(backend.blockchain().hash(i).unwrap().is_some()) @@ -2222,6 +2226,40 @@ mod tests { } } + #[test] + fn test_tree_route_regression() { + // NOTE: this is a test for a regression introduced in #3665, the result + // of tree_route would be erroneously computed, since it was taking into + // account the `ancestor` in `CachedHeaderMetadata` for the comparison. + // in this test we simulate the same behavior with the side-effect + // triggering the issue being eviction of a previously fetched record + // from the cache, therefore this test is dependent on the LRU cache + // size for header metadata, which is currently set to 5000 elements. + let backend = Backend::::new_test(10000, 10000); + let blockchain = backend.blockchain(); + + let genesis = insert_header(&backend, 0, Default::default(), Vec::new(), Default::default()); + + let block100 = (1..=100).fold(genesis, |parent, n| { + insert_header(&backend, n, parent, Vec::new(), Default::default()) + }); + + let block7000 = (101..=7000).fold(block100, |parent, n| { + insert_header(&backend, n, parent, Vec::new(), Default::default()) + }); + + // This will cause the ancestor of `block100` to be set to `genesis` as a side-effect. + lowest_common_ancestor(blockchain, genesis, block100).unwrap(); + + // While traversing the tree we will have to do 6900 calls to + // `header_metadata`, which will make sure we will exhaust our cache + // which only takes 5000 elements. In particular, the `CachedHeaderMetadata` struct for + // block #100 will be evicted and will get a new value (with ancestor set to its parent). + let tree_route = tree_route(blockchain, block100, block7000).unwrap(); + + assert!(tree_route.retracted().is_empty()); + } + #[test] fn test_leaves_with_complex_block_tree() { let backend: Arc> = Arc::new(Backend::new_test(20, 20)); diff --git a/core/client/db/src/light.rs b/core/client/db/src/light.rs index 111b8e0deb43b59a6f06485897e5da8363b40e51..6e71b88ae73f9c7707900a4e79d218bd8376b079 100644 --- a/core/client/db/src/light.rs +++ b/core/client/db/src/light.rs @@ -70,22 +70,10 @@ impl LightStorage { /// Create new storage with given settings. pub fn new(config: DatabaseSettings) -> ClientResult { - Self::new_inner(config) - } - - #[cfg(feature = "kvdb-rocksdb")] - fn new_inner(config: DatabaseSettings) -> ClientResult { let db = crate::utils::open_database(&config, columns::META, "light")?; Self::from_kvdb(db as Arc<_>) } - #[cfg(not(feature = "kvdb-rocksdb"))] - fn new_inner(_config: DatabaseSettings) -> ClientResult { - log::warn!("Running without the RocksDB feature. The database will NOT be saved."); - let db = Arc::new(kvdb_memorydb::create(crate::utils::NUM_COLUMNS)); - Self::from_kvdb(db as Arc<_>) - } - /// Create new memory-backed `LightStorage` for tests. #[cfg(any(test, feature = "test-helpers"))] pub fn new_test() -> Self { @@ -207,7 +195,7 @@ impl HeaderMetadata for LightStorage { header_metadata.clone(), ); header_metadata - }).ok_or(ClientError::UnknownBlock("header not found in db".to_owned())) + }).ok_or(ClientError::UnknownBlock(format!("header not found in db: {}", hash))) }) } diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index af8c9e379c4c1c99d0522e1e750e429976421ea5..8c81e44ba6bf22800e52a7bfa25904e25782cef9 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -22,6 +22,7 @@ use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use linked_hash_map::{LinkedHashMap, Entry}; use hash_db::Hasher; use sr_primitives::traits::{Block as BlockT, Header}; +use primitives::hexdisplay::HexDisplay; use state_machine::{backend::Backend as StateBackend, TrieBackend}; use log::trace; use super::{StorageCollection, ChildStorageCollection}; @@ -168,7 +169,7 @@ impl Cache { trace!("Reverting enacted block {:?}", block); m.is_canon = true; for a in &m.storage { - trace!("Reverting enacted key {:?}", a); + trace!("Reverting enacted key {:?}", HexDisplay::from(a)); self.lru_storage.remove(a); } for a in &m.child_storage { @@ -188,7 +189,7 @@ impl Cache { trace!("Retracting block {:?}", block); m.is_canon = false; for a in &m.storage { - trace!("Retracted key {:?}", a); + trace!("Retracted key {:?}", HexDisplay::from(a)); self.lru_storage.remove(a); } for a in &m.child_storage { @@ -416,21 +417,16 @@ impl, B: BlockT> CachingState { key: Option<&[u8]>, child_key: Option<&ChildStorageKey>, parent_hash: &Option, - modifications: - &VecDeque> + modifications: &VecDeque> ) -> bool { let mut parent = match *parent_hash { None => { - trace!("Cache lookup skipped for {:?}: no parent hash", key); + trace!("Cache lookup skipped for {:?}: no parent hash", key.as_ref().map(HexDisplay::from)); return false; } Some(ref parent) => parent, }; - if modifications.is_empty() { - trace!("Cache lookup allowed for {:?}", key); - return true; - } // Ignore all storage modified in later blocks // Modifications contains block ordered by the number // We search for our parent in that list first and then for @@ -445,7 +441,7 @@ impl, B: BlockT> CachingState { } if let Some(key) = key { if m.storage.contains(key) { - trace!("Cache lookup skipped for {:?}: modified in a later block", key); + trace!("Cache lookup skipped for {:?}: modified in a later block", HexDisplay::from(&key)); return false; } } @@ -456,7 +452,7 @@ impl, B: BlockT> CachingState { } } } - trace!("Cache lookup skipped for {:?}: parent hash is unknown", key); + trace!("Cache lookup skipped for {:?}: parent hash is unknown", key.as_ref().map(HexDisplay::from)); false } @@ -475,17 +471,17 @@ impl, B: BlockT> StateBackend for CachingState< let local_cache = self.cache.local_cache.upgradable_read(); // Note that local cache makes that lru is not refreshed if let Some(entry) = local_cache.storage.get(key).cloned() { - trace!("Found in local cache: {:?}", key); + trace!("Found in local cache: {:?}", HexDisplay::from(&key)); return Ok(entry) } let mut cache = self.cache.shared_cache.lock(); if Self::is_allowed(Some(key), None, &self.cache.parent_hash, &cache.modifications) { if let Some(entry) = cache.lru_storage.get(key).map(|a| a.clone()) { - trace!("Found in shared cache: {:?}", key); + trace!("Found in shared cache: {:?}", HexDisplay::from(&key)); return Ok(entry) } } - trace!("Cache miss: {:?}", key); + trace!("Cache miss: {:?}", HexDisplay::from(&key)); let value = self.state.storage(key)?; RwLockUpgradableReadGuard::upgrade(local_cache).storage.insert(key.to_vec(), value.clone()); Ok(value) @@ -494,17 +490,17 @@ impl, B: BlockT> StateBackend for CachingState< fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { let local_cache = self.cache.local_cache.upgradable_read(); if let Some(entry) = local_cache.hashes.get(key).cloned() { - trace!("Found hash in local cache: {:?}", key); + trace!("Found hash in local cache: {:?}", HexDisplay::from(&key)); return Ok(entry) } let mut cache = self.cache.shared_cache.lock(); if Self::is_allowed(Some(key), None, &self.cache.parent_hash, &cache.modifications) { if let Some(entry) = cache.lru_hashes.get(key).map(|a| a.0.clone()) { - trace!("Found hash in shared cache: {:?}", key); + trace!("Found hash in shared cache: {:?}", HexDisplay::from(&key)); return Ok(entry) } } - trace!("Cache hash miss: {:?}", key); + trace!("Cache hash miss: {:?}", HexDisplay::from(&key)); let hash = self.state.storage_hash(key)?; RwLockUpgradableReadGuard::upgrade(local_cache).hashes.insert(key.to_vec(), hash.clone()); Ok(hash) @@ -728,4 +724,44 @@ mod tests { // 32 key, 2 byte size assert_eq!(shared.lock().used_storage_cache_size(), 34 /* bytes */); } + + #[test] + fn fix_storage_mismatch_issue() { + let _ = ::env_logger::try_init(); + let root_parent = H256::random(); + + let key = H256::random()[..].to_vec(); + + let h0 = H256::random(); + let h1 = H256::random(); + + let shared = new_shared_cache::(256*1024, (0, 1)); + let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(root_parent.clone())); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![2]))], vec![], Some(h0.clone()), Some(0), || true); + + let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h0.clone())); + s.cache.sync_cache(&[], &[], vec![(key.clone(), Some(vec![3]))], vec![], Some(h1.clone()), Some(1), || true); + + let mut s = CachingState::new(InMemory::::default(), shared.clone(), Some(h1.clone())); + assert_eq!(s.storage(&key).unwrap(), Some(vec![3])); + + // Restart (or unknown block?), clear caches. + { + let mut cache = s.cache.shared_cache.lock(); + let cache = &mut *cache; + cache.lru_storage.clear(); + cache.lru_hashes.clear(); + cache.lru_child_storage.clear(); + cache.modifications.clear(); + } + + // New value is written because of cache miss. + s.cache.local_cache.write().storage.insert(key.clone(), Some(vec![42])); + + // New value is propagated. + s.cache.sync_cache(&[], &[], vec![], vec![], None, None, || true); + + let s = CachingState::new(InMemory::::default(), shared.clone(), Some(h1.clone())); + assert_eq!(s.storage(&key).unwrap(), None); + } } diff --git a/core/client/db/src/utils.rs b/core/client/db/src/utils.rs index 70f0ff20588bc58a82dfb0d021f1c9ee76fe0781..0a6112abe7a6edca2b990612d1a08aced74783fb 100644 --- a/core/client/db/src/utils.rs +++ b/core/client/db/src/utils.rs @@ -17,7 +17,6 @@ //! Db-based backend utility structures and functions, used by both //! full and light storages. -#[cfg(feature = "kvdb-rocksdb")] use std::sync::Arc; use std::{io, convert::TryInto}; @@ -34,8 +33,7 @@ use sr_primitives::traits::{ Block as BlockT, Header as HeaderT, Zero, UniqueSaturatedFrom, UniqueSaturatedInto, }; -#[cfg(feature = "kvdb-rocksdb")] -use crate::DatabaseSettings; +use crate::{DatabaseSettings, DatabaseSettingsSrc}; /// Number of columns in the db. Must be the same for both full && light dbs. /// Otherwise RocksDb will fail to open database && check its type. @@ -206,16 +204,26 @@ pub fn db_err(err: io::Error) -> client::error::Error { } /// Open RocksDB database. -#[cfg(feature = "kvdb-rocksdb")] pub fn open_database( config: &DatabaseSettings, col_meta: Option, db_type: &str ) -> client::error::Result> { - let mut db_config = DatabaseConfig::with_columns(Some(NUM_COLUMNS)); - db_config.memory_budget = config.cache_size; - let path = config.path.to_str().ok_or_else(|| client::error::Error::Backend("Invalid database path".into()))?; - let db = Database::open(&db_config, &path).map_err(db_err)?; + let db: Arc = match &config.source { + #[cfg(feature = "kvdb-rocksdb")] + DatabaseSettingsSrc::Path { path, cache_size } => { + let mut db_config = DatabaseConfig::with_columns(Some(NUM_COLUMNS)); + db_config.memory_budget = *cache_size; + let path = path.to_str().ok_or_else(|| client::error::Error::Backend("Invalid database path".into()))?; + Arc::new(Database::open(&db_config, &path).map_err(db_err)?) + }, + #[cfg(not(feature = "kvdb-rocksdb"))] + DatabaseSettingsSrc::Path { .. } => { + let msg = "Try to open RocksDB database with RocksDB disabled".into(); + return Err(client::error::Error::Backend(msg)); + }, + DatabaseSettingsSrc::Custom(db) => db.clone(), + }; // check database type match db.get(col_meta, meta_keys::TYPE).map_err(db_err)? { @@ -232,7 +240,7 @@ pub fn open_database( }, } - Ok(Arc::new(db)) + Ok(db) } /// Read database column entry for the given block. diff --git a/core/client/header-metadata/src/lib.rs b/core/client/header-metadata/src/lib.rs index cce45f264e8ea08bb543323f2bfcafd6b5c3165a..a8c3886020e271daac7fafb9a75cbe4cd4e2548b 100644 --- a/core/client/header-metadata/src/lib.rs +++ b/core/client/header-metadata/src/lib.rs @@ -122,7 +122,7 @@ pub fn tree_route>( // numbers are equal now. walk backwards until the block is the same - while to != from { + while to.hash != from.hash { to_branch.push(HashAndNumber { number: to.number, hash: to.hash, @@ -257,7 +257,7 @@ impl HeaderMetadata for HeaderMetadataCache { } /// Cached header metadata. Used to efficiently traverse the tree. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone)] pub struct CachedHeaderMetadata { /// Hash of the header. pub hash: Block::Hash, diff --git a/core/client/src/block_builder/block_builder.rs b/core/client/src/block_builder/block_builder.rs index 08711469e9a1b5164721bd186f914a4389805b3f..f5cd6a9f660b0925bc2984529d2d60c519cfba3f 100644 --- a/core/client/src/block_builder/block_builder.rs +++ b/core/client/src/block_builder/block_builder.rs @@ -22,6 +22,7 @@ use sr_primitives::traits::{ Header as HeaderT, Hash, Block as BlockT, One, HashFor, ProvideRuntimeApi, ApiRef, DigestFor, }; use primitives::{H256, ExecutionContext}; +use state_machine::StorageProof; use crate::blockchain::HeaderBackend; use crate::runtime_api::{Core, ApiExt}; use crate::error; @@ -140,7 +141,7 @@ where /// /// The proof will be `Some(_)`, if proof recording was enabled while creating /// the block builder. - pub fn bake_and_extract_proof(mut self) -> error::Result<(Block, Option>>)> { + pub fn bake_and_extract_proof(mut self) -> error::Result<(Block, Option)> { self.bake_impl()?; let proof = self.api.extract_proof(); diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs index ebb882709d8a686cbbd0fdf271a8da2f204bfcaa..e634fcf8faa9f6e35547cc975ddbc5cce6583e88 100644 --- a/core/client/src/call_executor.rs +++ b/core/client/src/call_executor.rs @@ -21,13 +21,13 @@ use sr_primitives::{ }; use state_machine::{ self, OverlayedChanges, Ext, ExecutionManager, StateMachine, ExecutionStrategy, - backend::Backend as _, ChangesTrieTransaction, + backend::Backend as _, ChangesTrieTransaction, StorageProof, }; use executor::{RuntimeVersion, RuntimeInfo, NativeVersion}; use hash_db::Hasher; use primitives::{ - offchain::{self, NeverOffchainExt}, H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, - traits::CodeExecutor, + offchain::OffchainExt, H256, Blake2Hasher, NativeOrEncoded, NeverNativeValue, + traits::{CodeExecutor, KeystoreExt}, }; use crate::runtime_api::{ProofRecorder, InitializeBlock}; @@ -47,15 +47,13 @@ where /// Execute a call to a contract on top of state in a block of given hash. /// /// No changes are made. - fn call< - O: offchain::Externalities, - >( + fn call( &self, id: &BlockId, method: &str, call_data: &[u8], strategy: ExecutionStrategy, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, ) -> Result, error::Error>; /// Execute a contextual call on top of state in a block of a given hash. @@ -65,7 +63,6 @@ where /// of the execution context. fn contextual_call< 'a, - O: offchain::Externalities, IB: Fn() -> error::Result<()>, EM: Fn( Result, Self::Error>, @@ -83,7 +80,7 @@ where initialize_block: InitializeBlock<'a, B>, execution_manager: ExecutionManager, native_call: Option, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, proof_recorder: &Option>>>, enable_keystore: bool, ) -> error::Result> where ExecutionManager: Clone; @@ -97,11 +94,10 @@ where /// /// No changes are made. fn call_at_state< - O: offchain::Externalities, S: state_machine::Backend, F: FnOnce( Result, Self::Error>, - Result, Self::Error> + Result, Self::Error>, ) -> Result, Self::Error>, R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, @@ -112,7 +108,7 @@ where call_data: &[u8], manager: ExecutionManager, native_call: Option, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, ) -> Result< ( NativeOrEncoded, @@ -131,7 +127,7 @@ where overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] - ) -> Result<(Vec, Vec>), error::Error> { + ) -> Result<(Vec, StorageProof), error::Error> { let trie_state = state.as_trie_backend() .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) @@ -149,7 +145,7 @@ where overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] - ) -> Result<(Vec, Vec>), error::Error>; + ) -> Result<(Vec, StorageProof), error::Error>; /// Get runtime version if supported. fn native_runtime_version(&self) -> Option<&NativeVersion>; @@ -191,18 +187,18 @@ impl Clone for LocalCallExecutor where E: Clone { impl CallExecutor for LocalCallExecutor where B: backend::Backend, - E: CodeExecutor + RuntimeInfo, + E: CodeExecutor + RuntimeInfo, Block: BlockT, { type Error = E::Error; - fn call( + fn call( &self, id: &BlockId, method: &str, call_data: &[u8], strategy: ExecutionStrategy, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, ) -> error::Result> { let mut changes = OverlayedChanges::default(); let state = self.backend.state_at(*id)?; @@ -214,7 +210,7 @@ where &self.executor, method, call_data, - self.keystore.clone(), + self.keystore.clone().map(KeystoreExt), ).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( strategy.get_manager(), false, @@ -227,7 +223,6 @@ where fn contextual_call< 'a, - O: offchain::Externalities, IB: Fn() -> error::Result<()>, EM: Fn( Result, Self::Error>, @@ -245,7 +240,7 @@ where initialize_block: InitializeBlock<'a, Block>, execution_manager: ExecutionManager, native_call: Option, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, recorder: &Option>>>, enable_keystore: bool, ) -> Result, error::Error> where ExecutionManager: Clone { @@ -259,7 +254,7 @@ where } let keystore = if enable_keystore { - self.keystore.clone() + self.keystore.clone().map(KeystoreExt) } else { None }; @@ -326,7 +321,6 @@ where &mut overlay, &state, self.backend.changes_trie_storage(), - NeverOffchainExt::new(), None, ); let version = self.executor.runtime_version(&mut ext); @@ -335,11 +329,10 @@ where } fn call_at_state< - O: offchain::Externalities, S: state_machine::Backend, F: FnOnce( Result, Self::Error>, - Result, Self::Error> + Result, Self::Error>, ) -> Result, Self::Error>, R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, @@ -350,7 +343,7 @@ where call_data: &[u8], manager: ExecutionManager, native_call: Option, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, ) -> error::Result<( NativeOrEncoded, (S::Transaction, ::Out), @@ -364,7 +357,7 @@ where &self.executor, method, call_data, - self.keystore.clone(), + self.keystore.clone().map(KeystoreExt), ).execute_using_consensus_failure_handler( manager, true, @@ -384,14 +377,14 @@ where overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] - ) -> Result<(Vec, Vec>), error::Error> { + ) -> Result<(Vec, StorageProof), error::Error> { state_machine::prove_execution_on_trie_backend( trie_state, overlay, &self.executor, method, call_data, - self.keystore.clone(), + self.keystore.clone().map(KeystoreExt), ) .map_err(Into::into) } diff --git a/core/client/src/cht.rs b/core/client/src/cht.rs index 63a5b39c26904d8cd783fe0581fb8b0b4ef8036f..aff875032d3570bec3329630d6f0df7f3e1a7292 100644 --- a/core/client/src/cht.rs +++ b/core/client/src/cht.rs @@ -30,7 +30,7 @@ use trie; use primitives::{H256, convert_hash}; use sr_primitives::traits::{Header as HeaderT, SimpleArithmetic, Zero, One}; use state_machine::backend::InMemory as InMemoryState; -use state_machine::{MemoryDB, TrieBackend, Backend as StateBackend, +use state_machine::{MemoryDB, TrieBackend, Backend as StateBackend, StorageProof, prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend}; use crate::error::{Error as ClientError, Result as ClientResult}; @@ -88,7 +88,7 @@ pub fn build_proof( cht_num: Header::Number, blocks: BlocksI, hashes: HashesI -) -> ClientResult>> +) -> ClientResult where Header: HeaderT, Hasher: hash_db::Hasher, @@ -114,7 +114,7 @@ pub fn check_proof( local_root: Header::Hash, local_number: Header::Number, remote_hash: Header::Hash, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult<()> where Header: HeaderT, diff --git a/core/client/src/client.rs b/core/client/src/client.rs index aff099233a41e05da7441bd55517bb1ab31bedc6..71d6e4f01d63788f8b16668affc569d92bb9a7e1 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -28,7 +28,7 @@ use hash_db::{Hasher, Prefix}; use primitives::{ Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, NeverNativeValue, ExecutionContext, NativeOrEncoded, storage::{StorageKey, StorageData, well_known_keys}, - offchain::{NeverOffchainExt, self}, traits::CodeExecutor, + offchain::{OffchainExt, self}, traits::CodeExecutor, }; use substrate_telemetry::{telemetry, SUBSTRATE_INFO}; use sr_primitives::{ @@ -43,7 +43,7 @@ use state_machine::{ DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, ExecutionStrategy, ExecutionManager, prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage, ChangesTrieTransaction, ChangesTrieConfigurationRange, key_changes, key_changes_proof, - OverlayedChanges, BackendTrustLevel, + OverlayedChanges, BackendTrustLevel, StorageProof, merge_storage_proofs, }; use executor::{RuntimeVersion, RuntimeInfo}; use consensus::{ @@ -248,7 +248,7 @@ pub fn new_in_mem( Block, RA >> where - E: CodeExecutor + RuntimeInfo, + E: CodeExecutor + RuntimeInfo, S: BuildStorage, Block: BlockT, { @@ -264,7 +264,7 @@ pub fn new_with_backend( keystore: Option, ) -> error::Result, Block, RA>> where - E: CodeExecutor + RuntimeInfo, + E: CodeExecutor + RuntimeInfo, S: BuildStorage, Block: BlockT, B: backend::LocalBackend @@ -342,15 +342,6 @@ impl Client where self.backend.state_at(*block) } - /// Expose backend reference. To be used in tests only - #[doc(hidden)] - #[deprecated(note="Rather than relying on `client` to provide this, access \ - to the backend should be handled at setup only - see #1134. This function \ - will be removed once that is in place.")] - pub fn backend(&self) -> &Arc { - &self.backend - } - /// Given a `BlockId` and a key prefix, return the matching child storage keys in that block. pub fn storage_keys(&self, id: &BlockId, key_prefix: &StorageKey) -> error::Result> { let keys = self.state_at(id)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect(); @@ -430,7 +421,7 @@ impl Client where } /// Reads storage value at a given block + key, returning read proof. - pub fn read_proof(&self, id: &BlockId, keys: I) -> error::Result>> where + pub fn read_proof(&self, id: &BlockId, keys: I) -> error::Result where I: IntoIterator, I::Item: AsRef<[u8]>, { @@ -446,7 +437,7 @@ impl Client where id: &BlockId, storage_key: &[u8], keys: I, - ) -> error::Result>> where + ) -> error::Result where I: IntoIterator, I::Item: AsRef<[u8]>, { @@ -463,14 +454,14 @@ impl Client where id: &BlockId, method: &str, call_data: &[u8] - ) -> error::Result<(Vec, Vec>)> { + ) -> error::Result<(Vec, StorageProof)> { let state = self.state_at(id)?; let header = self.prepare_environment_block(id)?; prove_execution(state, header, &self.executor, method, call_data) } /// Reads given header and generates CHT-based header proof. - pub fn header_proof(&self, id: &BlockId) -> error::Result<(Block::Header, Vec>)> { + pub fn header_proof(&self, id: &BlockId) -> error::Result<(Block::Header, StorageProof)> { self.header_proof_with_cht_size(id, cht::size()) } @@ -486,7 +477,7 @@ impl Client where &self, id: &BlockId, cht_size: NumberFor, - ) -> error::Result<(Block::Header, Vec>)> { + ) -> error::Result<(Block::Header, StorageProof)> { let proof_error = || error::Error::Backend(format!("Failed to generate header proof for {:?}", id)); let header = self.backend.blockchain().expect_header(*id)?; let block_num = *header.number(); @@ -705,18 +696,18 @@ impl Client where &self, cht_size: NumberFor, blocks: I - ) -> error::Result>> { + ) -> error::Result { // most probably we have touched several changes tries that are parts of the single CHT // => GroupBy changes tries by CHT number and then gather proof for the whole group at once - let mut proof = HashSet::new(); + let mut proofs = Vec::new(); cht::for_each_cht_group::(cht_size, blocks, |_, cht_num, cht_blocks| { let cht_proof = self.changes_trie_roots_proof_at_cht(cht_size, cht_num, cht_blocks)?; - proof.extend(cht_proof); + proofs.push(cht_proof); Ok(()) }, ())?; - Ok(proof.into_iter().collect()) + Ok(merge_storage_proofs(proofs)) } /// Generates CHT-based proof for roots of changes tries at given blocks (that are part of single CHT). @@ -725,7 +716,7 @@ impl Client where cht_size: NumberFor, cht_num: NumberFor, blocks: Vec> - ) -> error::Result>> { + ) -> error::Result { let cht_start = cht::start_number(cht_size, cht_num); let mut current_num = cht_start; let cht_range = ::std::iter::from_fn(|| { @@ -1058,18 +1049,27 @@ impl Client where }), } }; - let (_, storage_update, changes_update) = self.executor.call_at_state::<_, _, _, NeverNativeValue, fn() -> _>( - transaction_state, - &mut overlay, - "Core_execute_block", - &::new(import_headers.pre().clone(), body.unwrap_or_default()).encode(), - match origin { - BlockOrigin::NetworkInitialSync => get_execution_manager(self.execution_strategies().syncing), - _ => get_execution_manager(self.execution_strategies().importing), - }, - None, - NeverOffchainExt::new(), - )?; + + let encoded_block = ::encode_from( + import_headers.pre(), + &body.unwrap_or_default() + ); + + let (_, storage_update, changes_update) = self.executor + .call_at_state::<_, _, NeverNativeValue, fn() -> _>( + transaction_state, + &mut overlay, + "Core_execute_block", + &encoded_block, + match origin { + BlockOrigin::NetworkInitialSync => get_execution_manager( + self.execution_strategies().syncing, + ), + _ => get_execution_manager(self.execution_strategies().importing), + }, + None, + None, + )?; overlay.commit_prospective(); @@ -1116,7 +1116,7 @@ impl Client where // then some other block is the common ancestor. if route_from_best.common_block().hash != block { // NOTE: we're setting the finalized block as best block, this might - // be slightly innacurate since we might have a "better" block + // 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 // an accurate "best" block need to go through `SelectChain` @@ -1460,12 +1460,13 @@ impl CallRuntimeAt for Client where }; let capabilities = context.capabilities(); - let mut offchain_extensions = match context { - ExecutionContext::OffchainCall(ext) => ext.map(|x| x.0), - _ => None, - }.map(|ext| offchain::LimitedExternalities::new(capabilities, ext)); + let offchain_extensions = if let ExecutionContext::OffchainCall(Some(ext)) = context { + Some(OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0))) + } else { + None + }; - self.executor.contextual_call::<_, _, fn(_,_) -> _,_,_>( + self.executor.contextual_call::<_, fn(_,_) -> _,_,_>( || core_api.initialize_block(at, &self.prepare_environment_block(at)?), at, function, @@ -1474,7 +1475,7 @@ impl CallRuntimeAt for Client where initialize_block, manager, native_call, - offchain_extensions.as_mut(), + offchain_extensions, recorder, capabilities.has(offchain::Capability::Keystore), ) @@ -1870,7 +1871,7 @@ pub(crate) mod tests { use consensus::{BlockOrigin, SelectChain}; use test_client::{ prelude::*, - client_db::{Backend, DatabaseSettings, PruningMode}, + client_db::{Backend, DatabaseSettings, DatabaseSettingsSrc, PruningMode}, runtime::{self, Block, Transfer, RuntimeApi, TestAPI}, }; @@ -2754,11 +2755,13 @@ pub(crate) mod tests { // states let backend = Arc::new(Backend::new( DatabaseSettings { - cache_size: None, state_cache_size: 1 << 20, state_cache_child_ratio: None, - path: tmp.path().into(), pruning: PruningMode::ArchiveAll, + source: DatabaseSettingsSrc::Path { + path: tmp.path().into(), + cache_size: None, + } }, u64::max_value(), ).unwrap()); diff --git a/core/client/src/genesis.rs b/core/client/src/genesis.rs index 0c0d49f8eaccc937c4a7a5f0ed2a2bc7ecbd09cf..031b2dc0ad7df2a58b51e03798c73f07f8134f1e 100644 --- a/core/client/src/genesis.rs +++ b/core/client/src/genesis.rs @@ -53,7 +53,7 @@ mod tests { runtime::{Hash, Transfer, Block, BlockNumber, Header, Digest}, AccountKeyring, Sr25519Keyring, }; - use primitives::{Blake2Hasher, map, offchain::NeverOffchainExt}; + use primitives::{Blake2Hasher, map}; use hex_literal::*; native_executor_instance!( @@ -63,7 +63,7 @@ mod tests { ); fn executor() -> executor::NativeExecutor { - executor::NativeExecutor::new(None) + executor::NativeExecutor::new(executor::WasmExecutionMethod::Interpreted, None) } fn construct_block( @@ -93,7 +93,7 @@ mod tests { StateMachine::new( backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - NeverOffchainExt::new(), + None, &mut overlay, &executor(), "Core_initialize_block", @@ -107,7 +107,7 @@ mod tests { StateMachine::new( backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - NeverOffchainExt::new(), + None, &mut overlay, &executor(), "BlockBuilder_apply_extrinsic", @@ -121,7 +121,7 @@ mod tests { let (ret_data, _, _) = StateMachine::new( backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - NeverOffchainExt::new(), + None, &mut overlay, &executor(), "BlockBuilder_finalize_block", @@ -169,7 +169,7 @@ mod tests { let _ = StateMachine::new( &backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - NeverOffchainExt::new(), + None, &mut overlay, &executor(), "Core_execute_block", @@ -199,7 +199,7 @@ mod tests { let _ = StateMachine::new( &backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - NeverOffchainExt::new(), + None, &mut overlay, &executor(), "Core_execute_block", @@ -229,7 +229,7 @@ mod tests { let r = StateMachine::new( &backend, Some(&InMemoryChangesTrieStorage::<_, u64>::new()), - NeverOffchainExt::new(), + None, &mut overlay, &executor(), "Core_execute_block", diff --git a/core/client/src/in_mem.rs b/core/client/src/in_mem.rs index 99dc1b62634ba13d8caa80fe00ca7673c1e0c3ab..5c35400d7743c2a7eb054f8f3ea35cf6dacdfbf4 100644 --- a/core/client/src/in_mem.rs +++ b/core/client/src/in_mem.rs @@ -324,7 +324,7 @@ impl HeaderMetadata for Blockchain { fn header_metadata(&self, hash: Block::Hash) -> Result, Self::Error> { self.header(BlockId::hash(hash))?.map(|header| CachedHeaderMetadata::from(&header)) - .ok_or(error::Error::UnknownBlock("header not found".to_owned())) + .ok_or(error::Error::UnknownBlock(format!("header not found: {}", hash))) } fn insert_header_metadata(&self, _hash: Block::Hash, _metadata: CachedHeaderMetadata) { diff --git a/core/client/src/lib.rs b/core/client/src/lib.rs index 70c9b759268fc8043f12b2c51d550747f15325d8..57147eb18b181e51803e30955b039d8dfb74aeac 100644 --- a/core/client/src/lib.rs +++ b/core/client/src/lib.rs @@ -49,7 +49,7 @@ //! use substrate_client::{Client, in_mem::Backend, LocalCallExecutor}; //! use primitives::Blake2Hasher; //! use sr_primitives::{StorageOverlay, ChildrenStorageOverlay}; -//! use executor::NativeExecutor; +//! use executor::{NativeExecutor, WasmExecutionMethod}; //! //! // In this example, we're using the `Block` and `RuntimeApi` types from the //! // `substrate-test-runtime-client` crate. These types are automatically generated when @@ -62,7 +62,7 @@ //! backend.clone(), //! LocalCallExecutor::new( //! backend.clone(), -//! NativeExecutor::::new(None), +//! NativeExecutor::::new(WasmExecutionMethod::Interpreted, None), //! None, //! ), //! // This parameter provides the storage for the chain genesis. @@ -122,7 +122,7 @@ pub use crate::client::{ #[cfg(feature = "std")] pub use crate::notifications::{StorageEventStream, StorageChangeSet}; #[cfg(feature = "std")] -pub use state_machine::ExecutionStrategy; +pub use state_machine::{ExecutionStrategy, StorageProof}; #[cfg(feature = "std")] pub use crate::leaves::LeafSet; #[cfg(feature = "std")] diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs index ec182cca11fe0916262066d22a298a62feb41c25..7f54004ae67222245e526af8b7087c80c07b0ecb 100644 --- a/core/client/src/light/call_executor.rs +++ b/core/client/src/light/call_executor.rs @@ -16,21 +16,20 @@ //! Methods that light client could use to execute runtime calls. -use std::{ - collections::HashSet, sync::Arc, panic::UnwindSafe, result, - cell::RefCell, rc::Rc, -}; +use std::{sync::Arc, panic::UnwindSafe, result, cell::RefCell, rc::Rc}; use codec::{Encode, Decode}; use primitives::{ - offchain, H256, Blake2Hasher, convert_hash, NativeOrEncoded, + offchain::OffchainExt, H256, Blake2Hasher, convert_hash, NativeOrEncoded, traits::CodeExecutor, }; -use sr_primitives::generic::BlockId; -use sr_primitives::traits::{One, Block as BlockT, Header as HeaderT, NumberFor}; +use sr_primitives::{ + generic::BlockId, traits::{One, Block as BlockT, Header as HeaderT, NumberFor}, +}; use state_machine::{ self, Backend as StateBackend, OverlayedChanges, ExecutionStrategy, create_proof_check_backend, - execution_proof_check_on_trie_backend, ExecutionManager, ChangesTrieTransaction, + execution_proof_check_on_trie_backend, ExecutionManager, ChangesTrieTransaction, StorageProof, + merge_storage_proofs, }; use hash_db::Hasher; @@ -74,15 +73,13 @@ impl CallExecutor for { type Error = ClientError; - fn call< - O: offchain::Externalities, - >( + fn call( &self, id: &BlockId, method: &str, call_data: &[u8], strategy: ExecutionStrategy, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, ) -> ClientResult> { match self.backend.is_local_state_available(id) { true => self.local.call(id, method, call_data, strategy, side_effects_handler), @@ -92,7 +89,6 @@ impl CallExecutor for fn contextual_call< 'a, - O: offchain::Externalities, IB: Fn() -> ClientResult<()>, EM: Fn( Result, Self::Error>, @@ -110,7 +106,7 @@ impl CallExecutor for initialize_block: InitializeBlock<'a, Block>, _manager: ExecutionManager, native_call: Option, - side_effects_handler: Option<&mut O>, + side_effects_handler: Option, recorder: &Option>>>, enable_keystore: bool, ) -> ClientResult> where ExecutionManager: Clone { @@ -119,7 +115,6 @@ impl CallExecutor for match self.backend.is_local_state_available(at) { true => CallExecutor::contextual_call::< - _, _, fn( Result, Local::Error>, @@ -153,7 +148,6 @@ impl CallExecutor for } fn call_at_state< - O: offchain::Externalities, S: StateBackend, FF: FnOnce( Result, Self::Error>, @@ -168,7 +162,7 @@ impl CallExecutor for _call_data: &[u8], _manager: ExecutionManager, _native_call: Option, - _side_effects_handler: Option<&mut O>, + _side_effects_handler: Option, ) -> ClientResult<( NativeOrEncoded, (S::Transaction, ::Out), @@ -183,7 +177,7 @@ impl CallExecutor for _changes: &mut OverlayedChanges, _method: &str, _call_data: &[u8] - ) -> ClientResult<(Vec, Vec>)> { + ) -> ClientResult<(Vec, StorageProof)> { Err(ClientError::NotAvailableOnLightClient) } @@ -202,7 +196,7 @@ pub fn prove_execution( executor: &E, method: &str, call_data: &[u8], -) -> ClientResult<(Vec, Vec>)> +) -> ClientResult<(Vec, StorageProof)> where Block: BlockT, S: StateBackend, @@ -222,11 +216,7 @@ pub fn prove_execution( // execute method + record execution proof let (result, exec_proof) = executor.prove_at_trie_state(&trie_state, &mut changes, method, call_data)?; - let total_proof = init_proof.into_iter() - .chain(exec_proof.into_iter()) - .collect::>() - .into_iter() - .collect(); + let total_proof = merge_storage_proofs(vec![init_proof, exec_proof]); Ok((result, total_proof)) } @@ -238,15 +228,14 @@ pub fn prove_execution( pub fn check_execution_proof( executor: &E, request: &RemoteCallRequest
, - remote_proof: Vec>, + remote_proof: StorageProof, ) -> ClientResult> where Header: HeaderT, - E: CodeExecutor, - H: Hasher, - H::Out: Ord + 'static, + E: CodeExecutor, + H: Hasher, { - check_execution_proof_with_make_header( + check_execution_proof_with_make_header::( executor, request, remote_proof, @@ -263,14 +252,13 @@ pub fn check_execution_proof( fn check_execution_proof_with_make_header Header>( executor: &E, request: &RemoteCallRequest
, - remote_proof: Vec>, + remote_proof: StorageProof, make_next_header: MakeNextHeader, ) -> ClientResult> where Header: HeaderT, - E: CodeExecutor, - H: Hasher, - H::Out: Ord + 'static, + E: CodeExecutor, + H: Hasher, { let local_state_root = request.header.state_root(); let root: H::Out = convert_hash(&local_state_root); @@ -301,33 +289,32 @@ fn check_execution_proof_with_make_header for DummyCallExecutor { type Error = ClientError; - fn call( + fn call( &self, _id: &BlockId, _method: &str, _call_data: &[u8], _strategy: ExecutionStrategy, - _side_effects_handler: Option<&mut O>, + _side_effects_handler: Option, ) -> Result, ClientError> { Ok(vec![42]) } fn contextual_call< 'a, - O: offchain::Externalities, IB: Fn() -> ClientResult<()>, EM: Fn( Result, Self::Error>, @@ -345,7 +332,7 @@ mod tests { _initialize_block: InitializeBlock<'a, Block>, _execution_manager: ExecutionManager, _native_call: Option, - _side_effects_handler: Option<&mut O>, + _side_effects_handler: Option, _proof_recorder: &Option>>>, _enable_keystore: bool, ) -> ClientResult> where ExecutionManager: Clone { @@ -357,7 +344,6 @@ mod tests { } fn call_at_state< - O: offchain::Externalities, S: state_machine::Backend, F: FnOnce( Result, Self::Error>, @@ -372,7 +358,7 @@ mod tests { _call_data: &[u8], _manager: ExecutionManager, _native_call: Option, - _side_effects_handler: Option<&mut O>, + _side_effects_handler: Option, ) -> Result< ( NativeOrEncoded, @@ -390,7 +376,7 @@ mod tests { _overlay: &mut OverlayedChanges, _method: &str, _call_data: &[u8] - ) -> Result<(Vec, Vec>), ClientError> { + ) -> Result<(Vec, StorageProof), ClientError> { unreachable!() } @@ -399,6 +385,10 @@ mod tests { } } + fn local_executor() -> NativeExecutor { + NativeExecutor::new(WasmExecutionMethod::Interpreted, None) + } + #[test] fn execution_proof_is_generated_and_checked() { fn execute(remote_client: &TestClient, at: u64, method: &'static str) -> (Vec, Vec) { @@ -413,14 +403,17 @@ mod tests { ).unwrap(); // check remote execution proof locally - let local_executor = NativeExecutor::::new(None); - let local_result = check_execution_proof(&local_executor, &RemoteCallRequest { - block: test_client::runtime::Hash::default(), - header: remote_header, - method: method.into(), - call_data: vec![], - retry_count: None, - }, remote_execution_proof).unwrap(); + let local_result = check_execution_proof::<_, _, Blake2Hasher>( + &local_executor(), + &RemoteCallRequest { + block: test_client::runtime::Hash::default(), + header: remote_header, + method: method.into(), + call_data: vec![], + retry_count: None, + }, + remote_execution_proof, + ).unwrap(); (remote_result, local_result) } @@ -437,9 +430,8 @@ mod tests { ).unwrap(); // check remote execution proof locally - let local_executor = NativeExecutor::::new(None); - let execution_result = check_execution_proof_with_make_header( - &local_executor, + let execution_result = check_execution_proof_with_make_header::<_, _, Blake2Hasher, _>( + &local_executor(), &RemoteCallRequest { block: test_client::runtime::Hash::default(), header: remote_header, @@ -516,7 +508,7 @@ mod tests { "test_method", &[], ExecutionStrategy::NativeElseWasm, - NeverOffchainExt::new(), + None, ).unwrap(), vec![42], ); @@ -526,7 +518,7 @@ mod tests { "test_method", &[], ExecutionStrategy::NativeElseWasm, - NeverOffchainExt::new(), + None, ); match call_on_unavailable { diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs index 3c4387209a49078abaf9b59ca144e46478fd2fc8..6ae28b748c5272d4ac4577d24bfeceadcebde8e8 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -23,7 +23,7 @@ use std::future::Future; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use codec::{Decode, Encode}; -use primitives::{ChangesTrieConfiguration, convert_hash, traits::CodeExecutor}; +use primitives::{ChangesTrieConfiguration, convert_hash, traits::CodeExecutor, H256}; use sr_primitives::traits::{ Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor, SimpleArithmetic, CheckedConversion, Zero, @@ -33,6 +33,7 @@ use state_machine::{ TrieBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage, read_child_proof_check, }; +pub use state_machine::StorageProof; use crate::cht; use crate::error::{Error as ClientError, Result as ClientResult}; @@ -129,7 +130,7 @@ pub struct ChangesProof { pub roots: BTreeMap, /// The proofs for all changes tries roots that have been touched AND are /// missing from the requester' node. It is a map of CHT number => proof. - pub roots_proof: Vec>, + pub roots_proof: StorageProof, } /// Remote block body request @@ -186,25 +187,25 @@ pub trait FetchChecker: Send + Sync { &self, request: &RemoteHeaderRequest, header: Option, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult; /// Check remote storage read proof. fn check_read_proof( &self, request: &RemoteReadRequest, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult, Option>>>; /// Check remote storage read proof. fn check_read_child_proof( &self, request: &RemoteReadChildRequest, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult, Option>>>; /// Check remote method execution proof. fn check_execution_proof( &self, request: &RemoteCallRequest, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult>; /// Check remote changes query proof. fn check_changes_proof( @@ -318,7 +319,7 @@ impl> LightDataChecker { &self, cht_size: NumberFor, remote_roots: &BTreeMap, B::Hash>, - remote_roots_proof: Vec>, + remote_roots_proof: StorageProof, ) -> ClientResult<()> where H: Hasher, @@ -370,16 +371,15 @@ impl> LightDataChecker { impl FetchChecker for LightDataChecker where Block: BlockT, - E: CodeExecutor, - H: Hasher, - H::Out: Ord + 'static, + E: CodeExecutor, + H: Hasher, S: BlockchainStorage, { fn check_header_proof( &self, request: &RemoteHeaderRequest, remote_header: Option, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult { let remote_header = remote_header.ok_or_else(|| ClientError::from(ClientError::InvalidCHTProof))?; @@ -395,7 +395,7 @@ impl FetchChecker for LightDataChecker fn check_read_proof( &self, request: &RemoteReadRequest, - remote_proof: Vec>, + remote_proof: StorageProof, ) -> ClientResult, Option>>> { read_proof_check::( convert_hash(request.header.state_root()), @@ -407,7 +407,7 @@ impl FetchChecker for LightDataChecker fn check_read_child_proof( &self, request: &RemoteReadChildRequest, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult, Option>>> { read_child_proof_check::( convert_hash(request.header.state_root()), @@ -420,7 +420,7 @@ impl FetchChecker for LightDataChecker fn check_execution_proof( &self, request: &RemoteCallRequest, - remote_proof: Vec> + remote_proof: StorageProof, ) -> ClientResult> { check_execution_proof::<_, _, H>(&self.executor, request, remote_proof) } @@ -503,7 +503,7 @@ pub mod tests { use parking_lot::Mutex; use codec::Decode; use crate::client::tests::prepare_client_with_key_changes; - use executor::{self, NativeExecutor}; + use executor::{NativeExecutor, WasmExecutionMethod}; use crate::error::Error as ClientError; use test_client::{ self, ClientExt, blockchain::HeaderBackend, AccountKeyring, @@ -563,13 +563,17 @@ pub mod tests { } type TestChecker = LightDataChecker< - executor::NativeExecutor, + NativeExecutor, Blake2Hasher, Block, DummyStorage, >; - fn prepare_for_read_proof_check() -> (TestChecker, Header, Vec>, u32) { + fn local_executor() -> NativeExecutor { + NativeExecutor::new(WasmExecutionMethod::Interpreted, None) + } + + fn prepare_for_read_proof_check() -> (TestChecker, Header, StorageProof, u32) { // prepare remote client let remote_client = test_client::new(); let remote_block_id = BlockId::Number(0); @@ -596,12 +600,14 @@ pub mod tests { None, crate::backend::NewBlockState::Final, ).unwrap(); - let local_executor = NativeExecutor::::new(None); - let local_checker = LightDataChecker::new(Arc::new(DummyBlockchain::new(DummyStorage::new())), local_executor); + let local_checker = LightDataChecker::new( + Arc::new(DummyBlockchain::new(DummyStorage::new())), + local_executor() + ); (local_checker, remote_block_header, remote_read_proof, heap_pages) } - fn prepare_for_read_child_proof_check() -> (TestChecker, Header, Vec>, Vec) { + fn prepare_for_read_child_proof_check() -> (TestChecker, Header, StorageProof, Vec) { use test_client::DefaultTestClientBuilderExt; use test_client::TestClientBuilderExt; // prepare remote client @@ -636,12 +642,14 @@ pub mod tests { None, crate::backend::NewBlockState::Final, ).unwrap(); - let local_executor = NativeExecutor::::new(None); - let local_checker = LightDataChecker::new(Arc::new(DummyBlockchain::new(DummyStorage::new())), local_executor); + let local_checker = LightDataChecker::new( + Arc::new(DummyBlockchain::new(DummyStorage::new())), + local_executor(), + ); (local_checker, remote_block_header, remote_read_proof, child_value) } - fn prepare_for_header_proof_check(insert_cht: bool) -> (TestChecker, Hash, Header, Vec>) { + fn prepare_for_header_proof_check(insert_cht: bool) -> (TestChecker, Hash, Header, StorageProof) { // prepare remote client let remote_client = test_client::new(); let mut local_headers_hashes = Vec::new(); @@ -662,8 +670,10 @@ pub mod tests { if insert_cht { local_storage.insert_cht_root(1, local_cht_root); } - let local_executor = NativeExecutor::::new(None); - let local_checker = LightDataChecker::new(Arc::new(DummyBlockchain::new(DummyStorage::new())), local_executor); + let local_checker = LightDataChecker::new( + Arc::new(DummyBlockchain::new(DummyStorage::new())), + local_executor(), + ); (local_checker, local_cht_root, remote_block_header, remote_header_proof) } @@ -744,7 +754,7 @@ pub mod tests { let (remote_client, local_roots, test_cases) = prepare_client_with_key_changes(); let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(DummyStorage::new())), - NativeExecutor::::new(None) + local_executor(), ); let local_checker = &local_checker as &dyn FetchChecker; let max = remote_client.info().chain.best_number; @@ -813,7 +823,7 @@ pub mod tests { local_storage.changes_tries_cht_roots.insert(0, local_cht_root); let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(local_storage)), - NativeExecutor::::new(None) + local_executor(), ); // check proof on local client @@ -842,7 +852,7 @@ pub mod tests { let (remote_client, local_roots, test_cases) = prepare_client_with_key_changes(); let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(DummyStorage::new())), - NativeExecutor::::new(None) + local_executor(), ); let local_checker = &local_checker as &dyn FetchChecker; let max = remote_client.info().chain.best_number; @@ -890,13 +900,13 @@ pub mod tests { max_block: remote_proof.max_block, proof: remote_proof.proof.clone(), roots: vec![(begin - 1, Default::default())].into_iter().collect(), - roots_proof: vec![], + roots_proof: StorageProof::empty(), }).is_err()); assert!(local_checker.check_changes_proof(&request, ChangesProof { max_block: remote_proof.max_block, proof: remote_proof.proof.clone(), roots: vec![(end + 1, Default::default())].into_iter().collect(), - roots_proof: vec![], + roots_proof: StorageProof::empty(), }).is_err()); } @@ -924,7 +934,7 @@ pub mod tests { // fails when changes trie CHT is missing from the local db let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(DummyStorage::new())), - NativeExecutor::::new(None) + local_executor(), ); assert!(local_checker.check_changes_tries_proof(4, &remote_proof.roots, remote_proof.roots_proof.clone()).is_err()); @@ -934,9 +944,12 @@ pub mod tests { local_storage.changes_tries_cht_roots.insert(0, local_cht_root); let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(local_storage)), - NativeExecutor::::new(None) + local_executor(), + ); + let result = local_checker.check_changes_tries_proof( + 4, &remote_proof.roots, StorageProof::empty() ); - assert!(local_checker.check_changes_tries_proof(4, &remote_proof.roots, vec![]).is_err()); + assert!(result.is_err()); } #[test] @@ -948,7 +961,7 @@ pub mod tests { let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(DummyStorage::new())), - NativeExecutor::::new(None) + local_executor(), ); let body_request = RemoteBodyRequest { @@ -971,7 +984,7 @@ pub mod tests { let local_checker = TestChecker::new( Arc::new(DummyBlockchain::new(DummyStorage::new())), - NativeExecutor::::new(None) + local_executor(), ); let body_request = RemoteBodyRequest { diff --git a/core/client/src/light/mod.rs b/core/client/src/light/mod.rs index c9d2e6040be2b2b4f6311e5b182ace27ed28e351..d06a9ae9dd93c09b4bb7e7322ea6d478ff916f53 100644 --- a/core/client/src/light/mod.rs +++ b/core/client/src/light/mod.rs @@ -63,7 +63,7 @@ pub fn new_light( B: BlockT, S: BlockchainStorage + 'static, GS: BuildStorage, - E: CodeExecutor + RuntimeInfo, + E: CodeExecutor + RuntimeInfo, { let local_executor = LocalCallExecutor::new(backend.clone(), code_executor, None); let executor = GenesisCallExecutor::new(backend.clone(), local_executor); @@ -76,7 +76,7 @@ pub fn new_fetch_checker>( executor: E, ) -> LightDataChecker where - E: CodeExecutor, + E: CodeExecutor, { LightDataChecker::new(blockchain, executor) } diff --git a/core/client/src/runtime_api.rs b/core/client/src/runtime_api.rs index a5700951e9c4c64d1266f6f4f3054d22a93b1af5..0fa0545daef2ffa49f85a2ba8ccb45b6d95d9e47 100644 --- a/core/client/src/runtime_api.rs +++ b/core/client/src/runtime_api.rs @@ -18,11 +18,14 @@ #[doc(hidden)] #[cfg(feature = "std")] -pub use state_machine::OverlayedChanges; +pub use state_machine::{OverlayedChanges, StorageProof}; #[doc(hidden)] #[cfg(feature = "std")] pub use primitives::NativeOrEncoded; #[doc(hidden)] +#[cfg(not(feature = "std"))] +pub use primitives::to_substrate_wasm_fn_return_value; +#[doc(hidden)] pub use sr_primitives::{ traits::{ Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, @@ -103,7 +106,7 @@ pub trait ApiExt { /// Extract the recorded proof. /// This stops the proof recording. - fn extract_proof(&mut self) -> Option>>; + fn extract_proof(&mut self) -> Option; } /// Before calling any runtime api function, the runtime need to be initialized diff --git a/core/consensus/aura/Cargo.toml b/core/consensus/aura/Cargo.toml index b63987ecb70d89bb6ed05a5c798735ba23c74c61..a09d51095018e57b5700c94a83baad1f6b5592ce 100644 --- a/core/consensus/aura/Cargo.toml +++ b/core/consensus/aura/Cargo.toml @@ -35,5 +35,5 @@ network = { package = "substrate-network", path = "../../network", features = [" service = { package = "substrate-service", path = "../../service" } test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } tokio = "0.1.22" -env_logger = "0.6.2" +env_logger = "0.7.0" tempfile = "3.1.0" diff --git a/core/consensus/babe/Cargo.toml b/core/consensus/babe/Cargo.toml index e225c2503a0f6d054992cc273478c98f256de07a..30b7c1f8d6b1cec1d34a17cd48fb1550e52de676 100644 --- a/core/consensus/babe/Cargo.toml +++ b/core/consensus/babe/Cargo.toml @@ -36,6 +36,7 @@ schnorrkel = { version = "0.8.5", features = ["preaudit_deprecated"] } rand = "0.7.2" merlin = "1.2.1" pdqselect = "0.1.0" +derive_more = "0.15.0" [dev-dependencies] keyring = { package = "substrate-keyring", path = "../../keyring" } @@ -44,7 +45,7 @@ network = { package = "substrate-network", path = "../../network", features = [" service = { package = "substrate-service", path = "../../service" } test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } tokio = "0.1.22" -env_logger = "0.6.2" +env_logger = "0.7.0" tempfile = "3.1.0" [features] diff --git a/core/consensus/babe/primitives/src/digest.rs b/core/consensus/babe/primitives/src/digest.rs index 3f275ecdb6341e900069b5234a19a28c92cf4153..95dd247810782396499596ffaa3a1d4d74c2ce65 100644 --- a/core/consensus/babe/primitives/src/digest.rs +++ b/core/consensus/babe/primitives/src/digest.rs @@ -91,7 +91,7 @@ impl BabePreDigest { } /// The prefix used by BABE for its VRF keys. -pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf"; +pub const BABE_VRF_PREFIX: &[u8] = b"substrate-babe-vrf"; /// A raw version of `BabePreDigest`, usable on `no_std`. #[derive(Copy, Clone, Encode, Decode)] @@ -195,8 +195,7 @@ impl Decode for BabePreDigest { /// Information about the next epoch. This is broadcast in the first block /// of the epoch. -#[derive(Decode, Encode, Default, PartialEq, Eq, Clone)] -#[cfg_attr(any(feature = "std", test), derive(Debug))] +#[derive(Decode, Encode, Default, PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)] pub struct NextEpochDescriptor { /// The authorities. pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, diff --git a/core/consensus/babe/primitives/src/lib.rs b/core/consensus/babe/primitives/src/lib.rs index 1293b7c8baa0027bc65b61419562b80c5d50f1e5..c464e797d8d5868439504e725aac90b379433e69 100644 --- a/core/consensus/babe/primitives/src/lib.rs +++ b/core/consensus/babe/primitives/src/lib.rs @@ -23,7 +23,7 @@ mod digest; use codec::{Encode, Decode}; use rstd::vec::Vec; -use sr_primitives::ConsensusEngineId; +use sr_primitives::{ConsensusEngineId, RuntimeDebug}; use substrate_client::decl_runtime_apis; #[cfg(feature = "std")] @@ -79,8 +79,7 @@ pub type BabeAuthorityWeight = u64; pub type BabeBlockWeight = u32; /// BABE epoch information -#[derive(Decode, Encode, Default, PartialEq, Eq, Clone)] -#[cfg_attr(any(feature = "std", test), derive(Debug))] +#[derive(Decode, Encode, Default, PartialEq, Eq, Clone, RuntimeDebug)] pub struct Epoch { /// The epoch index pub epoch_index: u64, @@ -127,8 +126,7 @@ pub enum ConsensusLog { } /// Configuration data used by the BABE consensus engine. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(any(feature = "std", test), derive(Debug))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] pub struct BabeConfiguration { /// The slot duration in milliseconds for BABE. Currently, only /// the value provided by this type at genesis will be used. diff --git a/core/consensus/babe/src/aux_schema.rs b/core/consensus/babe/src/aux_schema.rs index 67f61050fa31a6bcb059e8b7b61ae76666de40e5..6290d5cf316434bede137c5badfb0d9822ea1e0b 100644 --- a/core/consensus/babe/src/aux_schema.rs +++ b/core/consensus/babe/src/aux_schema.rs @@ -38,7 +38,7 @@ fn load_decode(backend: &B, key: &[u8]) -> ClientResult> T: Decode, { let corrupt = |e: codec::Error| { - ClientError::Backend(format!("BABE DB is corrupted. Decode error: {}", e.what())).into() + ClientError::Backend(format!("BABE DB is corrupted. Decode error: {}", e.what())) }; match backend.get_aux(key)? { None => Ok(None), diff --git a/core/consensus/babe/src/epoch_changes.rs b/core/consensus/babe/src/epoch_changes.rs index 311271ae15cc7126e2cfe24b66c4b32248b24371..09a14d286426d966c145f160a52816eca05427eb 100644 --- a/core/consensus/babe/src/epoch_changes.rs +++ b/core/consensus/babe/src/epoch_changes.rs @@ -190,23 +190,35 @@ impl EpochChanges where EpochChanges { inner: ForkTree::new() } } - /// Prune out finalized epochs, except for the ancestor of the finalized block. + /// Prune out finalized epochs, except for the ancestor of the finalized + /// block. The given slot should be the slot number at which the finalized + /// block was authored. pub fn prune_finalized>( &mut self, descendent_of_builder: D, - _hash: &Hash, - _number: Number, + hash: &Hash, + number: Number, + slot: SlotNumber, ) -> Result<(), fork_tree::Error> { - let _is_descendent_of = descendent_of_builder + let is_descendent_of = descendent_of_builder .build_is_descendent_of(None); - // TODO: - // https://github.com/paritytech/substrate/issues/3651 - // + let predicate = |epoch: &PersistedEpoch| match *epoch { + PersistedEpoch::Genesis(_, ref epoch_1) => + slot >= epoch_1.end_slot(), + PersistedEpoch::Regular(ref epoch_n) => + slot >= epoch_n.end_slot(), + }; + // prune any epochs which could not be _live_ as of the children of the - // finalized block. - // i.e. re-root the fork tree to the oldest ancestor of (hash, number) - // where epoch.end_slot() >= slot(hash) + // finalized block, i.e. re-root the fork tree to the oldest ancestor of + // (hash, number) where epoch.end_slot() >= finalized_slot + self.inner.prune( + hash, + &number, + &is_descendent_of, + &predicate, + )?; Ok(()) } @@ -300,6 +312,12 @@ impl EpochChanges where Err(e) => Err(e), } } + + /// Return the inner fork tree, useful for testing purposes. + #[cfg(test)] + pub fn tree(&self) -> &ForkTree { + &self.inner + } } /// Type alias to produce the epoch-changes tree from a block type. diff --git a/core/consensus/babe/src/lib.rs b/core/consensus/babe/src/lib.rs index d2a16bedb844fbdc3cce76372fd41336da5f0347..287b26a300f43382e720f89cf77a0be70cbde63a 100644 --- a/core/consensus/babe/src/lib.rs +++ b/core/consensus/babe/src/lib.rs @@ -66,7 +66,7 @@ use consensus_common::ImportResult; use consensus_common::import_queue::{ BoxJustificationImport, BoxFinalityProofImport, }; -use sr_primitives::{generic::{BlockId, OpaqueDigestItemId}, Justification}; +use sr_primitives::{generic::{BlockId, OpaqueDigestItemId}, Justification, RuntimeString}; use sr_primitives::traits::{ Block as BlockT, Header, DigestItemFor, ProvideRuntimeApi, Zero, @@ -102,6 +102,7 @@ use log::{warn, debug, info, trace}; use slots::{SlotWorker, SlotData, SlotInfo, SlotCompatible}; use epoch_changes::descendent_query; use header_metadata::HeaderMetadata; +use schnorrkel::SignatureError; mod aux_schema; mod verification; @@ -112,15 +113,73 @@ mod tests; pub use babe_primitives::{ AuthorityId, AuthorityPair, AuthoritySignature, Epoch, NextEpochDescriptor, }; -pub use epoch_changes::{EpochChanges, SharedEpochChanges}; +pub use epoch_changes::{EpochChanges, EpochChangesFor, SharedEpochChanges}; + + +#[derive(derive_more::Display, Debug)] +enum Error { + #[display(fmt = "Multiple BABE pre-runtime digests, rejecting!")] + MultiplePreRuntimeDigests, + #[display(fmt = "No BABE pre-runtime digest found")] + NoPreRuntimeDigest, + #[display(fmt = "Multiple BABE epoch change digests, rejecting!")] + MultipleEpochChangeDigests, + #[display(fmt = "Could not extract timestamp and slot: {:?}", _0)] + Extraction(consensus_common::Error), + #[display(fmt = "Could not fetch epoch at {:?}", _0)] + FetchEpoch(B::Hash), + #[display(fmt = "Header {:?} rejected: too far in the future", _0)] + TooFarInFuture(B::Hash), + #[display(fmt = "Parent ({}) of {} unavailable. Cannot import", _0, _1)] + ParentUnavailable(B::Hash, B::Hash), + #[display(fmt = "Slot number must increase: parent slot: {}, this slot: {}", _0, _1)] + SlotNumberMustIncrease(u64, u64), + #[display(fmt = "Header {:?} has a bad seal", _0)] + HeaderBadSeal(B::Hash), + #[display(fmt = "Header {:?} is unsealed", _0)] + HeaderUnsealed(B::Hash), + #[display(fmt = "Slot author not found")] + SlotAuthorNotFound, + #[display(fmt = "Secondary slot assignments are disabled for the current epoch.")] + SecondarySlotAssignmentsDisabled, + #[display(fmt = "Bad signature on {:?}", _0)] + BadSignature(B::Hash), + #[display(fmt = "Invalid author: Expected secondary author: {:?}, got: {:?}.", _0, _1)] + InvalidAuthor(AuthorityId, AuthorityId), + #[display(fmt = "No secondary author expected.")] + NoSecondaryAuthorExpected, + #[display(fmt = "VRF verification of block by author {:?} failed: threshold {} exceeded", _0, _1)] + VRFVerificationOfBlockFailed(AuthorityId, u128), + #[display(fmt = "VRF verification failed: {:?}", _0)] + VRFVerificationFailed(SignatureError), + #[display(fmt = "Could not fetch parent header: {:?}", _0)] + FetchParentHeader(client::error::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)] + ParentBlockNoAssociatedWeight(B::Hash), + #[display(fmt = "Checking inherents failed: {}", _0)] + CheckInherents(String), + Client(client::error::Error), + Runtime(RuntimeString), + ForkTree(Box>), +} -macro_rules! babe_err { - ($($i: expr),+) => { - { - debug!(target: "babe", $($i),+); - format!($($i),+) - } - }; +impl std::convert::From> for String { + fn from(error: Error) -> String { + error.to_string() + } +} + +fn babe_err(error: Error) -> Error { + debug!(target: "babe", "{}", error); + error } macro_rules! babe_info { @@ -254,24 +313,6 @@ pub fn start_babe(BabeParams { &inherent_data_providers, )?; - let epoch_changes = babe_link.epoch_changes.clone(); - let pruning_task = client.finality_notification_stream() - .for_each(move |notification| { - // TODO: supply is-descendent-of and maybe write to disk _now_ - // as opposed to waiting for the next epoch? - let res = epoch_changes.lock().prune_finalized( - descendent_query(&*client), - ¬ification.hash, - *notification.header.number(), - ); - - if let Err(e) = res { - babe_err!("Could not prune expired epoch changes: {:?}", e); - } - - future::ready(()) - }); - babe_info!("Starting BABE Authorship worker"); let slot_worker = slots::start_slot_worker( config.0, @@ -280,9 +321,9 @@ pub fn start_babe(BabeParams { sync_oracle, inherent_data_providers, babe_link.time_source, - ).map(|_| ()); + ); - Ok(future::select(slot_worker, pruning_task).map(|_| Ok::<(), ()>(())).compat()) + Ok(slot_worker.map(|_| Ok::<(), ()>(())).compat()) } struct BabeWorker { @@ -403,7 +444,7 @@ impl slots::SimpleSlotWorker for BabeWorker Result { self.env.init(block).map_err(|e| { - consensus_common::Error::ClientImport(format!("{:?}", e)).into() + consensus_common::Error::ClientImport(format!("{:?}", e)) }) } } @@ -428,7 +469,7 @@ 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: &H) -> 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 @@ -443,17 +484,17 @@ fn find_pre_digest(header: &H) -> Result for log in header.digest().logs() { trace!(target: "babe", "Checking log {:?}, looking for pre runtime digest", log); match (log.as_babe_pre_digest(), pre_digest.is_some()) { - (Some(_), true) => Err(babe_err!("Multiple BABE pre-runtime digests, rejecting!"))?, + (Some(_), true) => return Err(babe_err(Error::MultiplePreRuntimeDigests)), (None, _) => trace!(target: "babe", "Ignoring digest not meant for us"), (s, false) => pre_digest = s, } } - pre_digest.ok_or_else(|| babe_err!("No BABE pre-runtime digest found")) + pre_digest.ok_or_else(|| babe_err(Error::NoPreRuntimeDigest)) } /// Extract the BABE epoch change digest from the given header, if it exists. fn find_next_epoch_digest(header: &B::Header) - -> Result, String> + -> Result, Error> where DigestItemFor: CompatibleDigestItem, { let mut epoch_digest: Option<_> = None; @@ -461,7 +502,7 @@ fn find_next_epoch_digest(header: &B::Header) trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log); let log = log.try_to::(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID)); match (log, epoch_digest.is_some()) { - (Some(ConsensusLog::NextEpochData(_)), true) => Err(babe_err!("Multiple BABE epoch change digests, rejecting!"))?, + (Some(ConsensusLog::NextEpochData(_)), true) => return Err(babe_err(Error::MultipleEpochChangeDigests)), (Some(ConsensusLog::NextEpochData(epoch)), false) => epoch_digest = Some(epoch), _ => trace!(target: "babe", "Ignoring digest not meant for us"), } @@ -511,20 +552,20 @@ impl BabeVerifier { block: Block, block_id: BlockId, inherent_data: InherentData, - ) -> Result<(), String> + ) -> Result<(), Error> where PRA: ProvideRuntimeApi, PRA::Api: BlockBuilderApi { let inherent_res = self.api.runtime_api().check_inherents( &block_id, block, inherent_data, - ).map_err(|e| format!("{:?}", e))?; + ).map_err(Error::Client)?; if !inherent_res.ok() { inherent_res .into_errors() .try_for_each(|(i, e)| { - Err(self.inherent_data_providers.error_to_string(&i, &e)) + Err(Error::CheckInherents(self.inherent_data_providers.error_to_string(&i, &e))) }) } else { Ok(()) @@ -603,30 +644,29 @@ impl Verifier for BabeVerifier::Runtime)?; let (_, slot_now, _) = self.time_source.extract_timestamp_and_slot(&inherent_data) - .map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?; + .map_err(Error::::Extraction)?; let hash = header.hash(); let parent_hash = *header.parent_hash(); - let parent_header = self.client.header(&BlockId::Hash(parent_hash)) - .map_err(|e| format!("Could not fetch parent header {:?}: {:?}", parent_hash, e))? - .ok_or_else(|| format!("Parent header {:?} not found.", parent_hash))?; + let parent_header_metadata = self.client.header_metadata(parent_hash) + .map_err(Error::::FetchParentHeader)?; - let pre_digest = find_pre_digest::(&header)?; + let pre_digest = find_pre_digest::(&header)?; let epoch = { let epoch_changes = self.epoch_changes.lock(); epoch_changes.epoch_for_child_of( descendent_query(&*self.client), &parent_hash, - parent_header.number().clone(), + parent_header_metadata.number, pre_digest.slot_number(), |slot| self.config.genesis_epoch(slot), ) - .map_err(|e| format!("{:?}", e))? - .ok_or_else(|| format!("Could not fetch epoch at {:?}", parent_hash))? + .map_err(|e| Error::::ForkTree(Box::new(e)))? + .ok_or_else(|| Error::::FetchEpoch(parent_hash))? }; // We add one to the current slot to allow for some small drift. @@ -710,7 +750,7 @@ impl Verifier for BabeVerifier ?hash, "a" => ?a, "b" => ?b ); - Err(format!("Header {:?} rejected: too far in the future", hash)) + Err(Error::::TooFarInFuture(hash).into()) } } } @@ -806,14 +846,35 @@ impl BlockImport for BabeBlockImport return Ok(ImportResult::AlreadyInChain), Ok(blockchain::BlockStatus::Unknown) => {}, - Err(e) => return Err(ConsensusError::ClientImport(e.to_string()).into()), + Err(e) => return Err(ConsensusError::ClientImport(e.to_string())), } - let pre_digest = find_pre_digest::(&block.header) + let pre_digest = find_pre_digest::(&block.header) .expect("valid babe headers must contain a predigest; \ header has been already verified; qed"); let slot_number = pre_digest.slot_number(); + let parent_hash = *block.header.parent_hash(); + let parent_header = self.client.header(&BlockId::Hash(parent_hash)) + .map_err(|e| ConsensusError::ChainLookup(e.to_string()))? + .ok_or_else(|| ConsensusError::ChainLookup(babe_err( + Error::::ParentUnavailable(parent_hash, hash) + ).into()))?; + + let parent_slot = find_pre_digest::(&parent_header) + .map(|d| d.slot_number()) + .expect("parent is non-genesis; valid BABE headers contain a pre-digest; \ + header has already been verified; qed"); + + // make sure that slot number is strictly increasing + if slot_number <= parent_slot { + return Err( + ConsensusError::ClientImport(babe_err( + Error::::SlotNumberMustIncrease(parent_slot, slot_number) + ).into()) + ); + } + let mut epoch_changes = self.epoch_changes.lock(); // check if there's any epoch change expected to happen at this slot. @@ -822,27 +883,13 @@ impl BlockImport for BabeBlockImport(&parent_header) - .map(|d| d.slot_number()) - .expect("parent is non-genesis; valid BABE headers contain a pre-digest; \ - header has already been verified; qed"); - let parent_weight = if *parent_header.number() == Zero::zero() { 0 } else { aux_schema::load_block_weight(&*self.client, parent_hash) .map_err(|e| ConsensusError::ClientImport(e.to_string()))? .ok_or_else(|| ConsensusError::ClientImport( - babe_err!("Parent block of {} has no associated weight", hash) + babe_err(Error::::ParentBlockNoAssociatedWeight(hash)).into() ))? }; @@ -854,10 +901,10 @@ impl BlockImport for BabeBlockImport| ConsensusError::ChainLookup( - babe_err!("Could not look up epoch: {:?}", e) + babe_err(Error::::CouldNotLookUpEpoch(Box::new(e))).into() ))? .ok_or_else(|| ConsensusError::ClientImport( - babe_err!("Block {} is not valid under any epoch.", hash) + babe_err(Error::::BlockNotValid(hash)).into() ))?; let first_in_epoch = parent_slot < epoch.as_ref().start_slot; @@ -868,7 +915,7 @@ impl BlockImport for BabeBlockImport(&block.header) - .map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; + .map_err(|e| ConsensusError::ClientImport(e.to_string()))?; match (first_in_epoch, next_epoch_digest.is_some()) { (true, true) => {}, @@ -876,12 +923,12 @@ impl BlockImport for BabeBlockImport { return Err( ConsensusError::ClientImport( - babe_err!("Expected epoch change to happen at {:?}, s{}", hash, slot_number), + babe_err(Error::::ExpectedEpochChange(hash, slot_number)).into(), ) ); }, (false, true) => { - return Err(ConsensusError::ClientImport("Unexpected epoch change".into())); + return Err(ConsensusError::ClientImport(Error::::UnexpectedEpochChange.into())); }, } @@ -889,6 +936,8 @@ impl BlockImport for BabeBlockImport BlockImport for BabeBlockImport( @@ -935,10 +997,7 @@ impl BlockImport for BabeBlockImport BlockImport for BabeBlockImport( + client: &Client, + epoch_changes: &mut EpochChangesFor, +) -> Result<(), ConsensusError> where + Block: BlockT, + E: CallExecutor + Send + Sync, + B: Backend, + RA: Send + Sync, +{ + let info = client.info().chain; + + let finalized_slot = { + let finalized_header = client.header(&BlockId::Hash(info.finalized_hash)) + .map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))? + .expect("best finalized hash was given by client; \ + finalized headers must exist in db; qed"); + + find_pre_digest::(&finalized_header) + .expect("finalized header must be valid; \ + valid blocks have a pre-digest; qed") + .slot_number() + }; + + epoch_changes.prune_finalized( + descendent_query(&*client), + &info.finalized_hash, + info.finalized_number, + finalized_slot, + ).map_err(|e| ConsensusError::ClientImport(format!("{:?}", e)))?; + + Ok(()) +} + /// Produce a BABE block-import object to be used later on in the construction of /// an import-queue. /// @@ -994,7 +1087,8 @@ pub fn block_import, I, RA, PRA>( api: Arc, ) -> ClientResult<(BabeBlockImport, BabeLink)> where B: Backend, - E: CallExecutor, + E: CallExecutor + Send + Sync, + RA: Send + Sync, { let epoch_changes = aux_schema::load_epoch_changes(&*client)?; let link = BabeLink { @@ -1003,6 +1097,14 @@ pub fn block_import, I, RA, PRA>( config: config.clone(), }; + // NOTE: this isn't entirely necessary, but since we didn't use to prune the + // epoch tree it is useful as a migration, so that nodes prune long trees on + // startup rather than waiting until importing the next epoch change block. + prune_finalized( + &client, + &mut epoch_changes.lock(), + )?; + let import = BabeBlockImport::new( client, api, diff --git a/core/consensus/babe/src/tests.rs b/core/consensus/babe/src/tests.rs index adfe0f03af2afa2db09566a14a0a010b9a406f0e..2e236f190b66a11d4d4961c5f149b9b65edfc581 100644 --- a/core/consensus/babe/src/tests.rs +++ b/core/consensus/babe/src/tests.rs @@ -81,7 +81,7 @@ impl Environment for DummyFactory { -> Result { - let parent_slot = crate::find_pre_digest(parent_header) + let parent_slot = crate::find_pre_digest::(parent_header) .expect("parent header has a pre-digest") .slot_number(); @@ -103,12 +103,13 @@ impl DummyProposer { &BlockId::Hash(self.parent_hash), pre_digests, ).unwrap(); + let mut block = match block_builder.bake().map_err(|e| e.into()) { Ok(b) => b, Err(e) => return future::ready(Err(e)), }; - let this_slot = crate::find_pre_digest(block.header()) + let this_slot = crate::find_pre_digest::(block.header()) .expect("baked block has valid pre-digest") .slot_number(); @@ -524,35 +525,33 @@ fn can_author_block() { } } -#[test] -fn importing_block_one_sets_genesis_epoch() { - let mut net = BabeTestNet::new(1); - - let peer = net.peer(0); - let data = peer.data.as_ref().expect("babe link set up during initialization"); - let client = peer.client().as_full().expect("Only full clients are used in tests").clone(); - - let mut environ = DummyFactory { - client: client.clone(), - config: data.link.config.clone(), - epoch_changes: data.link.epoch_changes.clone(), - mutator: Arc::new(|_, _| ()), - }; - - let genesis_header = client.header(&BlockId::Number(0)).unwrap().unwrap(); - - let mut proposer = environ.init(&genesis_header).unwrap(); - let babe_claim = Item::babe_pre_digest(babe_primitives::BabePreDigest::Secondary { - authority_index: 0, - slot_number: 999, +// Propose and import a new BABE block on top of the given parent. +fn propose_and_import_block( + parent: &TestHeader, + slot_number: Option, + proposer_factory: &mut DummyFactory, + block_import: &mut BoxBlockImport, +) -> H256 { + let mut proposer = proposer_factory.init(parent).unwrap(); + + let slot_number = slot_number.unwrap_or_else(|| { + let parent_pre_digest = find_pre_digest::(parent).unwrap(); + parent_pre_digest.slot_number() + 1 }); - let pre_digest = sr_primitives::generic::Digest { logs: vec![babe_claim] }; - let genesis_epoch = data.link.config.genesis_epoch(999); + let pre_digest = sr_primitives::generic::Digest { + logs: vec![ + Item::babe_pre_digest( + BabePreDigest::Secondary { + authority_index: 0, + slot_number, + }, + ), + ], + }; let mut block = futures::executor::block_on(proposer.propose_with(pre_digest)).unwrap(); - // seal by alice. let seal = { // sign the pre-sealed hash of the block and then // add it to a digest item. @@ -568,18 +567,14 @@ fn importing_block_one_sets_genesis_epoch() { block.header.digest_mut().pop(); h }; - assert_eq!(*block.header.number(), 1); - let (header, body) = block.deconstruct(); - let post_digests = vec![seal]; - let mut block_import = data.block_import.lock().take().expect("import set up during init"); - block_import.import_block( + let import_result = block_import.import_block( BlockImportParams { origin: BlockOrigin::Own, - header, + header: block.header, justification: None, - post_digests, - body: Some(body), + post_digests: vec![seal], + body: Some(block.extrinsics), finalized: false, auxiliary: Vec::new(), fork_choice: ForkChoiceStrategy::LongestChain, @@ -587,13 +582,197 @@ fn importing_block_one_sets_genesis_epoch() { Default::default(), ).unwrap(); + match import_result { + ImportResult::Imported(_) => {}, + _ => panic!("expected block to be imported"), + } + + post_hash +} + +#[test] +fn importing_block_one_sets_genesis_epoch() { + let mut net = BabeTestNet::new(1); + + let peer = net.peer(0); + let data = peer.data.as_ref().expect("babe link set up during initialization"); + let client = peer.client().as_full().expect("Only full clients are used in tests").clone(); + + let mut proposer_factory = DummyFactory { + client: client.clone(), + config: data.link.config.clone(), + epoch_changes: data.link.epoch_changes.clone(), + mutator: Arc::new(|_, _| ()), + }; + + let mut block_import = data.block_import.lock().take().expect("import set up during init"); + + let genesis_header = client.header(&BlockId::Number(0)).unwrap().unwrap(); + + let block_hash = propose_and_import_block( + &genesis_header, + Some(999), + &mut proposer_factory, + &mut block_import, + ); + + let genesis_epoch = data.link.config.genesis_epoch(999); + let epoch_changes = data.link.epoch_changes.lock(); let epoch_for_second_block = epoch_changes.epoch_for_child_of( descendent_query(&*client), - &post_hash, + &block_hash, 1, 1000, |slot| data.link.config.genesis_epoch(slot), ).unwrap().unwrap().into_inner(); + assert_eq!(epoch_for_second_block, genesis_epoch); } + +#[test] +fn importing_epoch_change_block_prunes_tree() { + use client::backend::Finalizer; + + let mut net = BabeTestNet::new(1); + + let peer = net.peer(0); + let data = peer.data.as_ref().expect("babe link set up during initialization"); + + let client = peer.client().as_full().expect("Only full clients are used in tests").clone(); + let mut block_import = data.block_import.lock().take().expect("import set up during init"); + let epoch_changes = data.link.epoch_changes.clone(); + + let mut proposer_factory = DummyFactory { + client: client.clone(), + config: data.link.config.clone(), + epoch_changes: data.link.epoch_changes.clone(), + mutator: Arc::new(|_, _| ()), + }; + + // This is just boilerplate code for proposing and importing n valid BABE + // blocks that are built on top of the given parent. The proposer takes care + // of producing epoch change digests according to the epoch duration (which + // is set to 6 slots in the test runtime). + let mut propose_and_import_blocks = |parent_id, n| { + let mut hashes = Vec::new(); + let mut parent_header = client.header(&parent_id).unwrap().unwrap(); + + for _ in 0..n { + let block_hash = propose_and_import_block( + &parent_header, + None, + &mut proposer_factory, + &mut block_import, + ); + hashes.push(block_hash); + parent_header = client.header(&BlockId::Hash(block_hash)).unwrap().unwrap(); + } + + hashes + }; + + // This is the block tree that we're going to use in this test. Each node + // represents an epoch change block, the epoch duration is 6 slots. + // + // *---- F (#7) + // / *------ G (#19) - H (#25) + // / / + // A (#1) - B (#7) - C (#13) - D (#19) - E (#25) + // \ + // *------ I (#25) + + // Create and import the canon chain and keep track of fork blocks (A, C, D) + // from the diagram above. + let canon_hashes = propose_and_import_blocks(BlockId::Number(0), 30); + + // Create the forks + let fork_1 = propose_and_import_blocks(BlockId::Hash(canon_hashes[0]), 10); + let fork_2 = propose_and_import_blocks(BlockId::Hash(canon_hashes[12]), 15); + let fork_3 = propose_and_import_blocks(BlockId::Hash(canon_hashes[18]), 10); + + // We should be tracking a total of 9 epochs in the fork tree + assert_eq!( + epoch_changes.lock().tree().iter().count(), + 9, + ); + + // And only one root + assert_eq!( + epoch_changes.lock().tree().roots().count(), + 1, + ); + + // We finalize block #13 from the canon chain, so on the next epoch + // change the tree should be pruned, to not contain F (#7). + client.finalize_block(BlockId::Hash(canon_hashes[12]), None, false).unwrap(); + propose_and_import_blocks(BlockId::Hash(client.info().chain.best_hash), 7); + + // at this point no hashes from the first fork must exist on the tree + assert!( + !epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_1.contains(h)), + ); + + // but the epoch changes from the other forks must still exist + assert!( + epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h)) + ); + + assert!( + epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)), + ); + + // finalizing block #25 from the canon chain should prune out the second fork + client.finalize_block(BlockId::Hash(canon_hashes[24]), None, false).unwrap(); + propose_and_import_blocks(BlockId::Hash(client.info().chain.best_hash), 8); + + // at this point no hashes from the second fork must exist on the tree + assert!( + !epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h)), + ); + + // while epoch changes from the last fork should still exist + assert!( + epoch_changes.lock().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)), + ); +} + +#[test] +#[should_panic] +fn verify_slots_are_strictly_increasing() { + let mut net = BabeTestNet::new(1); + + let peer = net.peer(0); + let data = peer.data.as_ref().expect("babe link set up during initialization"); + + let client = peer.client().as_full().expect("Only full clients are used in tests").clone(); + let mut block_import = data.block_import.lock().take().expect("import set up during init"); + + let mut proposer_factory = DummyFactory { + client: client.clone(), + config: data.link.config.clone(), + epoch_changes: data.link.epoch_changes.clone(), + mutator: Arc::new(|_, _| ()), + }; + + let genesis_header = client.header(&BlockId::Number(0)).unwrap().unwrap(); + + // we should have no issue importing this block + let b1 = propose_and_import_block( + &genesis_header, + Some(999), + &mut proposer_factory, + &mut block_import, + ); + + let b1 = client.header(&BlockId::Hash(b1)).unwrap().unwrap(); + + // we should fail to import this block since the slot number didn't increase. + // we will panic due to the `PanickingBlockImport` defined above. + propose_and_import_block( + &b1, + Some(999), + &mut proposer_factory, + &mut block_import, + ); +} diff --git a/core/consensus/babe/src/verification.rs b/core/consensus/babe/src/verification.rs index 05d610245060b75359b7e3d296afd79af707b0f1..36e34dfb9578b8ace59b25ee0eff192b284a4a1e 100644 --- a/core/consensus/babe/src/verification.rs +++ b/core/consensus/babe/src/verification.rs @@ -22,7 +22,7 @@ use babe_primitives::{Epoch, BabePreDigest, CompatibleDigestItem, AuthorityId}; use babe_primitives::{AuthoritySignature, SlotNumber, AuthorityIndex, AuthorityPair}; use slots::CheckedHeader; use log::{debug, trace}; -use super::{find_pre_digest, BlockT}; +use super::{find_pre_digest, babe_err, BlockT, Error}; use super::authorship::{make_transcript, calculate_primary_threshold, check_primary_threshold, secondary_slot_author}; /// BABE verification parameters @@ -41,15 +41,6 @@ pub(super) struct VerificationParams<'a, B: 'a + BlockT> { pub(super) config: &'a super::Config, } -macro_rules! babe_err { - ($($i: expr),+) => { - { - debug!(target: "babe", $($i),+); - format!($($i),+) - } - }; -} - /// Check a header has been signed by the right key. If the slot is too far in /// the future, an error will be returned. If successful, returns the pre-header /// and the digest item containing the seal. @@ -63,7 +54,7 @@ macro_rules! babe_err { /// with each having different validation logic. pub(super) fn check_header( params: VerificationParams, -) -> Result>, String> where +) -> Result>, Error> where DigestItemFor: CompatibleDigestItem, { let VerificationParams { @@ -75,16 +66,16 @@ pub(super) fn check_header( } = params; let authorities = &epoch.authorities; - let pre_digest = pre_digest.map(Ok).unwrap_or_else(|| find_pre_digest::(&header))?; + let pre_digest = pre_digest.map(Ok).unwrap_or_else(|| find_pre_digest::(&header))?; trace!(target: "babe", "Checking header"); let seal = match header.digest_mut().pop() { Some(x) => x, - None => return Err(babe_err!("Header {:?} is unsealed", header.hash())), + None => return Err(babe_err(Error::HeaderUnsealed(header.hash()))), }; let sig = seal.as_babe_seal().ok_or_else(|| { - babe_err!("Header {:?} has a bad seal", header.hash()) + babe_err(Error::HeaderBadSeal(header.hash())) })?; // the pre-hash of the header doesn't include the seal @@ -98,7 +89,7 @@ pub(super) fn check_header( let author = match authorities.get(pre_digest.authority_index() as usize) { Some(author) => author.0.clone(), - None => return Err(babe_err!("Slot author not found")), + None => return Err(babe_err(Error::SlotAuthorNotFound)), }; match &pre_digest { @@ -128,7 +119,7 @@ pub(super) fn check_header( )?; }, _ => { - return Err(babe_err!("Secondary slot assignments are disabled for the current epoch.")); + return Err(babe_err(Error::SecondarySlotAssignmentsDisabled)); } } @@ -156,7 +147,7 @@ fn check_primary_header( signature: AuthoritySignature, epoch: &Epoch, c: (u64, u64), -) -> Result<(), String> { +) -> Result<(), Error> { let (vrf_output, vrf_proof, authority_index, slot_number) = pre_digest; let author = &epoch.authorities[authority_index as usize].0; @@ -172,7 +163,7 @@ fn check_primary_header( schnorrkel::PublicKey::from_bytes(author.as_slice()).and_then(|p| { p.vrf_verify(transcript, vrf_output, vrf_proof) }).map_err(|s| { - babe_err!("VRF verification failed: {:?}", s) + babe_err(Error::VRFVerificationFailed(s)) })? }; @@ -183,13 +174,12 @@ fn check_primary_header( ); if !check_primary_threshold(&inout, threshold) { - return Err(babe_err!("VRF verification of block by author {:?} failed: \ - threshold {} exceeded", author, threshold)); + return Err(babe_err(Error::VRFVerificationOfBlockFailed(author.clone(), threshold))); } Ok(()) } else { - Err(babe_err!("Bad signature on {:?}", pre_hash)) + Err(babe_err(Error::BadSignature(pre_hash))) } } @@ -202,7 +192,7 @@ fn check_secondary_header( pre_digest: (AuthorityIndex, SlotNumber), signature: AuthoritySignature, epoch: &Epoch, -) -> Result<(), String> { +) -> Result<(), Error> { let (authority_index, slot_number) = pre_digest; // check the signature is valid under the expected authority and @@ -211,22 +201,17 @@ fn check_secondary_header( slot_number, &epoch.authorities, epoch.randomness, - ).ok_or_else(|| "No secondary author expected.".to_string())?; + ).ok_or_else(|| Error::NoSecondaryAuthorExpected)?; let author = &epoch.authorities[authority_index as usize].0; if expected_author != author { - let msg = format!("Invalid author: Expected secondary author: {:?}, got: {:?}.", - expected_author, - author, - ); - - return Err(msg); + return Err(Error::InvalidAuthor(expected_author.clone(), author.clone())); } if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) { Ok(()) } else { - Err(format!("Bad signature on {:?}", pre_hash)) + Err(Error::BadSignature(pre_hash)) } } diff --git a/core/consensus/common/src/select_chain.rs b/core/consensus/common/src/select_chain.rs index cae28656a13b90cc50319d3f4fb6449ebdf59de3..e696db6b877b47d1290637a00e76f7dc6cbf9ee5 100644 --- a/core/consensus/common/src/select_chain.rs +++ b/core/consensus/common/src/select_chain.rs @@ -42,8 +42,9 @@ pub trait SelectChain: Sync + Send + Clone { /// best chain to author new blocks upon and probably finalize. fn best_chain(&self) -> Result<::Header, Error>; - /// Get the best ancestor of `target_hash` that we should attempt - /// to finalize next. + /// Get the best descendent of `target_hash` that we should attempt to + /// finalize next, if any. It is valid to return the given `target_hash` + /// itself if no better descendent exists. fn finality_target( &self, target_hash: ::Hash, diff --git a/core/consensus/pow/Cargo.toml b/core/consensus/pow/Cargo.toml index 54dd58c46709e302a2e459eed58c28b9d24906e8..86efcbb95da2bcdd7e89a42ee17ffa0d174f8ef7 100644 --- a/core/consensus/pow/Cargo.toml +++ b/core/consensus/pow/Cargo.toml @@ -16,3 +16,4 @@ pow-primitives = { package = "substrate-consensus-pow-primitives", path = "primi consensus-common = { package = "substrate-consensus-common", path = "../common" } log = "0.4.8" futures-preview = { version = "0.3.0-alpha.19", features = ["compat"] } +derive_more = "0.15.0" diff --git a/core/consensus/pow/primitives/Cargo.toml b/core/consensus/pow/primitives/Cargo.toml index a7e0d284b09fab51e14ab9df23144b945595c4a2..021e4c80a754c245367b5e71290f6eb984c38db0 100644 --- a/core/consensus/pow/primitives/Cargo.toml +++ b/core/consensus/pow/primitives/Cargo.toml @@ -10,6 +10,7 @@ substrate-client = { path = "../../../client", default-features = false } rstd = { package = "sr-std", path = "../../../sr-std", default-features = false } sr-primitives = { path = "../../../sr-primitives", default-features = false } primitives = { package = "substrate-primitives", path = "../../../primitives", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [features] default = ["std"] @@ -18,4 +19,5 @@ std = [ "substrate-client/std", "sr-primitives/std", "primitives/std", + "codec/std", ] diff --git a/core/consensus/pow/primitives/src/lib.rs b/core/consensus/pow/primitives/src/lib.rs index 807a7b2df2c902c210346366039251883d7d9621..2079b1cbe7e88712499e93320f710169fb7c5722 100644 --- a/core/consensus/pow/primitives/src/lib.rs +++ b/core/consensus/pow/primitives/src/lib.rs @@ -20,17 +20,46 @@ use rstd::vec::Vec; use sr_primitives::ConsensusEngineId; +use codec::Decode; +use substrate_client::decl_runtime_apis; /// The `ConsensusEngineId` of PoW. pub const POW_ENGINE_ID: ConsensusEngineId = [b'p', b'o', b'w', b'_']; -/// Type of difficulty. -/// -/// For runtime designed for Substrate, it's always possible to fit its total -/// difficulty range under `u128::max_value()` because it can be freely scaled -/// up or scaled down. Very few PoW chains use difficulty values -/// larger than `u128::max_value()`. -pub type Difficulty = u128; - /// Type of seal. pub type Seal = Vec; + +/// Define methods that total difficulty should implement. +pub trait TotalDifficulty { + fn increment(&mut self, other: Self); +} + +impl TotalDifficulty for primitives::U256 { + fn increment(&mut self, other: Self) { + let ret = self.saturating_add(other); + *self = ret; + } +} + +impl TotalDifficulty for u128 { + fn increment(&mut self, other: Self) { + let ret = self.saturating_add(other); + *self = ret; + } +} + +decl_runtime_apis! { + /// API necessary for timestamp-based difficulty adjustment algorithms. + pub trait TimestampApi { + /// Return the timestamp in the current block. + fn timestamp() -> Moment; + } + + /// API for those chains that put their difficulty adjustment algorithm directly + /// onto runtime. Note that while putting difficulty adjustment algorithm to + /// runtime is safe, putting the PoW algorithm on runtime is not. + pub trait DifficultyApi { + /// Return the target difficulty of the next block. + fn difficulty() -> Difficulty; + } +} diff --git a/core/consensus/pow/src/lib.rs b/core/consensus/pow/src/lib.rs index 8bc0d3593a026f05fca4f148a4422bfd0c1b2f06..040eb01d9c5f01188ed80a2fd4ec386d1802ff3b 100644 --- a/core/consensus/pow/src/lib.rs +++ b/core/consensus/pow/src/lib.rs @@ -37,21 +37,59 @@ use client::{ block_builder::api::BlockBuilder as BlockBuilderApi, backend::AuxStore, well_known_cache_keys::Id as CacheKeyId, }; -use sr_primitives::Justification; +use sr_primitives::{Justification, RuntimeString}; use sr_primitives::generic::{BlockId, Digest, DigestItem}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, ProvideRuntimeApi}; use srml_timestamp::{TimestampInherentData, InherentError as TIError}; -use pow_primitives::{Difficulty, Seal, POW_ENGINE_ID}; +use pow_primitives::{Seal, TotalDifficulty, POW_ENGINE_ID}; use primitives::H256; use inherents::{InherentDataProviders, InherentData}; use consensus_common::{ - BlockImportParams, BlockOrigin, ForkChoiceStrategy, - Environment, Proposer, + BlockImportParams, BlockOrigin, ForkChoiceStrategy, SyncOracle, Environment, Proposer, + SelectChain, Error as ConsensusError }; use consensus_common::import_queue::{BoxBlockImport, BasicQueue, Verifier}; use codec::{Encode, Decode}; use log::*; +#[derive(derive_more::Display, Debug)] +pub enum Error { + #[display(fmt = "Header uses the wrong engine {:?}", _0)] + WrongEngine([u8; 4]), + #[display(fmt = "Header {:?} is unsealed", _0)] + HeaderUnsealed(B::Hash), + #[display(fmt = "PoW validation error: invalid seal")] + InvalidSeal, + #[display(fmt = "Rejecting block too far in future")] + TooFarInFuture, + #[display(fmt = "Fetching best header failed using select chain: {:?}", _0)] + BestHeaderSelectChain(ConsensusError), + #[display(fmt = "Fetching best header failed: {:?}", _0)] + BestHeader(client::error::Error), + #[display(fmt = "Best header does not exist")] + NoBestHeader, + #[display(fmt = "Block proposing error: {:?}", _0)] + BlockProposingError(String), + #[display(fmt = "Fetch best hash failed via select chain: {:?}", _0)] + BestHashSelectChain(ConsensusError), + #[display(fmt = "Error with block built on {:?}: {:?}", _0, _1)] + BlockBuiltError(B::Hash, ConsensusError), + #[display(fmt = "Creating inherents failed: {}", _0)] + CreateInherents(RuntimeString), + #[display(fmt = "Checking inherents failed: {}", _0)] + CheckInherents(String), + Client(client::error::Error), + Codec(codec::Error), + Environment(String), + Runtime(RuntimeString) +} + +impl std::convert::From> for String { + fn from(error: Error) -> String { + error.to_string() + } +} + /// Auxiliary storage prefix for PoW engine. pub const POW_AUX_PREFIX: [u8; 4] = *b"PoW:"; @@ -63,59 +101,78 @@ fn aux_key(hash: &H256) -> Vec { /// Auxiliary storage data for PoW. #[derive(Encode, Decode, Clone, Debug, Default)] -pub struct PowAux { - /// Total difficulty. +pub struct PowAux { + /// Difficulty of the current block. + pub difficulty: Difficulty, + /// Total difficulty up to current block. pub total_difficulty: Difficulty, } -impl PowAux { +impl PowAux where + Difficulty: Decode + Default, +{ /// Read the auxiliary from client. - pub fn read(client: &C, hash: &H256) -> Result { + pub fn read(client: &C, hash: &H256) -> Result> { let key = aux_key(hash); - match client.get_aux(&key).map_err(|e| format!("{:?}", e))? { - Some(bytes) => PowAux::decode(&mut &bytes[..]).map_err(|e| format!("{:?}", e)), - None => Ok(PowAux::default()), + match client.get_aux(&key).map_err(Error::Client)? { + Some(bytes) => Self::decode(&mut &bytes[..]) + .map_err(Error::Codec), + None => Ok(Self::default()), } } } /// Algorithm used for proof of work. pub trait PowAlgorithm { + /// Difficulty for the algorithm. + type Difficulty: TotalDifficulty + Default + Encode + Decode + Ord + Clone + Copy; + /// Get the next block's difficulty. - fn difficulty(&self, parent: &BlockId) -> Result; + fn difficulty(&self, parent: &BlockId) -> Result>; /// Verify proof of work against the given difficulty. fn verify( &self, parent: &BlockId, pre_hash: &H256, seal: &Seal, - difficulty: Difficulty, - ) -> Result; - /// Mine a seal that satisfy the given difficulty. + difficulty: Self::Difficulty, + ) -> Result>; + /// Mine a seal that satisfies the given difficulty. fn mine( &self, parent: &BlockId, pre_hash: &H256, - seed: &H256, - difficulty: Difficulty, + difficulty: Self::Difficulty, round: u32, - ) -> Result, String>; + ) -> Result, Error>; } /// A verifier for PoW blocks. -pub struct PowVerifier { +pub struct PowVerifier, C, S, Algorithm> { client: Arc, algorithm: Algorithm, inherent_data_providers: inherents::InherentDataProviders, + select_chain: Option, + check_inherents_after: <::Header as HeaderT>::Number, } -impl PowVerifier { - fn check_header>( +impl, C, S, Algorithm> PowVerifier { + pub fn new( + client: Arc, + algorithm: Algorithm, + check_inherents_after: <::Header as HeaderT>::Number, + select_chain: Option, + inherent_data_providers: 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, Difficulty, DigestItem), String> where + ) -> Result<(B::Header, Algorithm::Difficulty, DigestItem), Error> where Algorithm: PowAlgorithm, { let hash = header.hash(); @@ -125,10 +182,10 @@ impl PowVerifier { if id == POW_ENGINE_ID { (DigestItem::Seal(id, seal.clone()), seal) } else { - return Err(format!("Header uses the wrong engine {:?}", id)) + return Err(Error::WrongEngine(id)) } }, - _ => return Err(format!("Header {:?} is unsealed", hash)), + _ => return Err(Error::HeaderUnsealed(hash)), }; let pre_hash = header.hash(); @@ -140,28 +197,32 @@ impl PowVerifier { &inner_seal, difficulty, )? { - return Err("PoW validation error: invalid seal".into()); + return Err(Error::InvalidSeal); } Ok((header, difficulty, seal)) } - fn check_inherents>( + fn check_inherents( &self, block: B, block_id: BlockId, inherent_data: InherentData, timestamp_now: u64, - ) -> Result<(), String> where + ) -> Result<(), Error> where C: ProvideRuntimeApi, C::Api: BlockBuilderApi { const MAX_TIMESTAMP_DRIFT_SECS: u64 = 60; + if *block.header().number() < self.check_inherents_after { + return Ok(()) + } + let inherent_res = self.client.runtime_api().check_inherents( &block_id, block, inherent_data, - ).map_err(|e| format!("{:?}", e))?; + ).map_err(Error::Client)?; if !inherent_res.ok() { inherent_res @@ -169,13 +230,15 @@ impl PowVerifier { .try_for_each(|(i, e)| match TIError::try_from(&i, &e) { Some(TIError::ValidAtTimestamp(timestamp)) => { if timestamp > timestamp_now + MAX_TIMESTAMP_DRIFT_SECS { - return Err("Rejecting block too far in future".into()); + return Err(Error::TooFarInFuture); } Ok(()) }, - Some(TIError::Other(e)) => Err(e.into()), - None => Err(self.inherent_data_providers.error_to_string(&i, &e)), + Some(TIError::Other(e)) => Err(Error::Runtime(e)), + None => Err(Error::CheckInherents( + self.inherent_data_providers.error_to_string(&i, &e) + )), }) } else { Ok(()) @@ -183,9 +246,10 @@ impl PowVerifier { } } -impl, C, Algorithm> Verifier for PowVerifier where +impl, C, S, Algorithm> Verifier for PowVerifier where C: ProvideRuntimeApi + Send + Sync + HeaderBackend + AuxStore + ProvideCache + BlockOf, C::Api: BlockBuilderApi, + S: SelectChain, Algorithm: PowAlgorithm + Send + Sync, { fn verify( @@ -199,17 +263,23 @@ impl, C, Algorithm> Verifier for PowVerifier 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 best_aux = PowAux::read(self.client.as_ref(), &best_hash)?; - let mut aux = PowAux::read(self.client.as_ref(), &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::( + let (checked_header, difficulty, seal) = self.check_header( header, BlockId::Hash(parent_hash), )?; - aux.total_difficulty = aux.total_difficulty.saturating_add(difficulty); + aux.difficulty = difficulty; + aux.total_difficulty.increment(difficulty); if let Some(inner_body) = body.take() { let block = B::new(checked_header.clone(), inner_body); @@ -241,7 +311,7 @@ impl, C, Algorithm> Verifier for PowVerifier Result<(), consensus_common::Error> { if !inherent_data_providers.has_provider(&srml_timestamp::INHERENT_IDENTIFIER) { @@ -258,10 +328,12 @@ fn register_pow_inherent_data_provider( pub type PowImportQueue = BasicQueue; /// Import queue for PoW engine. -pub fn import_queue( +pub fn import_queue( block_import: BoxBlockImport, client: Arc, algorithm: Algorithm, + check_inherents_after: <::Header as HeaderT>::Number, + select_chain: Option, inherent_data_providers: InherentDataProviders, ) -> Result, consensus_common::Error> where B: BlockT, @@ -269,14 +341,17 @@ pub fn import_queue( C: Send + Sync + AuxStore + 'static, C::Api: BlockBuilderApi, Algorithm: PowAlgorithm + Send + Sync + 'static, + S: SelectChain + 'static, { register_pow_inherent_data_provider(&inherent_data_providers)?; - let verifier = PowVerifier { - client: client.clone(), + let verifier = PowVerifier::new( + client.clone(), algorithm, + check_inherents_after, + select_chain, inherent_data_providers, - }; + ); Ok(BasicQueue::new( verifier, @@ -296,19 +371,24 @@ pub fn import_queue( /// information, or just be a graffiti. `round` is for number of rounds the /// CPU miner runs each time. This parameter should be tweaked so that each /// mining round is within sub-second time. -pub fn start_mine, C, Algorithm, E>( +pub fn start_mine, C, Algorithm, E, SO, S>( mut block_import: BoxBlockImport, client: Arc, algorithm: Algorithm, mut env: E, preruntime: Option>, round: u32, + mut sync_oracle: SO, + build_time: std::time::Duration, + select_chain: Option, inherent_data_providers: inherents::InherentDataProviders, ) where C: HeaderBackend + AuxStore + 'static, Algorithm: PowAlgorithm + Send + Sync + 'static, E: Environment + Send + Sync + 'static, E::Error: std::fmt::Debug, + SO: SyncOracle + Send + Sync + 'static, + S: SelectChain + 'static, { if let Err(_) = register_pow_inherent_data_provider(&inherent_data_providers) { warn!("Registering inherent data provider for timestamp failed"); @@ -323,6 +403,9 @@ pub fn start_mine, C, Algorithm, E>( &mut env, preruntime.as_ref(), round, + &mut sync_oracle, + build_time.clone(), + select_chain.as_ref(), &inherent_data_providers ) { Ok(()) => (), @@ -331,36 +414,58 @@ pub fn start_mine, C, Algorithm, E>( e ), } - std::thread::sleep(std::time::Duration::new(1, 0)); } }); } -fn mine_loop, C, Algorithm, E>( +fn mine_loop, C, Algorithm, E, SO, S>( block_import: &mut BoxBlockImport, client: &C, algorithm: &Algorithm, env: &mut E, preruntime: Option<&Vec>, round: u32, + sync_oracle: &mut SO, + build_time: std::time::Duration, + select_chain: Option<&S>, inherent_data_providers: &inherents::InherentDataProviders, -) -> Result<(), String> where +) -> Result<(), Error> where C: HeaderBackend + AuxStore, Algorithm: PowAlgorithm, E: Environment, E::Error: std::fmt::Debug, + SO: SyncOracle, + S: SelectChain, { 'outer: loop { - let best_hash = client.info().best_hash; - let best_header = client.header(BlockId::Hash(best_hash)) - .map_err(|e| format!("Fetching best header failed: {:?}", e))? - .ok_or("Best header does not exist")?; + if sync_oracle.is_major_syncing() { + debug!(target: "pow", "Skipping proposal due to sync."); + std::thread::sleep(std::time::Duration::new(1, 0)); + continue 'outer + } + + let (best_hash, best_header) = match select_chain { + Some(select_chain) => { + let header = select_chain.best_chain() + .map_err(Error::BestHeaderSelectChain)?; + let hash = header.hash(); + (hash, header) + }, + None => { + let hash = client.info().best_hash; + let header = client.header(BlockId::Hash(hash)) + .map_err(Error::BestHeader)? + .ok_or(Error::NoBestHeader)?; + (hash, header) + }, + }; let mut aux = PowAux::read(client, &best_hash)?; - let mut proposer = env.init(&best_header).map_err(|e| format!("{:?}", e))?; + let mut proposer = env.init(&best_header) + .map_err(|e| Error::Environment(format!("{:?}", e)))?; let inherent_data = inherent_data_providers - .create_inherent_data().map_err(String::from)?; + .create_inherent_data().map_err(Error::CreateInherents)?; let mut inherent_digest = Digest::default(); if let Some(preruntime) = &preruntime { inherent_digest.push(DigestItem::PreRuntime(POW_ENGINE_ID, preruntime.to_vec())); @@ -368,21 +473,19 @@ fn mine_loop, C, Algorithm, E>( let block = futures::executor::block_on(proposer.propose( inherent_data, inherent_digest, - std::time::Duration::new(0, 0) - )).map_err(|e| format!("Block proposing error: {:?}", e))?; + build_time.clone(), + )).map_err(|e| Error::BlockProposingError(format!("{:?}", e)))?; let (header, body) = block.deconstruct(); - let seed = H256::random(); let (difficulty, seal) = { - loop { - let difficulty = algorithm.difficulty( - &BlockId::Hash(best_hash), - )?; + let difficulty = algorithm.difficulty( + &BlockId::Hash(best_hash), + )?; + loop { let seal = algorithm.mine( &BlockId::Hash(best_hash), &header.hash(), - &seed, difficulty, round, )?; @@ -397,10 +500,28 @@ fn mine_loop, C, Algorithm, E>( } }; - aux.total_difficulty = aux.total_difficulty.saturating_add(difficulty); - let hash = header.hash(); + aux.difficulty = difficulty; + aux.total_difficulty.increment(difficulty); + let hash = { + let mut header = header.clone(); + header.digest_mut().push(DigestItem::Seal(POW_ENGINE_ID, seal.clone())); + header.hash() + }; 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 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, @@ -413,6 +534,6 @@ fn mine_loop, C, Algorithm, E>( }; block_import.import_block(import_block, HashMap::default()) - .map_err(|e| format!("Error with block built on {:?}: {:?}", best_hash, e))?; + .map_err(|e| Error::BlockBuiltError(best_hash, e))?; } } diff --git a/core/consensus/slots/src/aux_schema.rs b/core/consensus/slots/src/aux_schema.rs index 1d54cb5c2ee6cf05e52edb70c6f6fea5621bb180..186288c17443227ec7b1661f758e73565a119a3b 100644 --- a/core/consensus/slots/src/aux_schema.rs +++ b/core/consensus/slots/src/aux_schema.rs @@ -38,7 +38,7 @@ fn load_decode(backend: &C, key: &[u8]) -> ClientResult> None => Ok(None), Some(t) => T::decode(&mut &t[..]) .map_err( - |e| ClientError::Backend(format!("Slots DB is corrupted. Decode error: {}", e.what())).into(), + |e| ClientError::Backend(format!("Slots DB is corrupted. Decode error: {}", e.what())), ) .map(Some) } diff --git a/core/consensus/slots/src/lib.rs b/core/consensus/slots/src/lib.rs index fd86a0f277331e74b73773683532092d13d479cb..e33d00d25531f7c9d30e9b9ef59e6f37f8a8c906 100644 --- a/core/consensus/slots/src/lib.rs +++ b/core/consensus/slots/src/lib.rs @@ -189,9 +189,9 @@ pub trait SimpleSlotWorker { logs, }, remaining_duration, - ).map_err(|e| consensus_common::Error::ClientImport(format!("{:?}", e)).into()), + ).map_err(|e| consensus_common::Error::ClientImport(format!("{:?}", e))), Delay::new(remaining_duration) - .map_err(|err| consensus_common::Error::FaultyTimer(err).into()) + .map_err(consensus_common::Error::FaultyTimer) ).map(|v| match v { futures::future::Either::Left((b, _)) => b.map(|b| (b, claim)), futures::future::Either::Right((Ok(_), _)) => @@ -220,9 +220,9 @@ pub trait SimpleSlotWorker { } let (header, body) = block.deconstruct(); - let header_num = header.number().clone(); + let header_num = *header.number(); let header_hash = header.hash(); - let parent_hash = header.parent_hash().clone(); + let parent_hash = *header.parent_hash(); let block_import_params = block_import_params_maker( header, @@ -401,9 +401,8 @@ impl SlotDuration { .map_err(|_| { client::error::Error::Backend({ error!(target: "slots", "slot duration kept in invalid format"); - format!("slot duration kept in invalid format") + "slot duration kept in invalid format".to_string() }) - .into() }), None => { use sr_primitives::traits::Zero; diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index 1f53cd4099dab6aeeebf7e141a2c89d76790b192..c5604d50e4a0f2a9ad0747ab15ecef297eee2f1b 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -17,11 +17,21 @@ wasmi = "0.5.1" parity-wasm = "0.40.3" lazy_static = "1.4.0" wasm-interface = { package = "substrate-wasm-interface", path = "../wasm-interface" } +externalities = { package = "substrate-externalities", path = "../externalities" } parking_lot = "0.9.0" log = "0.4.8" libsecp256k1 = "0.3.0" tiny-keccak = "1.5.0" +cranelift-codegen = { version = "0.46.1", optional = true } +cranelift-entity = { version = "0.46.1", optional = true } +cranelift-frontend = { version = "0.46.1", optional = true } +cranelift-native = { version = "0.46.1", optional = true } +cranelift-wasm = { version = "0.46.1", optional = true } +wasmtime-environ = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" } +wasmtime-jit = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" } +wasmtime-runtime = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" } + [dev-dependencies] assert_matches = "1.3.0" wabt = "0.9.2" @@ -30,7 +40,18 @@ runtime-test = { package = "substrate-runtime-test", path = "runtime-test" } substrate-client = { path = "../client" } substrate-offchain = { path = "../offchain/" } state_machine = { package = "substrate-state-machine", path = "../state-machine" } +test-case = "0.3.3" [features] default = [] wasm-extern-trace = [] +wasmtime = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", +] diff --git a/core/executor/runtime-test/Cargo.toml b/core/executor/runtime-test/Cargo.toml index 153c5c9a1f49fa58e5b1b7cc5cceb2cb46cca443..72041218bf9f2ac9959e85d0e63d34ff8e94788f 100644 --- a/core/executor/runtime-test/Cargo.toml +++ b/core/executor/runtime-test/Cargo.toml @@ -13,7 +13,7 @@ primitives = { package = "substrate-primitives", path = "../../primitives", def sr-primitives = { package = "sr-primitives", path = "../../sr-primitives", default-features = false } [build-dependencies] -wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../../utils/wasm-builder-runner" } +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.4", path = "../../utils/wasm-builder-runner" } [features] default = [ "std" ] diff --git a/core/executor/runtime-test/build.rs b/core/executor/runtime-test/build.rs index 136dc793986b541681a44da901b9ea1d42c1630c..68e5205f0c7a867bda0b7bad5efd2252a9fd33f9 100644 --- a/core/executor/runtime-test/build.rs +++ b/core/executor/runtime-test/build.rs @@ -21,7 +21,7 @@ fn main() { "wasm_binary.rs", WasmBuilderSource::CratesOrPath { path: "../../utils/wasm-builder", - version: "1.0.7", + version: "1.0.8", }, // This instructs LLD to export __heap_base as a global variable, which is used by the // external memory allocator. diff --git a/core/executor/runtime-test/src/lib.rs b/core/executor/runtime-test/src/lib.rs index 35f3191ffdf5cc13260aabe377d34c2b95481246..61eca8dd4e23ffa37a1081b9aad9c14f5388b151 100644 --- a/core/executor/runtime-test/src/lib.rs +++ b/core/executor/runtime-test/src/lib.rs @@ -5,48 +5,23 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use rstd::{vec::Vec, slice, vec}; +#[cfg(not(feature = "std"))] +use rstd::{vec::Vec, vec}; +#[cfg(not(feature = "std"))] use runtime_io::{ set_storage, storage, clear_prefix, blake2_128, blake2_256, twox_128, twox_256, ed25519_verify, sr25519_verify, }; +#[cfg(not(feature = "std"))] use sr_primitives::{print, traits::{BlakeTwo256, Hash}}; +#[cfg(not(feature = "std"))] use primitives::{ed25519, sr25519}; -macro_rules! impl_stubs { - ( $( $new_name:ident => $invoke:expr, )* ) => { - $( - impl_stubs!(@METHOD $new_name => $invoke); - )* - }; - ( @METHOD $new_name:ident => $invoke:expr ) => { - #[no_mangle] - pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { - let input: &[u8] = if input_len == 0 { - &[0u8; 0] - } else { - unsafe { - slice::from_raw_parts(input_data, input_len) - } - }; - - let output: Vec = $invoke(input); - let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); - - // Leak the output vector to avoid it being freed. - // This is fine in a WASM context since the heap - // will be discarded after the call. - rstd::mem::forget(output); - res - } - }; -} - -impl_stubs!( - test_data_in => |input| { +primitives::wasm_export_functions! { + fn test_data_in(input: Vec) -> Vec { print("set_storage"); - set_storage(b"input", input); + set_storage(b"input", &input); print("storage"); let foo = storage(b"foo").unwrap(); @@ -56,25 +31,44 @@ impl_stubs!( print("finished!"); b"all ok!".to_vec() - }, - test_clear_prefix => |input| { - clear_prefix(input); + } + + fn test_clear_prefix(input: Vec) -> Vec { + clear_prefix(&input); b"all ok!".to_vec() - }, - test_empty_return => |_| Vec::new(), - test_exhaust_heap => |_| Vec::with_capacity(16777216), - test_panic => |_| panic!("test panic"), - test_conditional_panic => |input: &[u8]| { + } + + fn test_empty_return() {} + + fn test_exhaust_heap() -> Vec { Vec::with_capacity(16777216) } + + fn test_panic() { panic!("test panic") } + + fn test_conditional_panic(input: Vec) -> Vec { if input.len() > 0 { panic!("test panic") } - input.to_vec() - }, - test_blake2_256 => |input| blake2_256(input).to_vec(), - test_blake2_128 => |input| blake2_128(input).to_vec(), - test_twox_256 => |input| twox_256(input).to_vec(), - test_twox_128 => |input| twox_128(input).to_vec(), - test_ed25519_verify => |input: &[u8]| { + + input + } + + fn test_blake2_256(input: Vec) -> Vec { + blake2_256(&input).to_vec() + } + + fn test_blake2_128(input: Vec) -> Vec { + blake2_128(&input).to_vec() + } + + fn test_twox_256(input: Vec) -> Vec { + twox_256(&input).to_vec() + } + + fn test_twox_128(input: Vec) -> Vec { + twox_128(&input).to_vec() + } + + fn test_ed25519_verify(input: Vec) -> bool { let mut pubkey = [0; 32]; let mut sig = [0; 64]; @@ -82,9 +76,10 @@ impl_stubs!( sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - [ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) as u8].to_vec() - }, - test_sr25519_verify => |input: &[u8]| { + ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) + } + + fn test_sr25519_verify(input: Vec) -> bool { let mut pubkey = [0; 32]; let mut sig = [0; 64]; @@ -92,9 +87,10 @@ impl_stubs!( sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - [sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) as u8].to_vec() - }, - test_ordered_trie_root => |_| { + sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) + } + + fn test_ordered_trie_root() -> Vec { BlakeTwo256::ordered_trie_root( vec![ b"zero"[..].into(), @@ -102,24 +98,25 @@ impl_stubs!( b"two"[..].into(), ], ).as_ref().to_vec() - }, - test_sandbox => |code: &[u8]| { - let ok = execute_sandboxed(code, &[]).is_ok(); - [ok as u8].to_vec() - }, - test_sandbox_args => |code: &[u8]| { - let ok = execute_sandboxed( - code, + } + + fn test_sandbox(code: Vec) -> bool { + execute_sandboxed(&code, &[]).is_ok() + } + + fn test_sandbox_args(code: Vec) -> bool { + execute_sandboxed( + &code, &[ sandbox::TypedValue::I32(0x12345678), sandbox::TypedValue::I64(0x1234567887654321), - ] - ).is_ok(); - [ok as u8].to_vec() - }, - test_sandbox_return_val => |code: &[u8]| { + ], + ).is_ok() + } + + fn test_sandbox_return_val(code: Vec) -> bool { let ok = match execute_sandboxed( - code, + &code, &[ sandbox::TypedValue::I32(0x1336), ] @@ -127,41 +124,43 @@ impl_stubs!( Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(0x1337))) => true, _ => false, }; - [ok as u8].to_vec() - }, - test_sandbox_instantiate => |code: &[u8]| { + + ok + } + + fn test_sandbox_instantiate(code: Vec) -> u8 { let env_builder = sandbox::EnvironmentDefinitionBuilder::new(); - let code = match sandbox::Instance::new(code, &env_builder, &mut ()) { + let code = match sandbox::Instance::new(&code, &env_builder, &mut ()) { Ok(_) => 0, Err(sandbox::Error::Module) => 1, Err(sandbox::Error::Execution) => 2, Err(sandbox::Error::OutOfBounds) => 3, }; - [code].to_vec() - }, - test_offchain_local_storage => |_| { + + code + } + + fn test_offchain_local_storage() -> bool { let kind = primitives::offchain::StorageKind::PERSISTENT; assert_eq!(runtime_io::local_storage_get(kind, b"test"), None); runtime_io::local_storage_set(kind, b"test", b"asd"); assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); let res = runtime_io::local_storage_compare_and_set(kind, b"test", Some(b"asd"), b""); - assert_eq!(res, true); assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"".to_vec())); + res + } - [0].to_vec() - }, - test_offchain_local_storage_with_none => |_| { + fn test_offchain_local_storage_with_none() { let kind = primitives::offchain::StorageKind::PERSISTENT; assert_eq!(runtime_io::local_storage_get(kind, b"test"), None); let res = runtime_io::local_storage_compare_and_set(kind, b"test", None, b"value"); assert_eq!(res, true); assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"value".to_vec())); + } - [0].to_vec() - }, - test_offchain_http => |_| { + fn test_offchain_http() -> bool { use primitives::offchain::HttpRequestStatus; let run = || -> Option<()> { let id = runtime_io::http_request_start("POST", "http://localhost:12345", &[]).ok()?; @@ -182,16 +181,23 @@ impl_stubs!( Some(()) }; - [if run().is_some() { 0 } else { 1 }].to_vec() - }, -); + run().is_some() + } + } -fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result { +#[cfg(not(feature = "std"))] +fn execute_sandboxed( + code: &[u8], + args: &[sandbox::TypedValue], +) -> Result { struct State { counter: u32, } - fn env_assert(_e: &mut State, args: &[sandbox::TypedValue]) -> Result { + fn env_assert( + _e: &mut State, + args: &[sandbox::TypedValue], + ) -> Result { if args.len() != 1 { return Err(sandbox::HostError); } @@ -202,7 +208,10 @@ fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result Result { + fn env_inc_counter( + e: &mut State, + args: &[sandbox::TypedValue], + ) -> Result { if args.len() != 1 { return Err(sandbox::HostError); } diff --git a/core/executor/src/error.rs b/core/executor/src/error.rs index c5148241eea5a0b4050abc820784427878022605..83168d598e03cfb83614c88199ea730829b36d7c 100644 --- a/core/executor/src/error.rs +++ b/core/executor/src/error.rs @@ -18,6 +18,8 @@ use serializer; use wasmi; +#[cfg(feature = "wasmtime")] +use wasmtime_jit::{ActionError, SetupError}; /// Result type alias. pub type Result = std::result::Result; @@ -31,6 +33,9 @@ pub enum Error { Trap(wasmi::Trap), /// Wasmi loading/instantiating error Wasmi(wasmi::Error), + /// Wasmtime action error + #[cfg(feature = "wasmtime")] + Wasmtime(ActionError), /// Error in the API. Parameter is an error message. ApiError(String), /// Method is not found @@ -75,9 +80,9 @@ pub enum Error { /// Someone tried to allocate more memory than the allowed maximum per allocation. #[display(fmt="Requested allocation size is too large")] RequestedAllocationTooLarge, - /// Executing the given function failed with the given error. - #[display(fmt="Function execution failed with: {}", _0)] - FunctionExecution(String), + /// Execution of a host function failed. + #[display(fmt="Host function {} execution failed with: {}", _0, _1)] + FunctionExecution(String, String), } impl std::error::Error for Error { @@ -98,3 +103,34 @@ impl From for Error { Error::Other(err) } } + +impl From for Error { + fn from(err: WasmError) -> Error { + Error::Other(err.to_string()) + } +} + +/// Type for errors occurring during Wasm runtime construction. +#[derive(Debug, derive_more::Display)] +pub enum WasmError { + /// Code could not be read from the state. + CodeNotFound, + /// Failure to reinitialize runtime instance from snapshot. + ApplySnapshotFailed, + /// Wasm code failed validation. + InvalidModule, + /// Wasm code could not be deserialized. + CantDeserializeWasm, + /// The module does not export a linear memory named `memory`. + InvalidMemory, + /// The number of heap pages requested is disallowed by the module. + InvalidHeapPages, + /// Instantiation error. + Instantiation(String), + /// The compiler does not support the host machine as a target. + #[cfg(feature = "wasmtime")] + MissingCompilerSupport(&'static str), + /// Wasmtime setup error. + #[cfg(feature = "wasmtime")] + WasmtimeSetup(SetupError), +} diff --git a/core/executor/src/wasm_executor.rs b/core/executor/src/host_interface.rs similarity index 55% rename from core/executor/src/wasm_executor.rs rename to core/executor/src/host_interface.rs index 6ded0adad63cdaf7d3a285f6afd19648a480a3ae..4d1515d76999b7fafcf4b899ebe90698999b89dd 100644 --- a/core/executor/src/wasm_executor.rs +++ b/core/executor/src/host_interface.rs @@ -14,33 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Wasm interface module. +//! Definition and implementation of the Substrate Wasm host interface. //! -//! This module defines and implements the wasm part of Substrate Host Interface and provides -//! an interface for calling into the wasm runtime. +//! These are the host functions callable from within the Substrate runtime. -use std::{convert::TryFrom, str, panic}; -use tiny_keccak; -use secp256k1; - -use wasmi::{ - Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef, - memory_units::Pages, RuntimeValue::{I32, I64, self}, -}; -use super::{sandbox, allocator, error::{Error, Result}}; -use codec::{Encode, Decode}; +use codec::Encode; +use std::{convert::TryFrom, str}; use primitives::{ - blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair, crypto::KeyTypeId, - offchain, sandbox as sandbox_primitives, Blake2Hasher, - traits::Externalities, -}; -use trie::TrieConfiguration; -use trie::trie_types::Layout; -use log::trace; -use wasm_interface::{ - FunctionContext, HostFunctions, Pointer, WordSize, Sandbox, MemoryId, PointerType, - Result as WResult, + blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Blake2Hasher, Pair, + crypto::KeyTypeId, offchain, }; +use trie::{TrieConfiguration, trie_types::Layout}; +use wasm_interface::{FunctionContext, Pointer, PointerType, Result as WResult, WordSize}; #[cfg(feature="wasm-extern-trace")] macro_rules! debug_trace { @@ -52,318 +37,42 @@ macro_rules! debug_trace { ( $( $x:tt )* ) => () } -struct FunctionExecutor { - sandbox_store: sandbox::Store, - heap: allocator::FreeingBumpHeapAllocator, - memory: MemoryRef, - table: Option, -} - -impl FunctionExecutor { - fn new(m: MemoryRef, heap_base: u32, t: Option) -> Result { - Ok(FunctionExecutor { - sandbox_store: sandbox::Store::new(), - heap: allocator::FreeingBumpHeapAllocator::new(heap_base), - memory: m, - table: t, - }) - } -} - -impl sandbox::SandboxCapabilities for FunctionExecutor { - 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> { - let heap = &mut self.heap; - self.memory.with_direct_access_mut(|mem| { - heap.allocate(mem, len) - }) - } - fn deallocate(&mut self, ptr: Pointer) -> Result<()> { - 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<()> { - self.memory.set(ptr.into(), data).map_err(Into::into) - } - fn read_memory(&self, ptr: Pointer, len: WordSize) -> Result> { - self.memory.get(ptr.into(), len as usize).map_err(Into::into) - } - - fn invoke( - &mut self, - dispatch_thunk: &Self::SupervisorFuncRef, - invoke_args_ptr: Pointer, - invoke_args_len: WordSize, - state: u32, - func_idx: sandbox::SupervisorFuncIndex, - ) -> Result - { - let result = wasmi::FuncInstance::invoke( - dispatch_thunk, - &[ - RuntimeValue::I32(u32::from(invoke_args_ptr) as i32), - RuntimeValue::I32(invoke_args_len as i32), - RuntimeValue::I32(state as i32), - RuntimeValue::I32(usize::from(func_idx) as i32), - ], - self, - ); - match result { - Ok(Some(RuntimeValue::I64(val))) => Ok(val), - Ok(_) => return Err("Supervisor function returned unexpected result!".into()), - Err(err) => Err(Error::Trap(err)), - } - } -} - -impl FunctionContext for FunctionExecutor { - fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { - self.memory.get_into(address.into(), dest).map_err(|e| format!("{:?}", e)) - } - - fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { - self.memory.set(address.into(), data).map_err(|e| format!("{:?}", e)) - } - - fn allocate_memory(&mut self, size: WordSize) -> WResult> { - let heap = &mut self.heap; - self.memory.with_direct_access_mut(|mem| { - heap.allocate(mem, size).map_err(|e| format!("{:?}", e)) - }) - } - - fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { - let heap = &mut self.heap; - self.memory.with_direct_access_mut(|mem| { - heap.deallocate(mem, ptr).map_err(|e| format!("{:?}", e)) - }) - } - - fn sandbox(&mut self) -> &mut dyn Sandbox { - self - } -} - -impl Sandbox for FunctionExecutor { - fn memory_get( - &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| format!("{:?}", e))?; - - match MemoryInstance::transfer( - &sandboxed_memory, - offset as usize, - &self.memory, - buf_ptr.into(), - buf_len as usize, - ) { - Ok(()) => Ok(sandbox_primitives::ERR_OK), - Err(_) => Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - } - } - - 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| format!("{:?}", e))?; - - match MemoryInstance::transfer( - &self.memory, - val_ptr.into(), - &sandboxed_memory, - offset as usize, - val_len as usize, - ) { - Ok(()) => Ok(sandbox_primitives::ERR_OK), - Err(_) => Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), - } - } - - fn memory_teardown(&mut self, memory_id: MemoryId) -> WResult<()> { - self.sandbox_store.memory_teardown(memory_id).map_err(|e| format!("{:?}", e)) - } - - fn memory_new( - &mut self, - initial: u32, - maximum: u32, - ) -> WResult { - self.sandbox_store.new_memory(initial, maximum).map_err(|e| format!("{:?}", e)) - } - - fn invoke( - &mut self, - instance_id: u32, - export_name: &str, - args: &[u8], - return_val: Pointer, - return_val_len: WordSize, - state: u32, - ) -> WResult { - trace!(target: "sr-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| format!("{:?}", e))?; - 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")?; - } - self.write_memory(return_val, val).map_err(|_| "Return value buffer is OOB")?; - 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| format!("{:?}", e)) - } - - 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")?; - table.get(dispatch_thunk_id) - .map_err(|_| "dispatch_thunk_idx is out of the table bounds")? - .ok_or_else(|| "dispatch_thunk_idx points on an empty table entry")? - .clone() - }; - - 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) - } -} - -trait WritePrimitive { - fn write_primitive(&mut self, ptr: Pointer, t: T) -> WResult<()>; -} - -impl WritePrimitive for &mut dyn FunctionContext { - fn write_primitive(&mut self, ptr: Pointer, t: u32) -> WResult<()> { - let r = t.to_le_bytes(); - self.write_memory(ptr.cast(), &r) - } -} +pub struct SubstrateExternals; -trait ReadPrimitive { - fn read_primitive(&self, offset: Pointer) -> WResult; +enum RecoverResult { + Invalid(u32), + Valid(secp256k1::PublicKey), } -impl ReadPrimitive for &mut dyn FunctionContext { - fn read_primitive(&self, ptr: Pointer) -> WResult { - let mut r = [0u8; 4]; - self.read_memory_into(ptr.cast(), &mut r)?; - Ok(u32::from_le_bytes(r)) - } +fn secp256k1_recover( + context: &mut dyn FunctionContext, + msg_data: Pointer, + sig_data: Pointer, +) -> WResult { + let mut sig = [0u8; 65]; + context.read_memory_into(sig_data, &mut sig[..]) + .map_err(|_| "Invalid attempt to get signature in ext_secp256k1_ecdsa_recover")?; + let rs = match secp256k1::Signature::parse_slice(&sig[0..64]) { + Ok(rs) => rs, + _ => return Ok(RecoverResult::Invalid(1)), + }; + + let recovery_id = if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8; + let v = match secp256k1::RecoveryId::parse(recovery_id) { + Ok(v) => v, + _ => return Ok(RecoverResult::Invalid(2)), + }; + + let mut msg = [0u8; 32]; + context.read_memory_into(msg_data, &mut msg[..]) + .map_err(|_| "Invalid attempt to get message in ext_secp256k1_ecdsa_recover")?; + + Ok(match secp256k1::recover(&secp256k1::Message::parse(&msg), &rs, &v) { + Ok(pubkey) => RecoverResult::Valid(pubkey), + Err(_) => RecoverResult::Invalid(3), + }) } -fn deadline_to_timestamp(deadline: u64) -> Option { - if deadline == 0 { - None - } else { - Some(offchain::Timestamp::from_unix_millis(deadline)) - } -} - -impl FunctionExecutor { - fn resolver() -> &'static dyn wasmi::ModuleImportResolver { - struct Resolver; - impl wasmi::ModuleImportResolver for Resolver { - fn resolve_func(&self, name: &str, signature: &wasmi::Signature) - -> std::result::Result - { - let signature = wasm_interface::Signature::from(signature); - - if let Some((index, func)) = SubstrateExternals::functions().iter() - .enumerate() - .find(|f| name == f.1.name()) - { - if signature == func.signature() { - Ok(wasmi::FuncInstance::alloc_host(signature.into(), index)) - } else { - Err(wasmi::Error::Instantiation( - format!( - "Invalid signature for function `{}` expected `{:?}`, got `{:?}`", - func.name(), - signature, - func.signature(), - ) - )) - } - } else { - Err(wasmi::Error::Instantiation( - format!("Export {} not found", name), - )) - } - } - } - &Resolver - } -} - -impl wasmi::Externals for FunctionExecutor { - fn invoke_index(&mut self, index: usize, args: wasmi::RuntimeArgs) - -> std::result::Result, wasmi::Trap> - { - let mut args = args.as_ref().iter().copied().map(Into::into); - let function = SubstrateExternals::functions().get(index).ok_or_else(|| - Error::from( - format!("Could not find host function with index: {}", index), - ) - )?; - - function.execute(self, &mut args) - .map_err(Error::FunctionExecution) - .map_err(wasmi::Trap::from) - .map(|v| v.map(Into::into)) - } -} -struct SubstrateExternals; - impl_wasm_host_interface! { impl SubstrateExternals where context { ext_malloc(size: WordSize) -> Pointer { @@ -474,6 +183,22 @@ impl_wasm_host_interface! { Ok(()) } + ext_log( + level: u32, + target_data: Pointer, + target_len: WordSize, + message_data: Pointer, + message_len: WordSize, + ) { + let target = context.read_memory(target_data, target_len) + .map_err(|_| "Invalid attempt to determine target in ext_log")?; + let message = context.read_memory(message_data, message_len) + .map_err(|_| "Invalid attempt to determine message in ext_log")?; + + runtime_io::log(level.into(), &target, &message); + Ok(()) + } + ext_set_storage( key_data: Pointer, key_len: WordSize, @@ -484,10 +209,7 @@ impl_wasm_host_interface! { .map_err(|_| "Invalid attempt to determine key in ext_set_storage")?; let value = context.read_memory(value_data, value_len) .map_err(|_| "Invalid attempt to determine value in ext_set_storage")?; - with_external_storage(move || - Ok(runtime_io::set_storage(&key, &value)) - )?; - Ok(()) + Ok(runtime_io::set_storage(&key, &value)) } ext_set_child_storage( @@ -505,10 +227,7 @@ impl_wasm_host_interface! { let value = context.read_memory(value_data, value_len) .map_err(|_| "Invalid attempt to determine value in ext_set_child_storage")?; - with_external_storage(move || - Ok(runtime_io::set_child_storage(&storage_key, &key, &value)) - )?; - Ok(()) + Ok(runtime_io::set_child_storage(&storage_key, &key, &value)) } ext_clear_child_storage( @@ -522,27 +241,19 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_clear_child_storage")?; - with_external_storage(move || - Ok(runtime_io::clear_child_storage(&storage_key, &key)) - )?; - Ok(()) + Ok(runtime_io::clear_child_storage(&storage_key, &key)) } ext_clear_storage(key_data: Pointer, key_len: WordSize) { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_clear_storage")?; - with_external_storage(move || - Ok(runtime_io::clear_storage(&key)) - )?; - Ok(()) + Ok(runtime_io::clear_storage(&key)) } ext_exists_storage(key_data: Pointer, key_len: WordSize) -> u32 { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_exists_storage")?; - with_external_storage(move || - Ok(if runtime_io::exists_storage(&key) { 1 } else { 0 }) - ) + Ok(if runtime_io::exists_storage(&key) { 1 } else { 0 }) } ext_exists_child_storage( @@ -556,18 +267,13 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_exists_child_storage")?; - with_external_storage(move || - Ok(if runtime_io::exists_child_storage(&storage_key, &key) { 1 } else { 0 }) - ) + Ok(if runtime_io::exists_child_storage(&storage_key, &key) { 1 } else { 0 }) } ext_clear_prefix(prefix_data: Pointer, prefix_len: WordSize) { let prefix = context.read_memory(prefix_data, prefix_len) .map_err(|_| "Invalid attempt to determine prefix in ext_clear_prefix")?; - with_external_storage(move || - Ok(runtime_io::clear_prefix(&prefix)) - )?; - Ok(()) + Ok(runtime_io::clear_prefix(&prefix)) } ext_clear_child_prefix( @@ -580,21 +286,13 @@ impl_wasm_host_interface! { .map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_prefix")?; let prefix = context.read_memory(prefix_data, prefix_len) .map_err(|_| "Invalid attempt to determine prefix in ext_clear_child_prefix")?; - with_external_storage(move || - Ok(runtime_io::clear_child_prefix(&storage_key, &prefix)) - )?; - - Ok(()) + Ok(runtime_io::clear_child_prefix(&storage_key, &prefix)) } ext_kill_child_storage(storage_key_data: Pointer, storage_key_len: WordSize) { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?; - with_external_storage(move || - Ok(runtime_io::kill_child_storage(&storage_key)) - )?; - - Ok(()) + Ok(runtime_io::kill_child_storage(&storage_key)) } ext_get_allocated_storage( @@ -604,11 +302,8 @@ impl_wasm_host_interface! { ) -> Pointer { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_storage")?; - let maybe_value = with_external_storage(move || - Ok(runtime_io::storage(&key)) - )?; - if let Some(value) = maybe_value { + if let Some(value) = runtime_io::storage(&key) { let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) .map_err(|_| "Invalid attempt to set memory in ext_get_allocated_storage")?; @@ -634,11 +329,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?; - let maybe_value = with_external_storage(move || - Ok(runtime_io::child_storage(&storage_key, &key)) - )?; - - if let Some(value) = maybe_value { + if let Some(value) = runtime_io::child_storage(&storage_key, &key) { let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) .map_err(|_| "Invalid attempt to set memory in ext_get_allocated_child_storage")?; @@ -661,11 +352,8 @@ impl_wasm_host_interface! { ) -> WordSize { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to get key in ext_get_storage_into")?; - let maybe_value = with_external_storage(move || - Ok(runtime_io::storage(&key)) - )?; - if let Some(value) = maybe_value { + if let Some(value) = runtime_io::storage(&key) { let data = &value[value.len().min(value_offset as usize)..]; let written = std::cmp::min(value_len as usize, data.len()); context.write_memory(value_data, &data[..written]) @@ -690,11 +378,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to get key in ext_get_child_storage_into")?; - let maybe_value = with_external_storage(move || - Ok(runtime_io::child_storage(&storage_key, &key)) - )?; - - if let Some(value) = maybe_value { + if let Some(value) = runtime_io::child_storage(&storage_key, &key) { let data = &value[value.len().min(value_offset as usize)..]; let written = std::cmp::min(value_len as usize, data.len()); context.write_memory(value_data, &data[..written]) @@ -706,12 +390,8 @@ impl_wasm_host_interface! { } ext_storage_root(result: Pointer) { - let r = with_external_storage(move || - Ok(runtime_io::storage_root()) - )?; - context.write_memory(result, r.as_ref()) - .map_err(|_| "Invalid attempt to set memory in ext_storage_root")?; - Ok(()) + context.write_memory(result, runtime_io::storage_root().as_ref()) + .map_err(|_| "Invalid attempt to set memory in ext_storage_root".into()) } ext_child_storage_root( @@ -721,9 +401,7 @@ impl_wasm_host_interface! { ) -> Pointer { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_child_storage_root")?; - let value = with_external_storage(move || - Ok(runtime_io::child_storage_root(&storage_key)) - )?; + let value = runtime_io::child_storage_root(&storage_key); let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) @@ -741,11 +419,8 @@ impl_wasm_host_interface! { let mut parent_hash = [0u8; 32]; context.read_memory_into(parent_hash_data, &mut parent_hash[..]) .map_err(|_| "Invalid attempt to get parent_hash in ext_storage_changes_root")?; - let r = with_external_storage(move || - Ok(runtime_io::storage_changes_root(parent_hash)) - )?; - if let Some(r) = r { + if let Some(r) = runtime_io::storage_changes_root(parent_hash) { context.write_memory(result, &r[..]) .map_err(|_| "Invalid attempt to set memory in ext_storage_changes_root")?; Ok(1) @@ -1088,33 +763,29 @@ impl_wasm_host_interface! { sig_data: Pointer, pubkey_data: Pointer, ) -> u32 { - let mut sig = [0u8; 65]; - context.read_memory_into(sig_data, &mut sig[..]) - .map_err(|_| "Invalid attempt to get signature in ext_secp256k1_ecdsa_recover")?; - let rs = match secp256k1::Signature::parse_slice(&sig[0..64]) { - Ok(rs) => rs, - _ => return Ok(1), - }; - - let recovery_id = if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8; - let v = match secp256k1::RecoveryId::parse(recovery_id) { - Ok(v) => v, - _ => return Ok(2), - }; - - let mut msg = [0u8; 32]; - context.read_memory_into(msg_data, &mut msg[..]) - .map_err(|_| "Invalid attempt to get message in ext_secp256k1_ecdsa_recover")?; - - let pubkey = match secp256k1::recover(&secp256k1::Message::parse(&msg), &rs, &v) { - Ok(pk) => pk, - _ => return Ok(3), - }; - - context.write_memory(pubkey_data, &pubkey.serialize()[1..65]) - .map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?; + match secp256k1_recover(context, msg_data, sig_data)? { + RecoverResult::Invalid(c) => Ok(c), + RecoverResult::Valid(pubkey) => { + context.write_memory(pubkey_data, &pubkey.serialize()[1..65]) + .map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?; + Ok(0) + } + } + } - Ok(0) + ext_secp256k1_ecdsa_recover_compressed( + msg_data: Pointer, + sig_data: Pointer, + pubkey_data: Pointer, + ) -> u32 { + match secp256k1_recover(context, msg_data, sig_data)? { + RecoverResult::Invalid(c) => Ok(c), + RecoverResult::Valid(pubkey) => { + context.write_memory(pubkey_data, &pubkey.serialize_compressed()[..]) + .map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?; + Ok(0) + } + } } ext_is_validator() -> u32 { @@ -1400,457 +1071,34 @@ impl_wasm_host_interface! { } } -/// Execute closure that access external storage. -/// -/// All panics that happen within closure are captured and transformed into -/// runtime error. This requires special panic handler mode to be enabled -/// during the call (see `panic_handler::AbortGuard::never_abort`). -/// If this mode isn't enabled, then all panics within externalities are -/// leading to process abort. -fn with_external_storage(f: F) -> std::result::Result - where - F: panic::UnwindSafe + FnOnce() -> Result -{ - // it is safe beause basic methods of StorageExternalities are guaranteed to touch only - // its internal state + we should discard it on error - panic::catch_unwind(move || f()) - .map_err(|_| Error::Runtime) - .and_then(|result| result) - .map_err(|err| format!("{}", err)) +trait WritePrimitive { + fn write_primitive(&mut self, ptr: Pointer, t: T) -> WResult<()>; } -/// Wasm rust executor for contracts. -/// -/// Executes the provided code in a sandboxed wasm runtime. -#[derive(Debug, Clone)] -pub struct WasmExecutor; - -impl WasmExecutor { - /// Create a new instance. - pub fn new() -> Self { - WasmExecutor - } - - /// Call a given method in the given code. - /// - /// Signature of this method needs to be `(I32, I32) -> I64`. - /// - /// This should be used for tests only. - pub fn call>( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8], - method: &str, - data: &[u8], - ) -> Result> { - let module = wasmi::Module::from_buffer(code)?; - let module = Self::instantiate_module(heap_pages, &module)?; - - self.call_in_wasm_module(ext, &module, method, data) - } - - /// Call a given method with a custom signature in the given code. - /// - /// This should be used for tests only. - pub fn call_with_custom_signature< - E: Externalities, - F: FnOnce(&mut dyn FnMut(&[u8]) -> Result) -> Result>, - FR: FnOnce(Option, &MemoryRef) -> Result>, - R, - >( - &self, - ext: &mut E, - heap_pages: usize, - code: &[u8], - method: &str, - create_parameters: F, - filter_result: FR, - ) -> Result { - let module = wasmi::Module::from_buffer(code)?; - let module = Self::instantiate_module(heap_pages, &module)?; - - self.call_in_wasm_module_with_custom_signature( - ext, - &module, - method, - create_parameters, - filter_result, - ) - } - - fn get_mem_instance(module: &ModuleRef) -> Result { - Ok(module - .export_by_name("memory") - .ok_or_else(|| Error::InvalidMemoryReference)? - .as_memory() - .ok_or_else(|| Error::InvalidMemoryReference)? - .clone()) - } - - /// Find the global named `__heap_base` in the given wasm module instance and - /// tries to get its value. - fn get_heap_base(module: &ModuleRef) -> Result { - let heap_base_val = module - .export_by_name("__heap_base") - .ok_or_else(|| Error::HeapBaseNotFoundOrInvalid)? - .as_global() - .ok_or_else(|| Error::HeapBaseNotFoundOrInvalid)? - .get(); - - match heap_base_val { - wasmi::RuntimeValue::I32(v) => Ok(v as u32), - _ => Err(Error::HeapBaseNotFoundOrInvalid), - } - } - - /// Call a given method in the given wasm-module runtime. - pub fn call_in_wasm_module>( - &self, - ext: &mut E, - module_instance: &ModuleRef, - method: &str, - data: &[u8], - ) -> Result> { - self.call_in_wasm_module_with_custom_signature( - ext, - module_instance, - method, - |alloc| { - let offset = alloc(data)?; - Ok(vec![I32(offset as i32), I32(data.len() as i32)]) - }, - |res, memory| { - if let Some(I64(r)) = res { - let offset = r as u32; - let length = (r as u64 >> 32) as usize; - memory.get(offset, length).map_err(|_| Error::Runtime).map(Some) - } else { - Ok(None) - } - } - ) - } - - /// Call a given method in the given wasm-module runtime. - fn call_in_wasm_module_with_custom_signature< - E: Externalities, - F: FnOnce(&mut dyn FnMut(&[u8]) -> Result) -> Result>, - FR: FnOnce(Option, &MemoryRef) -> Result>, - R, - >( - &self, - ext: &mut E, - module_instance: &ModuleRef, - method: &str, - create_parameters: F, - filter_result: FR, - ) -> Result { - // extract a reference to a linear memory, optional reference to a table - // and then initialize FunctionExecutor. - let memory = Self::get_mem_instance(module_instance)?; - let table: Option = module_instance - .export_by_name("__indirect_function_table") - .and_then(|e| e.as_table().cloned()); - let heap_base = Self::get_heap_base(module_instance)?; - - let mut fec = FunctionExecutor::new( - memory.clone(), - heap_base, - table, - )?; - - let parameters = create_parameters(&mut |data: &[u8]| { - let offset = fec.allocate_memory(data.len() as u32)?; - fec.write_memory(offset, data).map(|_| offset.into()).map_err(Into::into) - })?; - - let result = runtime_io::with_externalities( - ext, - || module_instance.invoke_export(method, ¶meters, &mut fec), - ); - - match result { - Ok(val) => match filter_result(val, &memory)? { - Some(val) => Ok(val), - None => Err(Error::InvalidReturn), - }, - Err(e) => { - trace!( - target: "wasm-executor", - "Failed to execute code with {} pages", - memory.current_size().0 - ); - Err(e.into()) - }, - } - } - - /// Prepare module instance - pub fn instantiate_module( - heap_pages: usize, - module: &Module, - ) -> Result { - // start module instantiation. Don't run 'start' function yet. - let intermediate_instance = ModuleInstance::new( - module, - &ImportsBuilder::new() - .with_resolver("env", FunctionExecutor::resolver()) - )?; - - // Verify that the module has the heap base global variable. - let _ = Self::get_heap_base(intermediate_instance.not_started_instance())?; - - // Extract a reference to a linear memory. - let memory = Self::get_mem_instance(intermediate_instance.not_started_instance())?; - memory.grow(Pages(heap_pages)).map_err(|_| Error::Runtime)?; - - if intermediate_instance.has_start() { - // Runtime is not allowed to have the `start` function. - Err(Error::RuntimeHasStartFn) - } else { - Ok(intermediate_instance.assert_no_start()) - } +impl WritePrimitive for &mut dyn FunctionContext { + fn write_primitive(&mut self, ptr: Pointer, t: u32) -> WResult<()> { + let r = t.to_le_bytes(); + self.write_memory(ptr.cast(), &r) } } +trait ReadPrimitive { + fn read_primitive(&self, offset: Pointer) -> WResult; +} -#[cfg(test)] -mod tests { - use super::*; - - use codec::Encode; - - use state_machine::TestExternalities as CoreTestExternalities; - use hex_literal::hex; - use primitives::map; - use runtime_test::WASM_BINARY; - use substrate_offchain::testing; - - type TestExternalities = CoreTestExternalities; - - #[test] - fn returning_should_work() { - let mut ext = TestExternalities::default(); - let test_code = WASM_BINARY; - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_empty_return", &[]).unwrap(); - assert_eq!(output, vec![0u8; 0]); - } - - #[test] - fn panicking_should_work() { - let mut ext = TestExternalities::default(); - let test_code = WASM_BINARY; - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_panic", &[]); - assert!(output.is_err()); - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[]); - assert_eq!(output.unwrap(), vec![0u8; 0]); - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[2]); - assert!(output.is_err()); - } - - #[test] - fn storage_should_work() { - let mut ext = TestExternalities::default(); - ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); - let test_code = WASM_BINARY; - - let output = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_data_in", b"Hello world").unwrap(); - - assert_eq!(output, b"all ok!".to_vec()); - - let expected = TestExternalities::new((map![ - b"input".to_vec() => b"Hello world".to_vec(), - b"foo".to_vec() => b"bar".to_vec(), - b"baz".to_vec() => b"bar".to_vec() - ], map![])); - assert_eq!(ext, expected); - } - - #[test] - fn clear_prefix_should_work() { - let mut ext = TestExternalities::default(); - ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); - ext.set_storage(b"aab".to_vec(), b"2".to_vec()); - 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 = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_clear_prefix", b"ab").unwrap(); - - assert_eq!(output, b"all ok!".to_vec()); - - let expected = TestExternalities::new((map![ - b"aaa".to_vec() => b"1".to_vec(), - b"aab".to_vec() => b"2".to_vec(), - b"bbb".to_vec() => b"5".to_vec() - ], map![])); - assert_eq!(expected, ext); - } - - #[test] - fn blake2_256_should_work() { - let mut ext = TestExternalities::default(); - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", &[]).unwrap(), - blake2_256(&b""[..]).encode() - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_256", b"Hello world!").unwrap(), - blake2_256(&b"Hello world!"[..]).encode() - ); - } - - #[test] - fn blake2_128_should_work() { - let mut ext = TestExternalities::default(); - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_128", &[]).unwrap(), - blake2_128(&b""[..]).encode() - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_128", b"Hello world!").unwrap(), - blake2_128(&b"Hello world!"[..]).encode() - ); - } - - #[test] - fn twox_256_should_work() { - let mut ext = TestExternalities::default(); - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", &[]).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a"), - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_256", b"Hello world!").unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74"), - ); - } - - #[test] - fn twox_128_should_work() { - let mut ext = TestExternalities::default(); - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", &[]).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd5") - ); - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_twox_128", b"Hello world!").unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af") - ); - } - - #[test] - fn ed25519_verify_should_work() { - let mut ext = TestExternalities::::default(); - 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![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(sig.as_ref()); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), - vec![1] - ); - - let other_sig = key.sign(b"all is not ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(other_sig.as_ref()); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), - vec![0] - ); - } - - #[test] - fn sr25519_verify_should_work() { - let mut ext = TestExternalities::::default(); - 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![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(sig.as_ref()); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata).unwrap(), - vec![1] - ); - - let other_sig = key.sign(b"all is not ok!"); - let mut calldata = vec![]; - calldata.extend_from_slice(key.public().as_ref()); - calldata.extend_from_slice(other_sig.as_ref()); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata).unwrap(), - vec![0] - ); - } - - #[test] - fn ordered_trie_root_should_work() { - let mut ext = TestExternalities::::default(); - let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_ordered_trie_root", &[]).unwrap(), - Layout::::ordered_trie_root(trie_input.iter()).as_fixed_bytes().encode() - ); - } - - #[test] - fn offchain_local_storage_should_work() { - use substrate_client::backend::OffchainStorage; - - let mut ext = TestExternalities::::default(); - let (offchain, state) = testing::TestOffchainExt::new(); - ext.set_offchain_externalities(offchain); - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_offchain_local_storage", &[]).unwrap(), - vec![0] - ); - assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); +impl ReadPrimitive for &mut dyn FunctionContext { + fn read_primitive(&self, ptr: Pointer) -> WResult { + let mut r = [0u8; 4]; + self.read_memory_into(ptr.cast(), &mut r)?; + Ok(u32::from_le_bytes(r)) } +} - #[test] - fn offchain_http_should_work() { - let mut ext = TestExternalities::::default(); - let (offchain, state) = testing::TestOffchainExt::new(); - ext.set_offchain_externalities(offchain); - state.write().expect_request( - 0, - testing::PendingRequest { - method: "POST".into(), - uri: "http://localhost:12345".into(), - body: vec![1, 2, 3, 4], - headers: vec![("X-Auth".to_owned(), "test".to_owned())], - sent: true, - response: Some(vec![1, 2, 3]), - response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())], - ..Default::default() - }, - ); - - let test_code = WASM_BINARY; - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_offchain_http", &[]).unwrap(), - vec![0] - ); +fn deadline_to_timestamp(deadline: u64) -> Option { + if deadline == 0 { + None + } else { + Some(offchain::Timestamp::from_unix_millis(deadline)) } } + diff --git a/core/executor/src/integration_tests/mod.rs b/core/executor/src/integration_tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..640795f8f0d5e3583f434484cdc4299b748d726a --- /dev/null +++ b/core/executor/src/integration_tests/mod.rs @@ -0,0 +1,441 @@ +// Copyright 2017-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 . + +mod sandbox; + +use codec::{Encode, Decode}; +use hex_literal::hex; +use primitives::{ + Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::OffchainExt, + traits::Externalities, +}; +use runtime_test::WASM_BINARY; +use state_machine::TestExternalities as CoreTestExternalities; +use substrate_offchain::testing; +use test_case::test_case; +use trie::{TrieConfiguration, trie_types::Layout}; + +use crate::{WasmExecutionMethod, call_in_wasm}; + +pub type TestExternalities = CoreTestExternalities; + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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))] +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()); + + let output = call_in_wasm( + "test_conditional_panic", + &[0], + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::::new())); + + let output = call_in_wasm( + "test_conditional_panic", + &vec![2].encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert!(output.is_err()); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn storage_should_work(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + + { + 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()); + } + + let expected = TestExternalities::new((map![ + b"input".to_vec() => b"Hello world".to_vec(), + b"foo".to_vec() => b"bar".to_vec(), + b"baz".to_vec() => b"bar".to_vec() + ], map![])); + assert_eq!(ext, expected); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + { + let mut ext = ext.ext(); + ext.set_storage(b"aaa".to_vec(), b"1".to_vec()); + ext.set_storage(b"aab".to_vec(), b"2".to_vec()); + 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( + "test_clear_prefix", + &b"ab".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(); + + assert_eq!(output, b"all ok!".to_vec().encode()); + } + + let expected = TestExternalities::new((map![ + b"aaa".to_vec() => b"1".to_vec(), + b"aab".to_vec() => b"2".to_vec(), + b"bbb".to_vec() => b"5".to_vec() + ], map![])); + assert_eq!(expected, ext); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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(), + ); + assert_eq!( + call_in_wasm( + "test_blake2_256", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + blake2_256(&b"Hello world!"[..]).to_vec().encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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(), + ); + assert_eq!( + call_in_wasm( + "test_blake2_128", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + blake2_128(&b"Hello world!"[..]).to_vec().encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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" + ).to_vec().encode(), + ); + assert_eq!( + call_in_wasm( + "test_twox_256", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + hex!( + "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" + ).to_vec().encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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(), + ); + assert_eq!( + call_in_wasm( + "test_twox_128", + &b"Hello world!".to_vec().encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_ed25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); + + let other_sig = key.sign(b"all is not ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(other_sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_ed25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + false.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_sr25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); + + let other_sig = key.sign(b"all is not ok!"); + let mut calldata = vec![]; + calldata.extend_from_slice(key.public().as_ref()); + calldata.extend_from_slice(other_sig.as_ref()); + + assert_eq!( + call_in_wasm( + "test_sr25519_verify", + &calldata.encode(), + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + false.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[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, + ).unwrap(), + Layout::::ordered_trie_root(trie_input.iter()).as_bytes().encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) { + use substrate_client::backend::OffchainStorage; + + 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, + ).unwrap(), + true.encode(), + ); + assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +fn offchain_http_should_work(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let (offchain, state) = testing::TestOffchainExt::new(); + ext.register_extension(OffchainExt::new(offchain)); + state.write().expect_request( + 0, + testing::PendingRequest { + method: "POST".into(), + uri: "http://localhost:12345".into(), + body: vec![1, 2, 3, 4], + headers: vec![("X-Auth".to_owned(), "test".to_owned())], + sent: true, + response: Some(vec![1, 2, 3]), + response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())], + ..Default::default() + }, + ); + + 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, + ).unwrap(), + true.encode(), + ); +} + diff --git a/core/executor/src/integration_tests/sandbox.rs b/core/executor/src/integration_tests/sandbox.rs new file mode 100644 index 0000000000000000000000000000000000000000..e4a1a0254dbc1c92e09040e551abd4387af7a9e4 --- /dev/null +++ b/core/executor/src/integration_tests/sandbox.rs @@ -0,0 +1,363 @@ +// Copyright 2018-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 . + +use codec::Encode; +use runtime_test::WASM_BINARY; +use test_case::test_case; +use wabt; + +use crate::{WasmExecutionMethod, call_in_wasm}; +use crate::integration_tests::TestExternalities; + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (import "env" "assert" (func $assert (param i32))) + (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) + (func (export "call") + (drop + (call $inc_counter (i32.const 5)) + ) + + (call $inc_counter (i32.const 3)) + ;; current counter value is on the stack + + ;; check whether current == 8 + i32.const 8 + i32.eq + + call $assert + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (import "env" "assert" (func $assert (param i32))) + (func (export "call") + i32.const 0 + call $assert + ) + ) + "#).unwrap(); + + assert_eq!( + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + vec![0], + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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(); + + let res = call_in_wasm( + "test_exhaust_heap", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ); + assert!(res.is_err()); + if let Err(err) = res { + assert!(err.to_string().contains("Allocator ran out of space")); + } +} + +#[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 + (import "env" "assert" (func $assert (param i32))) + (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) + + ;; Start function + (start $start) + (func $start + ;; Increment counter by 1 + (drop + (call $inc_counter (i32.const 1)) + ) + ) + + (func (export "call") + ;; Increment counter by 1. The current value is placed on the stack. + (call $inc_counter (i32.const 1)) + + ;; Counter is incremented twice by 1, once there and once in `start` func. + ;; So check the returned value is equal to 2. + i32.const 2 + i32.eq + call $assert + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (import "env" "assert" (func $assert (param i32))) + + (func (export "call") (param $x i32) (param $y i64) + ;; assert that $x = 0x12345678 + (call $assert + (i32.eq + (get_local $x) + (i32.const 0x12345678) + ) + ) + + (call $assert + (i64.eq + (get_local $y) + (i64.const 0x1234567887654321) + ) + ) + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_args", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (func (export "call") (param $x i32) (result i32) + (i32.add + (get_local $x) + (i32.const 1) + ) + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_return_val", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + true.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (import "env" "non-existent" (func)) + + (func (export "call") + ) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 1u8.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 1u8.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (func (export "call") + ) + + (func $start + ) + + (start $start) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 0u8.encode(), + ); +} + +#[test_case(WasmExecutionMethod::Interpreted)] +#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +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 + (func (export "call") + ) + + (func $start + unreachable + ) + + (start $start) + ) + "#).unwrap().encode(); + + assert_eq!( + call_in_wasm( + "test_sandbox_instantiate", + &code, + wasm_method, + &mut ext, + &test_code[..], + 8, + ).unwrap(), + 2u8.encode(), + ); +} diff --git a/core/executor/src/lib.rs b/core/executor/src/lib.rs index b5c502795142fa6d2f6c10b13a98802d2348aac3..d6d666dd28647ecc18539fa9529b96bfcc2c1b55 100644 --- a/core/executor/src/lib.rs +++ b/core/executor/src/lib.rs @@ -31,24 +31,54 @@ #[macro_use] mod wasm_utils; -mod wasm_executor; +mod wasmi_execution; #[macro_use] mod native_executor; mod sandbox; mod allocator; -mod wasm_runtimes_cache; +mod host_interface; +mod wasm_runtime; +#[cfg(feature = "wasmtime")] +mod wasmtime; +#[cfg(test)] +mod integration_tests; pub mod error; pub use wasmi; -pub use wasm_executor::WasmExecutor; pub use native_executor::{with_native_environment, NativeExecutor, NativeExecutionDispatch}; -pub use wasm_runtimes_cache::RuntimesCache; pub use runtime_version::{RuntimeVersion, NativeVersion}; pub use codec::Codec; #[doc(hidden)] -pub use primitives::{Blake2Hasher, traits::Externalities}; +pub use primitives::traits::Externalities; #[doc(hidden)] pub use wasm_interface; +pub use wasm_runtime::WasmExecutionMethod; + +/// Call the given `function` in the given wasm `code`. +/// +/// The signature of `function` needs to follow the default Substrate function signature. +/// +/// - `call_data`: Will be given as input parameters to `function` +/// - `execution_method`: The execution method to use. +/// - `ext`: The externalities that should be set while executing the wasm function. +/// - `heap_pages`: The number of heap pages to allocate. +/// +/// Returns the `Vec` that contains the return value of the function. +pub fn call_in_wasm( + function: &str, + call_data: &[u8], + execution_method: WasmExecutionMethod, + ext: &mut E, + code: &[u8], + heap_pages: u64, +) -> error::Result> { + let mut instance = wasm_runtime::create_wasm_runtime_with_code( + execution_method, + heap_pages, + code, + )?; + instance.call(ext, function, call_data) +} /// Provides runtime information. pub trait RuntimeInfo { @@ -56,8 +86,30 @@ pub trait RuntimeInfo { fn native_version(&self) -> &NativeVersion; /// Extract RuntimeVersion of given :code block - fn runtime_version> ( + fn runtime_version ( &self, ext: &mut E, ) -> Option; } + +#[cfg(test)] +mod tests { + use super::*; + use runtime_test::WASM_BINARY; + use runtime_io::TestExternalities; + + #[test] + fn call_in_interpreted_wasm_works() { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + let res = call_in_wasm( + "test_empty_return", + &[], + WasmExecutionMethod::Interpreted, + &mut ext, + &WASM_BINARY, + 8, + ).unwrap(); + assert_eq!(res, vec![0u8; 0]); + } +} diff --git a/core/executor/src/native_executor.rs b/core/executor/src/native_executor.rs index cdf0be7f76c6dd400230e92547a6047d9420755c..0fb84d9972b96eebd152931c29e131afdb4d6e88 100644 --- a/core/executor/src/native_executor.rs +++ b/core/executor/src/native_executor.rs @@ -14,22 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::{result, cell::RefCell, panic::UnwindSafe}; +use std::{result, cell::RefCell, panic::{UnwindSafe, AssertUnwindSafe}}; use crate::error::{Error, Result}; -use crate::wasm_executor::WasmExecutor; +use crate::wasm_runtime::{RuntimesCache, WasmExecutionMethod, WasmRuntime}; +use crate::RuntimeInfo; use runtime_version::{NativeVersion, RuntimeVersion}; use codec::{Decode, Encode}; -use crate::RuntimeInfo; -use primitives::{Blake2Hasher, NativeOrEncoded, traits::{CodeExecutor, Externalities}}; +use primitives::{NativeOrEncoded, traits::{CodeExecutor, Externalities}}; use log::{trace, warn}; -use crate::RuntimesCache; - thread_local! { static RUNTIMES_CACHE: RefCell = RefCell::new(RuntimesCache::new()); } -fn safe_call(f: F) -> Result +/// Default num of pages for the heap +const DEFAULT_HEAP_PAGES: u64 = 1024; + +pub(crate) fn safe_call(f: F) -> Result where F: UnwindSafe + FnOnce() -> U { // Substrate uses custom panic hook that terminates process on panic. Disable termination for the native call. @@ -40,10 +41,10 @@ fn safe_call(f: F) -> Result /// Set up the externalities and safe calling environment to execute calls to a native runtime. /// /// If the inner closure panics, it will be caught and return an error. -pub fn with_native_environment(ext: &mut dyn Externalities, f: F) -> Result +pub fn with_native_environment(ext: &mut dyn Externalities, f: F) -> Result where F: UnwindSafe + FnOnce() -> U { - runtime_io::with_externalities(ext, move || safe_call(f)) + externalities::set_and_run_with_externalities(ext, move || safe_call(f)) } /// Delegate for dispatching a CodeExecutor call. @@ -53,7 +54,7 @@ pub trait NativeExecutionDispatch: Send + Sync { /// Dispatch a method in the runtime. /// /// If the method with the specified name doesn't exist then `Err` is returned. - fn dispatch(ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result>; + fn dispatch(ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result>; /// Provide native runtime version. fn native_version() -> NativeVersion; @@ -64,32 +65,82 @@ pub trait NativeExecutionDispatch: Send + Sync { #[derive(Debug)] pub struct NativeExecutor { /// Dummy field to avoid the compiler complaining about us not using `D`. - _dummy: ::std::marker::PhantomData, - /// The fallback executor in case native isn't available. - fallback: WasmExecutor, + _dummy: std::marker::PhantomData, + /// Method used to execute fallback Wasm code. + fallback_method: WasmExecutionMethod, /// Native runtime version info. native_version: NativeVersion, /// The number of 64KB pages to allocate for Wasm execution. - default_heap_pages: Option, + default_heap_pages: u64, } impl NativeExecutor { /// Create new instance. - pub fn new(default_heap_pages: Option) -> Self { + /// + /// # Parameters + /// + /// `fallback_method` - Method used to execute fallback Wasm code. + /// + /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. + /// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided. + pub fn new(fallback_method: WasmExecutionMethod, default_heap_pages: Option) -> Self { NativeExecutor { _dummy: Default::default(), - fallback: WasmExecutor::new(), + fallback_method, native_version: D::native_version(), - default_heap_pages: default_heap_pages, + default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES), } } + + /// Execute the given closure `f` with the latest runtime (based on the `CODE` key in `ext`). + /// + /// The closure `f` is expected to return `Err(_)` when there happened a `panic!` in native code + /// while executing the runtime in Wasm. If a `panic!` occurred, the runtime is invalidated to + /// prevent any poisoned state. Native runtime execution does not need to report back + /// any `panic!`. + /// + /// # Safety + /// + /// `runtime` and `ext` are given as `AssertUnwindSafe` to the closure. As described above, the + /// runtime is invalidated on any `panic!` to prevent a poisoned state. `ext` is already + /// implicitly handled as unwind safe, as we store it in a global variable while executing the + /// native runtime. + fn with_runtime( + &self, + ext: &mut E, + f: impl for<'a> FnOnce( + AssertUnwindSafe<&'a mut (dyn WasmRuntime + 'static)>, + &'a RuntimeVersion, + AssertUnwindSafe<&'a mut E>, + ) -> Result>, + ) -> Result where E: Externalities { + RUNTIMES_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + let (runtime, version, code_hash) = cache.fetch_runtime( + ext, + self.fallback_method, + self.default_heap_pages, + )?; + + let runtime = AssertUnwindSafe(runtime); + let ext = AssertUnwindSafe(ext); + + match f(runtime, version, ext) { + Ok(res) => res, + Err(e) => { + cache.invalidate_runtime(self.fallback_method, code_hash); + Err(e) + } + } + }) + } } impl Clone for NativeExecutor { fn clone(&self) -> Self { NativeExecutor { _dummy: Default::default(), - fallback: self.fallback.clone(), + fallback_method: self.fallback_method, native_version: D::native_version(), default_heap_pages: self.default_heap_pages, } @@ -101,32 +152,28 @@ impl RuntimeInfo for NativeExecutor { &self.native_version } - fn runtime_version>( + fn runtime_version( &self, ext: &mut E, ) -> Option { - RUNTIMES_CACHE.with(|cache| { - let cache = &mut cache.borrow_mut(); - - match cache.fetch_runtime(&self.fallback, ext, self.default_heap_pages) { - Ok(runtime) => runtime.version(), - Err(e) => { - warn!(target: "executor", "Failed to fetch runtime: {:?}", e); - None - } + match self.with_runtime(ext, |_runtime, version, _ext| Ok(Ok(version.clone()))) { + Ok(version) => Some(version), + Err(e) => { + warn!(target: "executor", "Failed to fetch runtime: {:?}", e); + None } - }) + } } } -impl CodeExecutor for NativeExecutor { +impl CodeExecutor for NativeExecutor { type Error = Error; fn call < - E: Externalities, - R:Decode + Encode + PartialEq, - NC: FnOnce() -> result::Result + UnwindSafe + E: Externalities, + R: Decode + Encode + PartialEq, + NC: FnOnce() -> result::Result + UnwindSafe, >( &self, ext: &mut E, @@ -135,20 +182,11 @@ impl CodeExecutor for NativeExecutor, ) -> (Result>, bool){ - RUNTIMES_CACHE.with(|cache| { - let cache = &mut cache.borrow_mut(); - let cached_runtime = match cache.fetch_runtime( - &self.fallback, ext, self.default_heap_pages, - ) { - Ok(cached_runtime) => cached_runtime, - Err(e) => return (Err(e), false), - }; - let onchain_version = cached_runtime.version(); + let mut used_native = false; + let result = self.with_runtime(ext, |mut runtime, onchain_version, mut ext| { match ( use_native, - onchain_version - .as_ref() - .map_or(false, |v| v.can_call_with(&self.native_version.runtime_version)), + onchain_version.can_call_with(&self.native_version.runtime_version), native_call, ) { (_, false, _) => { @@ -157,54 +195,48 @@ impl CodeExecutor for NativeExecutor".into(), |v| format!("{}", v)) ); - ( - cached_runtime.with(|module| - self.fallback - .call_in_wasm_module(ext, module, method, data) - .map(NativeOrEncoded::Encoded) - ), - false + + safe_call( + move || runtime.call(&mut **ext, method, data).map(NativeOrEncoded::Encoded) ) } (false, _, _) => { - ( - cached_runtime.with(|module| - self.fallback - .call_in_wasm_module(ext, module, method, data) - .map(NativeOrEncoded::Encoded) - ), - false + safe_call( + move || runtime.call(&mut **ext, method, data).map(NativeOrEncoded::Encoded) ) - } + }, (true, true, Some(call)) => { trace!( target: "executor", "Request for native execution with native call succeeded (native: {}, chain: {}).", self.native_version.runtime_version, onchain_version - .as_ref() - .map_or_else(||"".into(), |v| format!("{}", v)) ); - ( - with_native_environment(ext, move || (call)()) - .and_then(|r| r.map(NativeOrEncoded::Native).map_err(|s| Error::ApiError(s.to_string()))), - true - ) + + used_native = true; + let res = with_native_environment(&mut **ext, move || (call)()) + .and_then(|r| r + .map(NativeOrEncoded::Native) + .map_err(|s| Error::ApiError(s.to_string())) + ); + + Ok(res) } _ => { trace!( target: "executor", "Request for native execution succeeded (native: {}, chain: {})", self.native_version.runtime_version, - onchain_version.as_ref().map_or_else(||"".into(), |v| format!("{}", v)) + onchain_version ); - (D::dispatch(ext, method, data).map(NativeOrEncoded::Encoded), true) + + used_native = true; + Ok(D::dispatch(&mut **ext, method, data).map(NativeOrEncoded::Encoded)) } } - }) + }); + (result, used_native) } } @@ -219,7 +251,7 @@ macro_rules! native_executor_instance { (IMPL $name:ident, $dispatcher:path, $version:path) => { impl $crate::NativeExecutionDispatch for $name { fn dispatch( - ext: &mut $crate::Externalities<$crate::Blake2Hasher>, + ext: &mut $crate::Externalities, method: &str, data: &[u8] ) -> $crate::error::Result> { diff --git a/core/executor/src/sandbox.rs b/core/executor/src/sandbox.rs index f09c246679e5d2e85440ad97960ea9dd8c656e47..87edae8c3037d9850ce6fb738b3e1995dd78436c 100644 --- a/core/executor/src/sandbox.rs +++ b/core/executor/src/sandbox.rs @@ -582,271 +582,3 @@ impl Store { instance_idx as u32 } } - -#[cfg(test)] -mod tests { - use super::*; - use primitives::{Blake2Hasher}; - use crate::wasm_executor::WasmExecutor; - use state_machine::TestExternalities as CoreTestExternalities; - use wabt; - use runtime_test::WASM_BINARY; - - type TestExternalities = CoreTestExternalities; - - #[test] - fn sandbox_should_work() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - (func (export "call") - (drop - (call $inc_counter (i32.const 5)) - ) - - (call $inc_counter (i32.const 3)) - ;; current counter value is on the stack - - ;; check whether current == 8 - i32.const 8 - i32.eq - - call $assert - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn sandbox_trap() { - let mut ext = TestExternalities::::default(); - 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(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![0], - ); - } - - #[test] - fn sandbox_should_trap_when_heap_exhausted() { - let mut ext = TestExternalities::::default(); - 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(); - - let res = WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_exhaust_heap", &code); - assert_eq!(res.is_err(), true); - if let Err(err) = res { - assert_eq!( - format!("{}", err), - format!( - "{}", - wasmi::Error::Trap(Error::FunctionExecution("AllocatorOutOfSpace".into()).into()), - ), - ); - } - } - - #[test] - fn start_called() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - - ;; Start function - (start $start) - (func $start - ;; Increment counter by 1 - (drop - (call $inc_counter (i32.const 1)) - ) - ) - - (func (export "call") - ;; Increment counter by 1. The current value is placed on the stack. - (call $inc_counter (i32.const 1)) - - ;; Counter is incremented twice by 1, once there and once in `start` func. - ;; So check the returned value is equal to 2. - i32.const 2 - i32.eq - call $assert - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn invoke_args() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") (param $x i32) (param $y i64) - ;; assert that $x = 0x12345678 - (call $assert - (i32.eq - (get_local $x) - (i32.const 0x12345678) - ) - ) - - (call $assert - (i64.eq - (get_local $y) - (i64.const 0x1234567887654321) - ) - ) - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_args", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn return_val() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") (param $x i32) (result i32) - (i32.add - (get_local $x) - (i32.const 1) - ) - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_return_val", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn unlinkable_module() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (import "env" "non-existent" (func)) - - (func (export "call") - ) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), - vec![1], - ); - } - - #[test] - fn corrupted_module() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - // Corrupted wasm file - let code = &[0, 0, 0, 0, 1, 0, 0, 0]; - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", code).unwrap(), - vec![1], - ); - } - - #[test] - fn start_fn_ok() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") - ) - - (func $start - ) - - (start $start) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), - vec![0], - ); - } - - #[test] - fn start_fn_traps() { - let mut ext = TestExternalities::::default(); - let test_code = WASM_BINARY; - - let code = wabt::wat2wasm(r#" - (module - (func (export "call") - ) - - (func $start - unreachable - ) - - (start $start) - ) - "#).unwrap(); - - assert_eq!( - WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), - vec![2], - ); - } -} diff --git a/core/executor/src/wasm_runtime.rs b/core/executor/src/wasm_runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..caaa01c43027e4e353716fe50c59fb6d8bcba5d3 --- /dev/null +++ b/core/executor/src/wasm_runtime.rs @@ -0,0 +1,226 @@ +// 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 . + +//! Traits and accessor functions for calling into the Substrate Wasm runtime. +//! +//! The primary means of accessing the runtimes is through a cache which saves the reusable +//! components of the runtime that are expensive to initialize. + +use crate::error::{Error, WasmError}; +use crate::wasmi_execution; +#[cfg(feature = "wasmtime")] +use crate::wasmtime; +use log::{trace, warn}; +use codec::Decode; +use primitives::{storage::well_known_keys, traits::Externalities, H256}; +use runtime_version::RuntimeVersion; +use std::{collections::hash_map::{Entry, HashMap}, panic::AssertUnwindSafe}; + +/// The Substrate Wasm runtime. +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; + + /// Call a method in the Substrate runtime by name. Returns the encoded result on success. + fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) + -> Result, Error>; +} + +/// Specification of different methods of executing the runtime Wasm code. +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +pub enum WasmExecutionMethod { + /// Uses the Wasmi interpreter. + Interpreted, + /// Uses the Wasmtime compiled runtime. + #[cfg(feature = "wasmtime")] + Compiled, +} + +/// A Wasm runtime object along with its cached runtime version. +struct VersionedRuntime { + runtime: Box, + /// Runtime version according to `Core_version`. + version: RuntimeVersion, +} + +/// Cache for the runtimes. +/// +/// When an instance is requested for the first time it is added to this cache. Metadata is kept +/// with the instance so that it can be efficiently reinitialized. +/// +/// When using the Wasmi interpreter execution method, the metadata includes the initial memory and +/// values of mutable globals. Follow-up requests to fetch a runtime return this one instance with +/// the memory reset to the initial memory. So, one runtime instance is reused for every fetch +/// request. +/// +/// For now the cache grows indefinitely, but that should be fine for now since runtimes can only be +/// upgraded rarely and there are no other ways to make the node to execute some other runtime. +pub struct RuntimesCache { + /// A cache of runtime instances along with metadata, ready to be reused. + /// + /// Instances are keyed by the Wasm execution method and the hash of their code. + instances: HashMap<(WasmExecutionMethod, [u8; 32]), Result>, +} + +impl RuntimesCache { + /// Creates a new instance of a runtimes cache. + pub fn new() -> RuntimesCache { + RuntimesCache { + instances: HashMap::new(), + } + } + + /// Fetches an instance of the runtime. + /// + /// On first use we create a new runtime instance, save it to the cache + /// and persist its initial memory. + /// + /// Each subsequent request will return this instance, with its memory restored + /// to the persisted initial memory. Thus, we reuse one single runtime instance + /// for every `fetch_runtime` invocation. + /// + /// # Parameters + /// + /// `ext` - Externalities to use for the runtime. This is used for setting + /// up an initial runtime instance. + /// + /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. + /// + /// # Return value + /// + /// If no error occurred a tuple `(&mut WasmRuntime, H256)` is + /// returned. `H256` is the hash of the runtime code. + /// + /// In case of failure one of two errors can be returned: + /// + /// `Err::InvalidCode` is returned for runtime code issues. + /// + /// `Error::InvalidMemoryReference` is returned if no memory export with the + /// identifier `memory` can be found in the runtime. + pub fn fetch_runtime( + &mut self, + ext: &mut E, + wasm_method: WasmExecutionMethod, + default_heap_pages: u64, + ) -> Result<(&mut (dyn WasmRuntime + 'static), &RuntimeVersion, H256), Error> { + let code_hash = ext + .original_storage_hash(well_known_keys::CODE) + .ok_or(Error::InvalidCode("`CODE` not found in storage.".into()))?; + + let heap_pages = ext + .storage(well_known_keys::HEAP_PAGES) + .and_then(|pages| u64::decode(&mut &pages[..]).ok()) + .unwrap_or(default_heap_pages); + + let result = match self.instances.entry((wasm_method, code_hash.into())) { + Entry::Occupied(o) => { + let result = o.into_mut(); + if let Ok(ref mut cached_runtime) = result { + if !cached_runtime.runtime.update_heap_pages(heap_pages) { + trace!( + target: "runtimes_cache", + "heap_pages were changed. Reinstantiating the instance", + ); + *result = create_versioned_wasm_runtime(ext, wasm_method, heap_pages); + if let Err(ref err) = result { + warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); + } + } + } + result + }, + Entry::Vacant(v) => { + trace!(target: "runtimes_cache", "no instance found in cache, creating now."); + let result = create_versioned_wasm_runtime(ext, wasm_method, heap_pages); + if let Err(ref err) = result { + warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); + } + v.insert(result) + } + }; + + result.as_mut() + .map(|entry| (entry.runtime.as_mut(), &entry.version, code_hash)) + .map_err(|ref e| Error::InvalidCode(format!("{:?}", e))) + } + + /// Invalidate the runtime for the given `wasm_method` and `code_hash`. + /// + /// Invalidation of a runtime is useful when there was a `panic!` in native while executing it. + /// The `panic!` maybe have brought the runtime into a poisoned state and so, it is better to + /// invalidate this runtime instance. + pub fn invalidate_runtime( + &mut self, + wasm_method: WasmExecutionMethod, + code_hash: H256, + ) { + // Just remove the instance, it will be re-created the next time it is requested. + self.instances.remove(&(wasm_method, code_hash.into())); + } +} + +/// Create a wasm runtime with the given `code`. +pub fn create_wasm_runtime_with_code( + wasm_method: WasmExecutionMethod, + heap_pages: u64, + code: &[u8], +) -> Result, WasmError> { + match wasm_method { + WasmExecutionMethod::Interpreted => + wasmi_execution::create_instance(code, heap_pages) + .map(|runtime| -> Box { Box::new(runtime) }), + #[cfg(feature = "wasmtime")] + WasmExecutionMethod::Compiled => + wasmtime::create_instance(code, heap_pages) + .map(|runtime| -> Box { Box::new(runtime) }), + } +} + +fn create_versioned_wasm_runtime( + ext: &mut E, + wasm_method: WasmExecutionMethod, + heap_pages: u64, +) -> Result { + let code = ext + .original_storage(well_known_keys::CODE) + .ok_or(WasmError::CodeNotFound)?; + let mut runtime = create_wasm_runtime_with_code(wasm_method, heap_pages, &code)?; + + // Call to determine runtime version. + let version_result = { + // `ext` is already implicitly handled as unwind safe, as we store it in a global variable. + let mut ext = AssertUnwindSafe(ext); + + // The following unwind safety assertion is OK because if the method call panics, the + // runtime will be dropped. + let mut runtime = AssertUnwindSafe(runtime.as_mut()); + crate::native_executor::safe_call( + move || runtime.call(&mut **ext, "Core_version", &[]) + ).map_err(|_| WasmError::Instantiation("panic in call to get runtime version".into()))? + }; + let encoded_version = version_result + .map_err(|e| WasmError::Instantiation(format!("failed to call \"Core_version\": {}", e)))?; + let version = RuntimeVersion::decode(&mut encoded_version.as_slice()) + .map_err(|_| WasmError::Instantiation("failed to decode \"Core_version\" result".into()))?; + + Ok(VersionedRuntime { + runtime, + version, + }) +} diff --git a/core/executor/src/wasm_runtimes_cache.rs b/core/executor/src/wasm_runtimes_cache.rs deleted file mode 100644 index a615660777c78ef49dbdd7decb72c92b3931f085..0000000000000000000000000000000000000000 --- a/core/executor/src/wasm_runtimes_cache.rs +++ /dev/null @@ -1,353 +0,0 @@ -// 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 . - -//! Implements a cache for pre-created Wasm runtime module instances. - -use crate::error::Error; -use crate::wasm_executor::WasmExecutor; -use log::{trace, warn}; -use codec::Decode; -use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; -use primitives::{storage::well_known_keys, Blake2Hasher, traits::Externalities}; -use runtime_version::RuntimeVersion; -use std::{collections::hash_map::{Entry, HashMap}, mem, rc::Rc}; -use wasmi::{Module as WasmModule, ModuleRef as WasmModuleInstanceRef, RuntimeValue}; - -#[derive(Debug)] -enum CacheError { - CodeNotFound, - ApplySnapshotFailed, - InvalidModule, - CantDeserializeWasm, - Instantiation(Error), -} - -/// A runtime along with its version and initial state snapshot. -#[derive(Clone)] -pub struct CachedRuntime { - /// A wasm module instance. - instance: WasmModuleInstanceRef, - /// Runtime version according to `Core_version`. - /// - /// Can be `None` if the runtime doesn't expose this function. - version: Option, - /// The snapshot of the instance's state taken just after the instantiation. - state_snapshot: StateSnapshot, -} - -impl CachedRuntime { - /// Perform an operation with the clean version of the runtime wasm instance. - pub fn with(&self, f: F) -> R - where - F: FnOnce(&WasmModuleInstanceRef) -> R, - { - self.state_snapshot.apply(&self.instance).expect( - "applying the snapshot can only fail if the passed instance is different - from the one that was used for creation of the snapshot; - we use the snapshot that is directly associated with the instance; - thus the snapshot was created using the instance; - qed", - ); - f(&self.instance) - } - - /// Returns the version of this cached runtime. - /// - /// Returns `None` if the runtime doesn't provide the information or there was an error - /// while fetching it. - pub fn version(&self) -> Option { - self.version.clone() - } -} - -/// A state snapshot of an instance taken just after instantiation. -/// -/// It is used for restoring the state of the module after execution. -#[derive(Clone)] -struct StateSnapshot { - /// The offset and the content of the memory segments that should be used to restore the snapshot - 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 { - // Returns `None` if instance is not valid. - fn take( - module_instance: &WasmModuleInstanceRef, - data_segments: Vec, - heap_pages: u64, - ) -> Option { - let prepared_segments = data_segments - .into_iter() - .map(|mut segment| { - // Just replace contents of the segment since the segments will be discarded later - // anyway. - let contents = mem::replace(segment.value_mut(), vec![]); - - let init_expr = match segment.offset() { - Some(offset) => offset.code(), - // Return if the segment is passive - None => return None - }; - - // [op, End] - if init_expr.len() != 2 { - return None; - } - let offset = match init_expr[0] { - Instruction::I32Const(v) => v as u32, - Instruction::GetGlobal(idx) => { - let global_val = module_instance.globals().get(idx as usize)?.get(); - match global_val { - RuntimeValue::I32(v) => v as u32, - _ => return None, - } - } - _ => return None, - }; - - Some((offset, contents)) - }) - .collect::>>()?; - - // Collect all values of mutable globals. - let global_mut_values = module_instance - .globals() - .iter() - .filter(|g| g.is_mutable()) - .map(|g| g.get()) - .collect(); - - Some(Self { - data_segments: prepared_segments, - global_mut_values, - heap_pages, - }) - } - - /// Reset the runtime instance to the initial version by restoring - /// the preserved memory and globals. - /// - /// Returns `Err` if applying the snapshot is failed. - fn apply(&self, instance: &WasmModuleInstanceRef) -> Result<(), CacheError> { - let memory = instance - .export_by_name("memory") - .ok_or(CacheError::ApplySnapshotFailed)? - .as_memory() - .cloned() - .ok_or(CacheError::ApplySnapshotFailed)?; - - // First, erase the memory and copy the data segments into it. - memory - .erase() - .map_err(|_| CacheError::ApplySnapshotFailed)?; - for (offset, contents) in &self.data_segments { - memory - .set(*offset, contents) - .map_err(|_| CacheError::ApplySnapshotFailed)?; - } - - // Second, restore the values of mutable globals. - for (global_ref, global_val) in instance - .globals() - .iter() - .filter(|g| g.is_mutable()) - .zip(self.global_mut_values.iter()) - { - // the instance should be the same as used for preserving and - // we iterate the same way it as we do it for preserving values that means that the - // types should be the same and all the values are mutable. So no error is expected/ - global_ref - .set(*global_val) - .map_err(|_| CacheError::ApplySnapshotFailed)?; - } - Ok(()) - } -} - -/// Default num of pages for the heap -const DEFAULT_HEAP_PAGES: u64 = 1024; - -/// Cache for the runtimes. -/// -/// When an instance is requested for the first time it is added to this -/// cache. Furthermore its initial memory and values of mutable globals are preserved here. Follow-up -/// requests to fetch a runtime return this one instance with the memory -/// reset to the initial memory. So, one runtime instance is reused for -/// every fetch request. -/// -/// For now the cache grows indefinitely, but that should be fine for now since runtimes can only be -/// upgraded rarely and there are no other ways to make the node to execute some other runtime. -pub struct RuntimesCache { - /// A cache of runtime instances along with metadata, ready to be reused. - /// - /// Instances are keyed by the hash of their code. - instances: HashMap<[u8; 32], Result, CacheError>>, -} - -impl RuntimesCache { - /// Creates a new instance of a runtimes cache. - pub fn new() -> RuntimesCache { - RuntimesCache { - instances: HashMap::new(), - } - } - - /// Fetches an instance of the runtime. - /// - /// On first use we create a new runtime instance, save it to the cache - /// and persist its initial memory. - /// - /// Each subsequent request will return this instance, with its memory restored - /// to the persisted initial memory. Thus, we reuse one single runtime instance - /// for every `fetch_runtime` invocation. - /// - /// # Parameters - /// - /// `wasm_executor`- Rust wasm executor. Executes the provided code in a - /// sandboxed Wasm runtime. - /// - /// `ext` - Externalities to use for the runtime. This is used for setting - /// up an initial runtime instance. The parameter is only needed for calling - /// into the Wasm module to find out the `Core_version`. - /// - /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. - /// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided. - /// - /// # Return value - /// - /// If no error occurred a tuple `(wasmi::ModuleRef, Option)` is - /// returned. `RuntimeVersion` is contained if the call to `Core_version` returned - /// a version. - /// - /// In case of failure one of two errors can be returned: - /// - /// `Err::InvalidCode` is returned for runtime code issues. - /// - /// `Error::InvalidMemoryReference` is returned if no memory export with the - /// identifier `memory` can be found in the runtime. - pub fn fetch_runtime>( - &mut self, - wasm_executor: &WasmExecutor, - ext: &mut E, - default_heap_pages: Option, - ) -> Result, Error> { - let code_hash = ext - .original_storage_hash(well_known_keys::CODE) - .ok_or(Error::InvalidCode("`CODE` not found in storage.".into()))?; - - let heap_pages = ext - .storage(well_known_keys::HEAP_PAGES) - .and_then(|pages| u64::decode(&mut &pages[..]).ok()) - .or(default_heap_pages) - .unwrap_or(DEFAULT_HEAP_PAGES); - - // This is direct result from fighting with borrowck. - let handle_result = - |cached_result: &Result, CacheError>| match *cached_result { - Err(ref e) => Err(Error::InvalidCode(format!("{:?}", e))), - Ok(ref cached_runtime) => Ok(Rc::clone(cached_runtime)), - }; - - match self.instances.entry(code_hash.into()) { - Entry::Occupied(mut o) => { - let result = o.get_mut(); - if let Ok(ref cached_runtime) = result { - if cached_runtime.state_snapshot.heap_pages != heap_pages { - trace!( - target: "runtimes_cache", - "heap_pages were changed. Reinstantiating the instance" - ); - *result = Self::create_wasm_instance(wasm_executor, ext, heap_pages); - if let Err(ref err) = result { - warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); - } - } - } - handle_result(result) - }, - Entry::Vacant(v) => { - trace!(target: "runtimes_cache", "no instance found in cache, creating now."); - let result = Self::create_wasm_instance( - wasm_executor, - ext, - heap_pages, - ); - if let Err(ref err) = result { - warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); - } - handle_result(v.insert(result)) - } - } - } - - fn create_wasm_instance>( - wasm_executor: &WasmExecutor, - ext: &mut E, - heap_pages: u64, - ) -> Result, CacheError> { - let code = ext - .original_storage(well_known_keys::CODE) - .ok_or(CacheError::CodeNotFound)?; - let module = WasmModule::from_buffer(&code).map_err(|_| CacheError::InvalidModule)?; - - // Extract the data segments from the wasm code. - // - // A return of this error actually indicates that there is a problem in logic, since - // we just loaded and validated the `module` above. - let data_segments = extract_data_segments(&code)?; - - // Instantiate this module. - let instance = WasmExecutor::instantiate_module(heap_pages as usize, &module) - .map_err(CacheError::Instantiation)?; - - // Take state snapshot before executing anything. - let state_snapshot = StateSnapshot::take(&instance, data_segments, heap_pages) - .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; - qed - ", - ); - - let version = wasm_executor - .call_in_wasm_module(ext, &instance, "Core_version", &[]) - .ok() - .and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok()); - Ok(Rc::new(CachedRuntime { - instance, - version, - state_snapshot, - })) - } -} - -/// Extract the data segments from the given wasm code. -/// -/// Returns `Err` if the given wasm code cannot be deserialized. -fn extract_data_segments(wasm_code: &[u8]) -> Result, CacheError> { - let raw_module: RawModule = deserialize_buffer(wasm_code) - .map_err(|_| CacheError::CantDeserializeWasm)?; - - let segments = raw_module - .data_section() - .map(|ds| ds.entries()) - .unwrap_or(&[]) - .to_vec(); - Ok(segments) -} diff --git a/core/executor/src/wasm_utils.rs b/core/executor/src/wasm_utils.rs index b217350ac6fc4c4e4a894305214b13a529e13b3d..6c1b1ebc50ac110ef9a7f802fe1a2609da5dfe4d 100644 --- a/core/executor/src/wasm_utils.rs +++ b/core/executor/src/wasm_utils.rs @@ -16,6 +16,8 @@ //! Utilities for defining the wasm host environment. +use wasm_interface::{Pointer, WordSize}; + /// Converts arguments into respective WASM types. #[macro_export] macro_rules! convert_args { @@ -171,3 +173,14 @@ macro_rules! impl_wasm_host_interface { } ); } + +/// Runtime API functions return an i64 which encodes a pointer in the least-significant 32 bits +/// and a length in the most-significant 32 bits. This interprets the returned value as a pointer, +/// length tuple. +pub fn interpret_runtime_api_result(retval: i64) -> (Pointer, WordSize) { + let ptr = >::new(retval as u32); + // The first cast to u64 is necessary so that the right shift does not sign-extend. + let len = ((retval as u64) >> 32) as WordSize; + (ptr, len) +} + diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs new file mode 100644 index 0000000000000000000000000000000000000000..dcd6ae89094bcc944c6e9aec783443894bf9dbcf --- /dev/null +++ b/core/executor/src/wasmi_execution.rs @@ -0,0 +1,636 @@ +// Copyright 2017-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 . + +//! Implementation of a Wasm runtime using the Wasmi interpreter. + +use std::{str, mem}; +use wasmi::{ + Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef, + memory_units::Pages, RuntimeValue::{I32, I64, self}, +}; +use crate::error::{Error, WasmError}; +use codec::{Encode, Decode}; +use primitives::{sandbox as sandbox_primitives, traits::Externalities}; +use crate::host_interface::SubstrateExternals; +use crate::sandbox; +use crate::allocator; +use crate::wasm_utils::interpret_runtime_api_result; +use crate::wasm_runtime::WasmRuntime; +use log::trace; +use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; +use wasm_interface::{ + FunctionContext, HostFunctions, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, +}; + +struct FunctionExecutor { + sandbox_store: sandbox::Store, + heap: allocator::FreeingBumpHeapAllocator, + memory: MemoryRef, + table: Option, +} + +impl FunctionExecutor { + fn new(m: MemoryRef, heap_base: u32, t: Option) -> Result { + Ok(FunctionExecutor { + sandbox_store: sandbox::Store::new(), + heap: allocator::FreeingBumpHeapAllocator::new(heap_base), + memory: m, + table: t, + }) + } +} + +impl sandbox::SandboxCapabilities for FunctionExecutor { + 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, + invoke_args_ptr: Pointer, + invoke_args_len: WordSize, + state: u32, + func_idx: sandbox::SupervisorFuncIndex, + ) -> Result + { + let result = wasmi::FuncInstance::invoke( + dispatch_thunk, + &[ + RuntimeValue::I32(u32::from(invoke_args_ptr) as i32), + RuntimeValue::I32(invoke_args_len as i32), + RuntimeValue::I32(state as i32), + RuntimeValue::I32(usize::from(func_idx) as i32), + ], + self, + ); + match result { + Ok(Some(RuntimeValue::I64(val))) => Ok(val), + Ok(_) => return Err("Supervisor function returned unexpected result!".into()), + Err(err) => Err(Error::Trap(err)), + } + } +} + +impl FunctionContext for FunctionExecutor { + fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { + self.memory.get_into(address.into(), dest).map_err(|e| e.to_string()) + } + + fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { + self.memory.set(address.into(), data).map_err(|e| e.to_string()) + } + + fn allocate_memory(&mut self, size: WordSize) -> WResult> { + let heap = &mut self.heap; + self.memory.with_direct_access_mut(|mem| { + heap.allocate(mem, size).map_err(|e| e.to_string()) + }) + } + + fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { + let heap = &mut self.heap; + self.memory.with_direct_access_mut(|mem| { + heap.deallocate(mem, ptr).map_err(|e| e.to_string()) + }) + } + + fn sandbox(&mut self) -> &mut dyn Sandbox { + self + } +} + +impl Sandbox for FunctionExecutor { + 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())?; + + match MemoryInstance::transfer( + &sandboxed_memory, + offset as usize, + &self.memory, + buf_ptr.into(), + buf_len as usize, + ) { + Ok(()) => Ok(sandbox_primitives::ERR_OK), + Err(_) => Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + } + } + + 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())?; + + match MemoryInstance::transfer( + &self.memory, + val_ptr.into(), + &sandboxed_memory, + offset as usize, + val_len as usize, + ) { + Ok(()) => Ok(sandbox_primitives::ERR_OK), + Err(_) => Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS), + } + } + + 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: u32, + ) -> 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: WordSize, + state: u32, + ) -> WResult { + trace!(target: "sr-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")?; + } + self.write_memory(return_val, val).map_err(|_| "Return value buffer is OOB")?; + 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")?; + table.get(dispatch_thunk_id) + .map_err(|_| "dispatch_thunk_idx is out of the table bounds")? + .ok_or_else(|| "dispatch_thunk_idx points on an empty table entry")? + .clone() + }; + + 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) + } +} + +impl FunctionExecutor { + fn resolver() -> &'static dyn wasmi::ModuleImportResolver { + struct Resolver; + impl wasmi::ModuleImportResolver for Resolver { + fn resolve_func(&self, name: &str, signature: &wasmi::Signature) + -> std::result::Result + { + let signature = wasm_interface::Signature::from(signature); + + if let Some((index, func)) = SubstrateExternals::functions().iter() + .enumerate() + .find(|f| name == f.1.name()) + { + if signature == func.signature() { + Ok(wasmi::FuncInstance::alloc_host(signature.into(), index)) + } else { + Err(wasmi::Error::Instantiation( + format!( + "Invalid signature for function `{}` expected `{:?}`, got `{:?}`", + func.name(), + signature, + func.signature(), + ) + )) + } + } else { + Err(wasmi::Error::Instantiation( + format!("Export {} not found", name), + )) + } + } + } + &Resolver + } +} + +impl wasmi::Externals for FunctionExecutor { + fn invoke_index(&mut self, index: usize, args: wasmi::RuntimeArgs) + -> Result, wasmi::Trap> + { + let mut args = args.as_ref().iter().copied().map(Into::into); + let function = SubstrateExternals::functions().get(index).ok_or_else(|| + Error::from( + format!("Could not find host function with index: {}", index), + ) + )?; + + function.execute(self, &mut args) + .map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg)) + .map_err(wasmi::Trap::from) + .map(|v| v.map(Into::into)) + } +} + +fn get_mem_instance(module: &ModuleRef) -> Result { + Ok(module + .export_by_name("memory") + .ok_or_else(|| Error::InvalidMemoryReference)? + .as_memory() + .ok_or_else(|| Error::InvalidMemoryReference)? + .clone()) +} + +/// Find the global named `__heap_base` in the given wasm module instance and +/// tries to get its value. +fn get_heap_base(module: &ModuleRef) -> Result { + let heap_base_val = module + .export_by_name("__heap_base") + .ok_or_else(|| Error::HeapBaseNotFoundOrInvalid)? + .as_global() + .ok_or_else(|| Error::HeapBaseNotFoundOrInvalid)? + .get(); + + match heap_base_val { + wasmi::RuntimeValue::I32(v) => Ok(v as u32), + _ => Err(Error::HeapBaseNotFoundOrInvalid), + } +} + +/// Call a given method in the given wasm-module runtime. +fn call_in_wasm_module( + ext: &mut dyn Externalities, + module_instance: &ModuleRef, + method: &str, + data: &[u8], +) -> Result, Error> { + call_in_wasm_module_with_custom_signature( + ext, + module_instance, + method, + |alloc| { + let offset = alloc(data)?; + Ok(vec![I32(offset as i32), I32(data.len() as i32)]) + }, + |res, memory| { + if let Some(I64(retval)) = res { + let (ptr, length) = interpret_runtime_api_result(retval); + memory.get(ptr.into(), length as usize).map_err(|_| Error::Runtime).map(Some) + } else { + Ok(None) + } + } + ) +} + +/// Call a given method in the given wasm-module runtime. +fn call_in_wasm_module_with_custom_signature< + F: FnOnce(&mut dyn FnMut(&[u8]) -> Result) -> Result, Error>, + FR: FnOnce(Option, &MemoryRef) -> Result, Error>, + R, +>( + ext: &mut dyn Externalities, + module_instance: &ModuleRef, + method: &str, + create_parameters: F, + filter_result: FR, +) -> Result { + // extract a reference to a linear memory, optional reference to a table + // and then initialize FunctionExecutor. + let memory = get_mem_instance(module_instance)?; + let table: Option = module_instance + .export_by_name("__indirect_function_table") + .and_then(|e| e.as_table().cloned()); + let heap_base = get_heap_base(module_instance)?; + + let mut fec = FunctionExecutor::new( + memory.clone(), + heap_base, + table, + )?; + + let parameters = create_parameters(&mut |data: &[u8]| { + let offset = fec.allocate_memory(data.len() as u32)?; + fec.write_memory(offset, data).map(|_| offset.into()).map_err(Into::into) + })?; + + let result = externalities::set_and_run_with_externalities( + ext, + || module_instance.invoke_export(method, ¶meters, &mut fec), + ); + + match result { + Ok(val) => match filter_result(val, &memory)? { + Some(val) => Ok(val), + None => Err(Error::InvalidReturn), + }, + Err(e) => { + trace!( + target: "wasm-executor", + "Failed to execute code with {} pages", + memory.current_size().0 + ); + Err(e.into()) + }, + } +} + +/// Prepare module instance +fn instantiate_module( + heap_pages: usize, + module: &Module, +) -> Result { + // start module instantiation. Don't run 'start' function yet. + let intermediate_instance = ModuleInstance::new( + module, + &ImportsBuilder::new() + .with_resolver("env", FunctionExecutor::resolver()) + )?; + + // 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)?; + + if intermediate_instance.has_start() { + // Runtime is not allowed to have the `start` function. + Err(Error::RuntimeHasStartFn) + } else { + Ok(intermediate_instance.assert_no_start()) + } +} + +/// A state snapshot of an instance taken just after instantiation. +/// +/// It is used for restoring the state of the module after execution. +#[derive(Clone)] +struct StateSnapshot { + /// The offset and the content of the memory segments that should be used to restore the snapshot + 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 { + // Returns `None` if instance is not valid. + fn take( + module_instance: &ModuleRef, + data_segments: Vec, + heap_pages: u64, + ) -> Option { + let prepared_segments = data_segments + .into_iter() + .map(|mut segment| { + // Just replace contents of the segment since the segments will be discarded later + // anyway. + let contents = mem::replace(segment.value_mut(), vec![]); + + let init_expr = match segment.offset() { + Some(offset) => offset.code(), + // Return if the segment is passive + None => return None + }; + + // [op, End] + if init_expr.len() != 2 { + return None; + } + let offset = match init_expr[0] { + Instruction::I32Const(v) => v as u32, + Instruction::GetGlobal(idx) => { + let global_val = module_instance.globals().get(idx as usize)?.get(); + match global_val { + RuntimeValue::I32(v) => v as u32, + _ => return None, + } + } + _ => return None, + }; + + Some((offset, contents)) + }) + .collect::>>()?; + + // Collect all values of mutable globals. + let global_mut_values = module_instance + .globals() + .iter() + .filter(|g| g.is_mutable()) + .map(|g| g.get()) + .collect(); + + Some(Self { + data_segments: prepared_segments, + global_mut_values, + heap_pages, + }) + } + + /// Reset the runtime instance to the initial version by restoring + /// 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)?; + + // First, erase the memory and copy the data segments into it. + memory + .erase() + .map_err(|_| WasmError::ApplySnapshotFailed)?; + for (offset, contents) in &self.data_segments { + memory + .set(*offset, contents) + .map_err(|_| WasmError::ApplySnapshotFailed)?; + } + + // Second, restore the values of mutable globals. + for (global_ref, global_val) in instance + .globals() + .iter() + .filter(|g| g.is_mutable()) + .zip(self.global_mut_values.iter()) + { + // the instance should be the same as used for preserving and + // we iterate the same way it as we do it for preserving values that means that the + // types should be the same and all the values are mutable. So no error is expected/ + global_ref + .set(*global_val) + .map_err(|_| WasmError::ApplySnapshotFailed)?; + } + Ok(()) + } +} + +/// A runtime along with its initial state snapshot. +#[derive(Clone)] +pub struct WasmiRuntime { + /// A wasm module instance. + instance: ModuleRef, + /// The snapshot of the instance's state taken just after the instantiation. + state_snapshot: StateSnapshot, +} + +impl WasmiRuntime { + /// Perform an operation with the clean version of the runtime wasm instance. + fn with(&self, f: F) -> R + where + F: FnOnce(&ModuleRef) -> R, + { + self.state_snapshot.apply(&self.instance).expect( + "applying the snapshot can only fail if the passed instance is different + from the one that was used for creation of the snapshot; + we use the snapshot that is directly associated with the instance; + thus the snapshot was created using the instance; + qed", + ); + f(&self.instance) + } +} + +impl WasmRuntime for WasmiRuntime { + fn update_heap_pages(&mut self, heap_pages: u64) -> bool { + self.state_snapshot.heap_pages == heap_pages + } + + fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) + -> Result, Error> + { + self.with(|module| { + call_in_wasm_module(ext, module, method, data) + }) + } +} + +pub fn create_instance(code: &[u8], heap_pages: u64) -> Result { + let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?; + + // Extract the data segments from the wasm code. + // + // A return of this error actually indicates that there is a problem in logic, since + // we just loaded and validated the `module` above. + let data_segments = extract_data_segments(&code)?; + + // Instantiate this module. + let instance = instantiate_module(heap_pages as usize, &module) + .map_err(|e| WasmError::Instantiation(e.to_string()))?; + + // Take state snapshot before executing anything. + let state_snapshot = StateSnapshot::take(&instance, data_segments, heap_pages) + .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; + qed + ", + ); + + Ok(WasmiRuntime { + instance, + state_snapshot, + }) +} + +/// Extract the data segments from the given wasm code. +/// +/// Returns `Err` if the given wasm code cannot be deserialized. +fn extract_data_segments(wasm_code: &[u8]) -> Result, WasmError> { + let raw_module: RawModule = deserialize_buffer(wasm_code) + .map_err(|_| WasmError::CantDeserializeWasm)?; + + let segments = raw_module + .data_section() + .map(|ds| ds.entries()) + .unwrap_or(&[]) + .to_vec(); + Ok(segments) +} diff --git a/core/executor/src/wasmtime/function_executor.rs b/core/executor/src/wasmtime/function_executor.rs new file mode 100644 index 0000000000000000000000000000000000000000..5dc8f42b280c45a53ef7c4829e03bd086c19e64d --- /dev/null +++ b/core/executor/src/wasmtime/function_executor.rs @@ -0,0 +1,387 @@ +// 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 . + +use crate::allocator::FreeingBumpHeapAllocator; +use crate::error::{Error, Result}; +use crate::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex}; +use crate::wasmtime::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 primitives::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 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(Error::Wasmtime)?; + + // 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: "sr-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/core/executor/src/wasmtime/mod.rs b/core/executor/src/wasmtime/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..7f442417ab8499906f41f72eb02b8161cdf7e3e5 --- /dev/null +++ b/core/executor/src/wasmtime/mod.rs @@ -0,0 +1,24 @@ +// 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 . + +///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute. + +mod function_executor; +mod runtime; +mod trampoline; +mod util; + +pub use runtime::create_instance; diff --git a/core/executor/src/wasmtime/runtime.rs b/core/executor/src/wasmtime/runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..da668e9c309994576af6d52b8990f7fdb7436db2 --- /dev/null +++ b/core/executor/src/wasmtime/runtime.rs @@ -0,0 +1,364 @@ +// 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 . + +//! Defines the compiled Wasm runtime that uses Wasmtime internally. + +use crate::error::{Error, Result, WasmError}; +use crate::host_interface::SubstrateExternals; +use crate::wasm_runtime::WasmRuntime; +use crate::wasm_utils::interpret_runtime_api_result; +use crate::wasmtime::function_executor::FunctionExecutorState; +use crate::wasmtime::trampoline::{EnvState, make_trampoline}; +use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into, write_memory_from}; +use crate::Externalities; + +use cranelift_codegen::ir; +use cranelift_codegen::isa::TargetIsa; +use cranelift_entity::{EntityRef, PrimaryMap}; +use cranelift_frontend::FunctionBuilderContext; +use cranelift_wasm::DefinedFuncIndex; +use std::cell::RefCell; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::rc::Rc; +use wasm_interface::{HostFunctions, Pointer, WordSize}; +use wasmtime_environ::{Module, translate_signature}; +use wasmtime_jit::{ + ActionOutcome, ActionError, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context, + SetupError, RuntimeValue, +}; +use wasmtime_runtime::{Export, Imports, InstanceHandle, VMFunctionBody}; + +/// A `WasmRuntime` implementation using the Wasmtime JIT to compile the runtime module to native +/// and execute the compiled code. +pub struct WasmtimeRuntime { + module: CompiledModule, + context: Context, + max_heap_pages: Option, + heap_pages: u32, +} + +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 call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result> { + call_method( + &mut self.context, + &mut self.module, + ext, + method, + data, + self.heap_pages, + ) + } +} + +/// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to +/// machine code, which can be computationally heavy. +pub fn create_instance(code: &[u8], heap_pages: u64) + -> std::result::Result +{ + let (compiled_module, context) = create_compiled_unit(code)?; + + // 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)?; + + Ok(WasmtimeRuntime { + module: compiled_module, + context, + max_heap_pages, + heap_pages, + }) +} + +fn create_compiled_unit(code: &[u8]) + -> 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)?; + context.name_instance("env".to_owned(), env_module); + + // Compile the wasm module. + let module = context.compile_module(&code) + .map_err(WasmError::WasmtimeSetup)?; + + Ok((module, context)) +} + +/// Call a function inside a precompiled Wasm module. +fn call_method( + context: &mut Context, + module: &mut CompiledModule, + ext: &mut dyn Externalities, + 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(SetupError::Instantiate) + .map_err(ActionError::Setup) + .map_err(Error::Wasmtime)?; + + // 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 = externalities::set_and_run_with_externalities(ext, || { + context + .invoke(&mut instance, method, &args[..]) + .map_err(Error::Wasmtime) + })?; + 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)] => + interpret_runtime_api_result(*retval), + _ => return Err(Error::InvalidReturn), + } + ActionOutcome::Trapped { message } => + return Err(trap_error.unwrap_or_else(|| + format!("Wasm execution trapped: {}", message).into() + )), + }; + + // 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, output_ptr, &mut output)?; + Ok(output) +} + +/// The implementation is based on wasmtime_wasi::instantiate_wasi. +fn instantiate_env_module( + global_exports: Rc>>>, + compiler: Compiler, +) -> 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 SubstrateExternals::functions().iter() { + 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); + + 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::WasmtimeSetup(SetupError::Instantiate(e))) +} + +/// Build a new TargetIsa for the host machine. +fn target_isa() -> std::result::Result, WasmError> { + let isa_builder = cranelift_native::builder() + .map_err(WasmError::MissingCompilerSupport)?; + 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), + } + }; + instance.memory_grow(memory_index, pages) + .map(|_| ()) + .ok_or_else(|| "requested heap_pages would exceed maximum memory size".into()) +} + +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()) +} + +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()) +} + +fn inject_input_data( + context: &mut Context, + instance: &mut InstanceHandle, + 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)?; + 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) +} diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs new file mode 100644 index 0000000000000000000000000000000000000000..7abc59faa5ef5c4bb908b5b0eeba044357a1618a --- /dev/null +++ b/core/executor/src/wasmtime/trampoline.rs @@ -0,0 +1,329 @@ +// 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 . + +//! 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 cranelift_codegen::{Context, binemit, ir, isa}; +use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode}; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; +use wasmtime_jit::{CodeMemory, Compiler}; +use wasmtime_runtime::{VMContext, VMFunctionBody}; +use wasm_interface::{HostFunctions, Function, Value, ValueType}; +use std::{cmp, panic, ptr}; + +use crate::error::{Error, WasmError}; +use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor}; + +const CALL_SUCCESS: u32 = 0; +const CALL_FAILED_WITH_ERROR: u32 = 1; +const CALL_WITH_BAD_HOST_STATE: u32 = 2; +const CALL_PANICKED: u32 = 3; + +/// 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 { + externals: &'static [&'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) -> Self { + EnvState { + externals: HF::functions(), + trap: None, + compiler, + code_memory, + executor_state: None, + } + } + + /// 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 { + let result = panic::catch_unwind(|| { + if let Some(state) = (*vmctx).host_state().downcast_mut::() { + match stub_fn_inner( + vmctx, + state.externals, + &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 + } + }); + result.unwrap_or(CALL_PANICKED) +} + +/// 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 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)); + + // Execute and write output back to the stack. + let return_val = func.execute(&mut context, &mut args) + .map_err(|e| Error::FunctionExecution(func.name().to_string(), e))?; + if let Some(val) = return_val { + write_value_to(values_vec, val); + } + + Ok(()) +} + +/// 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: {}", e)))?; + + let func_ref = code_memory + .allocate_copy_of_byte_slice(&code_buf) + .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/core/executor/src/wasmtime/util.rs b/core/executor/src/wasmtime/util.rs new file mode 100644 index 0000000000000000000000000000000000000000..874ccc8c85fbe2073b3633513237e38681c48297 --- /dev/null +++ b/core/executor/src/wasmtime/util.rs @@ -0,0 +1,113 @@ +// 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 . + +use crate::error::{Error, Result}; + +use cranelift_codegen::{ir, isa}; +use std::ops::Range; +use 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. +pub fn checked_range(offset: usize, len: usize, max: usize) -> Option> { + let end = offset.checked_add(len)?; + if end <= max { + Some(offset..end) + } else { + None + } +} + +/// Convert a wasm_interface Signature into a cranelift_codegen Signature. +pub fn cranelift_ir_signature(signature: Signature, call_conv: &isa::CallConv) -> 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/core/externalities/Cargo.toml b/core/externalities/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..97ece5439ac998301755e17ec322092f0037782b --- /dev/null +++ b/core/externalities/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "substrate-externalities" +version = "2.0.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +primitive-types = { version = "0.5.1", features = ["codec"] } +primitives-storage = { package = "substrate-primitives-storage", path = "../primitives/storage" } +rstd = { package = "sr-std", path = "../sr-std" } +environmental = { version = "1.0.2" } diff --git a/core/externalities/src/extensions.rs b/core/externalities/src/extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..c7d7bc48538ac645b9b6cf26858355906124f29f --- /dev/null +++ b/core/externalities/src/extensions.rs @@ -0,0 +1,127 @@ +// Copyright 2017-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 . + +//! Externalities extensions storage. +//! +//! Externalities support to register a wide variety custom extensions. The [`Extensions`] provides +//! some convenience functionality to store and retrieve these extensions. +//! +//! It is required that each extension implements the [`Extension`] trait. + +use std::{collections::HashMap, any::{Any, TypeId}, ops::DerefMut}; + +/// Marker trait for types that should be registered as [`Externalities`](crate::Externalities) extension. +/// +/// As extensions are stored as `Box`, this trait should give more confidence that the correct +/// type is registered and requested. +pub trait Extension: Sized {} + +/// Macro for declaring an extension that usable with [`Extensions`]. +/// +/// The extension will be an unit wrapper struct that implements [`Extension`], `Deref` and +/// `DerefMut`. The wrapped type is given by the user. +/// +/// # Example +/// ``` +/// # use substrate_externalities::decl_extension; +/// decl_extension! { +/// /// Some test extension +/// struct TestExt(String); +/// } +/// ``` +#[macro_export] +macro_rules! decl_extension { + ( + $( #[ $attr:meta ] )* + $vis:vis struct $ext_name:ident ($inner:ty); + ) => { + $( #[ $attr ] )* + $vis struct $ext_name (pub $inner); + + impl $crate::Extension for $ext_name {} + + impl std::ops::Deref for $ext_name { + type Target = $inner; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::DerefMut for $ext_name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + } +} + +/// Something that provides access to the [`Extensions`] store. +/// +/// This is a super trait of the [`Externalities`](crate::Externalities). +pub trait ExtensionStore { + /// Tries to find a registered extension by the given `type_id` and returns it as a `&mut dyn Any`. + /// + /// It is advised to use [`ExternalitiesExt::extension`](crate::ExternalitiesExt::extension) + /// instead of this function to get type system support and automatic type downcasting. + fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any>; +} + +/// Stores extensions that should be made available through the externalities. +#[derive(Default)] +pub struct Extensions { + extensions: HashMap>, +} + +impl Extensions { + /// Create new instance of `Self`. + pub fn new() -> Self { + Self::default() + } + + /// Register the given extension. + pub fn register(&mut self, ext: E) { + self.extensions.insert(ext.type_id(), Box::new(ext)); + } + + /// Return a mutable reference to the requested extension. + pub fn get_mut(&mut self, ext_type_id: TypeId) -> Option<&mut dyn Any> { + self.extensions.get_mut(&ext_type_id).map(DerefMut::deref_mut) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct DummyExt(u32); + impl Extension for DummyExt {} + + struct DummyExt2(u32); + impl Extension for DummyExt2 {} + + #[test] + fn register_and_retrieve_extension() { + let mut exts = Extensions::new(); + exts.register(DummyExt(1)); + exts.register(DummyExt2(2)); + + let ext = exts.get_mut(TypeId::of::()).expect("Extension is registered"); + let ext_ty = ext.downcast_mut::().expect("Downcasting works"); + + assert_eq!(ext_ty.0, 1); + } +} diff --git a/core/externalities/src/lib.rs b/core/externalities/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..ecd5f7a7a751da68d9b069ab749c2c96edec7ae0 --- /dev/null +++ b/core/externalities/src/lib.rs @@ -0,0 +1,139 @@ +// Copyright 2017-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 . + +//! Substrate externalities abstraction +//! +//! The externalities mainly provide access to storage and to registered extensions. Extensions +//! are for example the keystore or the offchain externalities. These externalities are used to +//! access the node from the runtime via the runtime interfaces. +//! +//! This crate exposes the main [`Externalities`] trait. + +use primitive_types::H256; + +use std::any::{Any, TypeId}; + +use primitives_storage::ChildStorageKey; + +pub use scope_limited::{set_and_run_with_externalities, with_externalities}; +pub use extensions::{Extension, Extensions, ExtensionStore}; + +mod extensions; +mod scope_limited; + +/// The Substrate externalities. +/// +/// Provides access to the storage and to other registered extensions. +pub trait Externalities: ExtensionStore { + /// Read runtime storage. + fn storage(&self, key: &[u8]) -> Option>; + + /// Get storage value hash. This may be optimized for large values. + fn storage_hash(&self, key: &[u8]) -> Option; + + /// Get child storage value hash. This may be optimized for large values. + fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option; + + /// Read original runtime storage, ignoring any overlayed changes. + fn original_storage(&self, key: &[u8]) -> Option>; + + /// Read original runtime child storage, ignoring any overlayed changes. + fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option>; + + /// Get original storage value hash, ignoring any overlayed changes. + /// This may be optimized for large values. + fn original_storage_hash(&self, key: &[u8]) -> Option; + + /// Get original child storage value hash, ignoring any overlayed changes. + /// This may be optimized for large values. + fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option; + + /// Read child runtime storage. + fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option>; + + /// Set storage entry `key` of current contract being called (effective immediately). + fn set_storage(&mut self, key: Vec, value: Vec) { + self.place_storage(key, Some(value)); + } + + /// Set child storage entry `key` of current contract being called (effective immediately). + fn set_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Vec) { + self.place_child_storage(storage_key, key, Some(value)) + } + + /// Clear a storage entry (`key`) of current contract being called (effective immediately). + fn clear_storage(&mut self, key: &[u8]) { + self.place_storage(key.to_vec(), None); + } + + /// Clear a child storage entry (`key`) of current contract being called (effective immediately). + fn clear_child_storage(&mut self, storage_key: ChildStorageKey, key: &[u8]) { + self.place_child_storage(storage_key, key.to_vec(), None) + } + + /// Whether a storage entry exists. + fn exists_storage(&self, key: &[u8]) -> bool { + self.storage(key).is_some() + } + + /// Whether a child storage entry exists. + fn exists_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> bool { + self.child_storage(storage_key, key).is_some() + } + + /// Clear an entire child storage. + fn kill_child_storage(&mut self, storage_key: ChildStorageKey); + + /// Clear storage entries which keys are start with the given prefix. + fn clear_prefix(&mut self, prefix: &[u8]); + + /// Clear child storage entries which keys are start with the given prefix. + fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]); + + /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). + fn place_storage(&mut self, key: Vec, value: Option>); + + /// Set or clear a child storage entry. Return whether the operation succeeds. + fn place_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Option>); + + /// Get the identity of the chain. + fn chain_id(&self) -> u64; + + /// Get the trie root of the current storage map. This will also update all child storage keys + /// in the top-level storage map. + fn storage_root(&mut self) -> H256; + + /// Get the trie root of a child storage map. This will also update the value of the child + /// storage keys in the top-level storage map. + /// If the storage root equals the default hash as defined by the trie, the key in the top-level + /// storage map will be removed. + fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec; + + /// Get the change trie root of the current storage overlay at a block with given parent. + fn storage_changes_root(&mut self, parent: H256) -> Result, ()>; +} + +/// Extension for the [`Externalities`] trait. +pub trait ExternalitiesExt { + /// Tries to find a registered extension and returns a mutable reference. + fn extension(&mut self) -> Option<&mut T>; +} + +impl ExternalitiesExt for T { + fn extension(&mut self) -> Option<&mut A> { + self.extension_by_type_id(TypeId::of::()).and_then(Any::downcast_mut) + } +} diff --git a/core/externalities/src/scope_limited.rs b/core/externalities/src/scope_limited.rs new file mode 100644 index 0000000000000000000000000000000000000000..ae185449be2b0cf2573872787ba4ce3f916fa28b --- /dev/null +++ b/core/externalities/src/scope_limited.rs @@ -0,0 +1,37 @@ +// 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 . + +//! Stores the externalities in an `environmental` value to make it scope limited available. + +use crate::Externalities; + +environmental::environmental!(ext: trait Externalities); + +/// Set the given externalities while executing the given closure. To get access to the externalities +/// while executing the given closure [`with_externalities`] grants access to them. The externalities +/// are only set for the same thread this function was called from. +pub fn set_and_run_with_externalities(ext: &mut dyn Externalities, f: F) -> R + where F: FnOnce() -> R +{ + ext::using(ext, f) +} + +/// Execute the given closure with the currently set externalities. +/// +/// Returns `None` if no externalities are set or `Some(_)` with the result of the closure. +pub fn with_externalities R, R>(f: F) -> Option { + ext::with(f) +} diff --git a/core/finality-grandpa/Cargo.toml b/core/finality-grandpa/Cargo.toml index 0a42ff4531fbe00dbb037d8aadca4f4806a392af..a4c6a0278a44504bc184a4d40af6779b86664d40 100644 --- a/core/finality-grandpa/Cargo.toml +++ b/core/finality-grandpa/Cargo.toml @@ -19,7 +19,7 @@ consensus_common = { package = "substrate-consensus-common", path = "../consensu primitives = { package = "substrate-primitives", path = "../primitives" } substrate-telemetry = { path = "../telemetry" } keystore = { package = "substrate-keystore", path = "../keystore" } -serde_json = "1.0.40" +serde_json = "1.0.41" client = { package = "substrate-client", path = "../client" } header-metadata = { package = "substrate-header-metadata", path = "../client/header-metadata" } inherents = { package = "substrate-inherents", path = "../../core/inherents" } @@ -34,6 +34,7 @@ network = { package = "substrate-network", path = "../network", features = ["tes keyring = { package = "substrate-keyring", path = "../keyring" } test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client"} babe_primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives" } -env_logger = "0.6.2" +state_machine = { package = "substrate-state-machine", path = "../state-machine" } +env_logger = "0.7.0" tokio = "0.1.22" tempfile = "3.1.0" diff --git a/core/finality-grandpa/primitives/src/lib.rs b/core/finality-grandpa/primitives/src/lib.rs index 384319a2982bcab0e31bd49ba852a2471dbe076a..27139bbeeffa8366e1313f1b40ff8e0cfaafae7d 100644 --- a/core/finality-grandpa/primitives/src/lib.rs +++ b/core/finality-grandpa/primitives/src/lib.rs @@ -24,7 +24,7 @@ extern crate alloc; #[cfg(feature = "std")] use serde::Serialize; use codec::{Encode, Decode, Codec}; -use sr_primitives::ConsensusEngineId; +use sr_primitives::{ConsensusEngineId, RuntimeDebug}; use client::decl_runtime_apis; use rstd::vec::Vec; @@ -52,15 +52,15 @@ pub type AuthorityWeight = u64; /// The index of an authority. pub type AuthorityIndex = u64; -/// The identifier of a GRANDPA set. +/// The monotonic identifier of a GRANDPA set of authorities. pub type SetId = u64; /// The round indicator. pub type RoundNumber = u64; /// A scheduled change of authority set. -#[cfg_attr(feature = "std", derive(Debug, Serialize))] -#[derive(Clone, Eq, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Serialize))] +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] pub struct ScheduledChange { /// The new authorities after the change, along with their respective weights. pub next_authorities: Vec<(AuthorityId, AuthorityWeight)>, @@ -69,8 +69,8 @@ pub struct ScheduledChange { } /// An consensus log item for GRANDPA. -#[cfg_attr(feature = "std", derive(Serialize, Debug))] -#[derive(Decode, Encode, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize))] +#[derive(Decode, Encode, PartialEq, Eq, Clone, RuntimeDebug)] pub enum ConsensusLog { /// Schedule an authority set change. /// diff --git a/core/finality-grandpa/src/communication/gossip.rs b/core/finality-grandpa/src/communication/gossip.rs index 831be7ad145310cb8fa419a4cb18ff675bb1c0aa..efcd1d48c67f795c3f72dcbf0fabca7d42ebcf07 100644 --- a/core/finality-grandpa/src/communication/gossip.rs +++ b/core/finality-grandpa/src/communication/gossip.rs @@ -132,7 +132,7 @@ struct View { impl Default for View { fn default() -> Self { View { - round: Round(0), + round: Round(1), set_id: SetId(0), last_commit: None, } @@ -144,7 +144,7 @@ impl View { fn update_set(&mut self, set_id: SetId) { if set_id != self.set_id { self.set_id = set_id; - self.round = Round(0); + self.round = Round(1); } } @@ -183,7 +183,13 @@ impl View { const KEEP_RECENT_ROUNDS: usize = 3; -/// Tracks topics we keep messages for. +/// Tracks gossip topics that we are keeping messages for. We keep topics of: +/// +/// - the last `KEEP_RECENT_ROUNDS` complete GRANDPA rounds, +/// +/// - the topic for the current and next round, +/// +/// - and a global topic for commit and catch-up messages. struct KeepTopics { current_set: SetId, rounds: VecDeque<(Round, SetId)>, @@ -194,21 +200,30 @@ impl KeepTopics { fn new() -> Self { KeepTopics { current_set: SetId(0), - rounds: VecDeque::with_capacity(KEEP_RECENT_ROUNDS + 1), + rounds: VecDeque::with_capacity(KEEP_RECENT_ROUNDS + 2), reverse_map: HashMap::new(), } } fn push(&mut self, round: Round, set_id: SetId) { self.current_set = std::cmp::max(self.current_set, set_id); - self.rounds.push_back((round, set_id)); - // the 1 is for the current round. - while self.rounds.len() > KEEP_RECENT_ROUNDS + 1 { + // under normal operation the given round is already tracked (since we + // track one round ahead). if we skip rounds (with a catch up) the given + // round topic might not be tracked yet. + if !self.rounds.contains(&(round, set_id)) { + self.rounds.push_back((round, set_id)); + } + + // we also accept messages for the next round + self.rounds.push_back((Round(round.0.saturating_add(1)), set_id)); + + // the 2 is for the current and next round. + while self.rounds.len() > KEEP_RECENT_ROUNDS + 2 { let _ = self.rounds.pop_front(); } - let mut map = HashMap::with_capacity(KEEP_RECENT_ROUNDS + 2); + let mut map = HashMap::with_capacity(KEEP_RECENT_ROUNDS + 3); map.insert(super::global_topic::(self.current_set.0), (None, self.current_set)); for &(round, set) in &self.rounds { @@ -247,7 +262,7 @@ fn neighbor_topics(view: &View>) -> Vec { #[derive(Debug, Encode, Decode)] pub(super) enum GossipMessage { /// Grandpa message with round and set info. - VoteOrPrecommit(VoteOrPrecommitMessage), + Vote(VoteMessage), /// Grandpa commit message with round and set info. Commit(FullCommitMessage), /// A neighbor packet. Not repropagated. @@ -264,9 +279,9 @@ impl From>> for GossipMessage { +pub(super) struct VoteMessage { /// The round this message is from. pub(super) round: Round, /// The voter set ID this message is from. @@ -497,6 +512,40 @@ enum PendingCatchUp { }, } +/// Configuration for the round catch-up mechanism. +enum CatchUpConfig { + /// Catch requests are enabled, our node will issue them whenever it sees a + /// neighbor packet for a round further than `CATCH_UP_THRESHOLD`. If + /// `only_from_authorities` is set, the node will only send catch-up + /// requests to other authorities it is connected to. This is useful if the + /// GRANDPA observer protocol is live on the network, in which case full + /// nodes (non-authorities) don't have the necessary round data to answer + /// catch-up requests. + Enabled { only_from_authorities: bool }, + /// Catch-up requests are disabled, our node will never issue them. This is + /// useful for the GRANDPA observer mode, where we are only interested in + /// commit messages and don't need to follow the full round protocol. + Disabled, +} + +impl CatchUpConfig { + fn enabled(only_from_authorities: bool) -> CatchUpConfig { + CatchUpConfig::Enabled { only_from_authorities } + } + + fn disabled() -> CatchUpConfig { + CatchUpConfig::Disabled + } + + fn request_allowed(&self, peer: &PeerInfo) -> bool { + match self { + CatchUpConfig::Disabled => false, + CatchUpConfig::Enabled { only_from_authorities, .. } => + !only_from_authorities || peer.roles.is_authority(), + } + } +} + struct Inner { local_view: Option>>, peers: Peers>, @@ -505,13 +554,30 @@ struct Inner { config: crate::Config, next_rebroadcast: Instant, pending_catch_up: PendingCatchUp, - catch_up_enabled: bool, + catch_up_config: CatchUpConfig, } type MaybeMessage = Option<(Vec, NeighborPacket>)>; impl Inner { - fn new(config: crate::Config, catch_up_enabled: bool) -> Self { + fn new(config: crate::Config) -> Self { + let catch_up_config = if config.observer_enabled { + if config.is_authority { + // since the observer protocol is enabled, we will only issue + // catch-up requests if we are an authority (and only to other + // authorities). + CatchUpConfig::enabled(true) + } else { + // otherwise, we are running the observer protocol and don't + // care about catch-up requests. + CatchUpConfig::disabled() + } + } else { + // if the observer protocol isn't enabled, then any full node should + // be able to answer catch-up requests. + CatchUpConfig::enabled(false) + }; + Inner { local_view: None, peers: Peers::default(), @@ -519,7 +585,7 @@ impl Inner { next_rebroadcast: Instant::now() + REBROADCAST_AFTER, authorities: Vec::new(), pending_catch_up: PendingCatchUp::None, - catch_up_enabled, + catch_up_config, config, } } @@ -554,7 +620,7 @@ impl Inner { { let local_view = match self.local_view { ref mut x @ None => x.get_or_insert(View { - round: Round(0), + round: Round(1), set_id, last_commit: None, }), @@ -566,7 +632,7 @@ impl Inner { }; local_view.update_set(set_id); - self.live_topics.push(Round(0), set_id); + self.live_topics.push(Round(1), set_id); self.authorities = authorities; } self.multicast_neighbor_packet() @@ -603,7 +669,7 @@ impl Inner { cost::PAST_REJECTION } - fn validate_round_message(&self, who: &PeerId, full: &VoteOrPrecommitMessage) + fn validate_round_message(&self, who: &PeerId, full: &VoteMessage) -> Action { match self.consider_vote(full.round, full.set_id) { @@ -808,10 +874,6 @@ impl Inner { } fn try_catch_up(&mut self, who: &PeerId) -> (Option>, Option) { - if !self.catch_up_enabled { - return (None, None); - } - let mut catch_up = None; let mut report = None; @@ -821,7 +883,7 @@ impl Inner { // won't be able to reply since they don't follow the full GRANDPA // protocol and therefore might not have the vote data available. if let (Some(peer), Some(local_view)) = (self.peers.peer(who), &self.local_view) { - if peer.roles.is_authority() && + if self.catch_up_config.request_allowed(&peer) && peer.view.set_id == local_view.set_id && peer.view.round.0.saturating_sub(CATCH_UP_THRESHOLD) > local_view.round.0 { @@ -934,11 +996,10 @@ impl GossipValidator { pub(super) fn new( config: crate::Config, set_state: environment::SharedVoterSetState, - catch_up_enabled: bool, ) -> (GossipValidator, ReportStream) { let (tx, rx) = mpsc::unbounded(); let val = GossipValidator { - inner: parking_lot::RwLock::new(Inner::new(config, catch_up_enabled)), + inner: parking_lot::RwLock::new(Inner::new(config)), set_state, report_sender: tx, }; @@ -994,7 +1055,7 @@ impl GossipValidator { let action = { match GossipMessage::::decode(&mut data) { - Ok(GossipMessage::VoteOrPrecommit(ref message)) + Ok(GossipMessage::Vote(ref message)) => self.inner.write().validate_round_message(who, message), Ok(GossipMessage::Commit(ref message)) => self.inner.write().validate_commit_message(who, message), Ok(GossipMessage::Neighbor(update)) => { @@ -1154,7 +1215,7 @@ impl network_gossip::Validator for GossipValidator Ok(GossipMessage::Neighbor(_)) => false, Ok(GossipMessage::CatchUpRequest(_)) => false, Ok(GossipMessage::CatchUp(_)) => false, - Ok(GossipMessage::VoteOrPrecommit(_)) => false, // should not be the case. + Ok(GossipMessage::Vote(_)) => false, // should not be the case. } }) } @@ -1259,6 +1320,8 @@ mod tests { justification_period: 256, keystore: None, name: None, + is_authority: true, + observer_enabled: true, } } @@ -1423,7 +1486,6 @@ mod tests { let (val, _) = GossipValidator::::new( config(), voter_set_state(), - true, ); let set_id = 1; @@ -1459,18 +1521,17 @@ mod tests { let (val, _) = GossipValidator::::new( config(), voter_set_state(), - true, ); let set_id = 1; let auth = AuthorityId::from_slice(&[1u8; 32]); let peer = PeerId::random(); val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); - val.note_round(Round(0), |_, _| {}); + val.note_round(Round(1), |_, _| {}); let inner = val.inner.read(); - let unknown_voter = inner.validate_round_message(&peer, &VoteOrPrecommitMessage { - round: Round(0), + let unknown_voter = inner.validate_round_message(&peer, &VoteMessage { + round: Round(1), set_id: SetId(set_id), message: SignedMessage:: { message: grandpa::Message::Prevote(grandpa::Prevote { @@ -1482,8 +1543,8 @@ mod tests { } }); - let bad_sig = inner.validate_round_message(&peer, &VoteOrPrecommitMessage { - round: Round(0), + let bad_sig = inner.validate_round_message(&peer, &VoteMessage { + round: Round(1), set_id: SetId(set_id), message: SignedMessage:: { message: grandpa::Message::Prevote(grandpa::Prevote { @@ -1504,7 +1565,6 @@ mod tests { let (val, _) = GossipValidator::::new( config(), voter_set_state(), - true, ); let set_id = 1; @@ -1512,7 +1572,7 @@ mod tests { let peer = PeerId::random(); val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); - val.note_round(Round(0), |_, _| {}); + val.note_round(Round(1), |_, _| {}); let validate_catch_up = || { let mut inner = val.inner.write(); @@ -1548,19 +1608,19 @@ mod tests { #[test] fn unanswerable_catch_up_requests_discarded() { - // create voter set state with round 1 completed + // create voter set state with round 2 completed let set_state: SharedVoterSetState = { let mut completed_rounds = voter_set_state().read().completed_rounds(); completed_rounds.push(environment::CompletedRound { - number: 1, + number: 2, state: grandpa::round::State::genesis(Default::default()), base: Default::default(), votes: Default::default(), }); let mut current_rounds = environment::CurrentRounds::new(); - current_rounds.insert(2, environment::HasVoted::No); + current_rounds.insert(3, environment::HasVoted::No); let set_state = environment::VoterSetState::::Live { completed_rounds, @@ -1573,7 +1633,6 @@ mod tests { let (val, _) = GossipValidator::::new( config(), set_state.clone(), - true, ); let set_id = 1; @@ -1581,7 +1640,7 @@ mod tests { let peer = PeerId::random(); val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); - val.note_round(Round(2), |_, _| {}); + val.note_round(Round(3), |_, _| {}); // add the peer making the request to the validator, // otherwise it is discarded @@ -1597,7 +1656,7 @@ mod tests { &set_state, ); - // we're at round 2, a catch up request for round 10 is out of scope + // we're at round 3, a catch up request for round 10 is out of scope assert!(res.0.is_none()); assert_eq!(res.1, Action::Discard(cost::OUT_OF_SCOPE_MESSAGE)); @@ -1605,16 +1664,16 @@ mod tests { &peer, CatchUpRequestMessage { set_id: SetId(set_id), - round: Round(1), + round: Round(2), }, &set_state, ); - // a catch up request for round 1 should be answered successfully + // a catch up request for round 2 should be answered successfully match res.0.unwrap() { GossipMessage::CatchUp(catch_up) => { assert_eq!(catch_up.set_id, SetId(set_id)); - assert_eq!(catch_up.message.round_number, 1); + assert_eq!(catch_up.message.round_number, 2); assert_eq!(res.1, Action::Discard(cost::CATCH_UP_REPLY)); }, @@ -1628,7 +1687,6 @@ mod tests { let (val, _) = GossipValidator::::new( config(), set_state.clone(), - true, ); // the validator starts at set id 2 @@ -1708,7 +1766,6 @@ mod tests { let (val, _) = GossipValidator::::new( config(), voter_set_state(), - true, ); // the validator starts at set id 1. @@ -1768,10 +1825,20 @@ mod tests { #[test] fn doesnt_send_catch_up_requests_when_disabled() { // we create a gossip validator with catch up requests disabled. + let config = { + let mut c = config(); + + // if the observer protocol is enabled and we are not an authority, + // then we don't issue any catch-up requests. + c.is_authority = false; + c.observer_enabled = true; + + c + }; + let (val, _) = GossipValidator::::new( - config(), + config, voter_set_state(), - false, ); // the validator starts at set id 1. @@ -1801,11 +1868,10 @@ mod tests { } #[test] - fn doesnt_send_catch_up_requests_to_non_authorities() { + fn doesnt_send_catch_up_requests_to_non_authorities_when_observer_enabled() { let (val, _) = GossipValidator::::new( config(), voter_set_state(), - true, ); // the validator starts at set id 1. @@ -1849,4 +1915,80 @@ mod tests { _ => panic!("expected catch up message"), } } + + #[test] + fn sends_catch_up_requests_to_non_authorities_when_observer_disabled() { + let config = { + let mut c = config(); + + // if the observer protocol is disable any full-node should be able + // to answer catch-up requests. + c.observer_enabled = false; + + c + }; + + let (val, _) = GossipValidator::::new( + config, + voter_set_state(), + ); + + // the validator starts at set id 1. + val.note_set(SetId(1), Vec::new(), |_, _| {}); + + // add the peer making the requests to the validator, otherwise it is + // discarded. + let peer_full = PeerId::random(); + val.inner.write().peers.new_peer(peer_full.clone(), Roles::FULL); + + let (_, _, catch_up_request, _) = val.inner.write().import_neighbor_message( + &peer_full, + NeighborPacket { + round: Round(42), + set_id: SetId(1), + commit_finalized_height: 50, + }, + ); + + // importing a neighbor message from a peer in the same set in a later + // round should lead to a catch up request, the node is not an + // authority, but since the observer protocol is disabled we should + // issue a catch-up request to it anyway. + match catch_up_request { + Some(GossipMessage::CatchUpRequest(request)) => { + assert_eq!(request.set_id, SetId(1)); + assert_eq!(request.round, Round(41)); + }, + _ => panic!("expected catch up message"), + } + } + + #[test] + fn doesnt_expire_next_round_messages() { + // NOTE: this is a regression test + let (val, _) = GossipValidator::::new( + config(), + voter_set_state(), + ); + + // the validator starts at set id 1. + val.note_set(SetId(1), Vec::new(), |_, _| {}); + + // we are at round 10 + val.note_round(Round(9), |_, _| {}); + val.note_round(Round(10), |_, _| {}); + + let mut is_expired = val.message_expired(); + + // we accept messages from rounds 9, 10 and 11 + // therefore neither of those should be considered expired + for round in &[9, 10, 11] { + assert!( + !is_expired( + crate::communication::round_topic::(*round, 1), + &[], + ) + ) + } + } } diff --git a/core/finality-grandpa/src/communication/mod.rs b/core/finality-grandpa/src/communication/mod.rs index 652c33c0262a598d8ba3202ba908eae78c370de1..4cc772fe9d4a03aa06a3e93655803b100d6f55f5 100644 --- a/core/finality-grandpa/src/communication/mod.rs +++ b/core/finality-grandpa/src/communication/mod.rs @@ -31,6 +31,7 @@ use std::sync::Arc; use futures::prelude::*; use futures::sync::{oneshot, mpsc}; +use futures03::stream::{StreamExt, TryStreamExt}; use grandpa::Message::{Prevote, Precommit, PrimaryPropose}; use grandpa::{voter, voter_set::VoterSet}; use log::{debug, trace}; @@ -38,7 +39,7 @@ use network::{consensus_gossip as network_gossip, NetworkService}; use network_gossip::ConsensusMessage; use codec::{Encode, Decode}; use primitives::Pair; -use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; +use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor}; use substrate_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; use tokio_executor::Executor; @@ -48,7 +49,7 @@ use crate::{ }; use crate::environment::HasVoted; use gossip::{ - GossipMessage, FullCatchUpMessage, FullCommitMessage, VoteOrPrecommitMessage, GossipValidator + GossipMessage, FullCatchUpMessage, FullCommitMessage, VoteMessage, GossipValidator }; use fg_primitives::{ AuthorityPair, AuthorityId, AuthoritySignature, SetId as SetIdNumber, RoundNumber, @@ -100,7 +101,7 @@ mod benefit { /// Intended to be a lightweight handle such as an `Arc`. pub trait Network: Clone + Send + 'static { /// A stream of input messages for a topic. - type In: Stream; + type In: Stream; /// Get a stream of messages for a specific gossip topic. fn messages_for(&self, topic: Block::Hash) -> Self::In; @@ -128,6 +129,14 @@ pub trait Network: Clone + Send + 'static { /// Inform peers that a block with given hash should be downloaded. fn announce(&self, block: Block::Hash, associated_data: Vec); + + /// Notifies the sync service to try and sync the given block from the given + /// peers. + /// + /// If the given vector of peers is empty then the underlying implementation + /// should make a best effort to fetch the block from any peers it is + /// connected to (NOTE: this assumption will change in the future #3629). + fn set_sync_fork_request(&self, peers: Vec, hash: Block::Hash, number: NumberFor); } /// Create a unique topic for a round and set-id combo. @@ -145,15 +154,30 @@ impl Network for Arc> where S: network::specialization::NetworkSpecialization, H: network::ExHashT, { - type In = NetworkStream; + type In = NetworkStream< + Box + Send + 'static>, + >; fn messages_for(&self, topic: B::Hash) -> Self::In { + // Given that one can only communicate with the Substrate network via the `NetworkService` via message-passing, + // and given that methods on the network consensus gossip are not exposed but only reachable by passing a + // closure into `with_gossip` on the `NetworkService` this function needs to make use of the `NetworkStream` + // construction. + // + // We create a oneshot channel and pass the sender within a closure to the network. At some point in the future + // the network passes the message channel back through the oneshot channel. But the consumer of this function + // expects a stream, not a stream within a oneshot. This complexity is abstracted within `NetworkStream`, + // waiting for the oneshot to resolve and from there on acting like a normal message channel. let (tx, rx) = oneshot::channel(); self.with_gossip(move |gossip, _| { - let inner_rx = gossip.messages_for(GRANDPA_ENGINE_ID, topic); + let inner_rx: Box + Send> = Box::new(gossip + .messages_for(GRANDPA_ENGINE_ID, topic) + .map(|x| Ok(x)) + .compat() + ); let _ = tx.send(inner_rx); }); - NetworkStream { outer: rx, inner: None } + NetworkStream::PollingOneshot(rx) } fn register_validator(&self, validator: Arc>) { @@ -200,30 +224,49 @@ impl Network for Arc> where fn announce(&self, block: B::Hash, associated_data: Vec) { self.announce_block(block, associated_data) } + + fn set_sync_fork_request(&self, peers: Vec, hash: B::Hash, number: NumberFor) { + NetworkService::set_sync_fork_request(self, peers, hash, number) + } } -/// A stream used by NetworkBridge in its implementation of Network. -pub struct NetworkStream { - inner: Option>, - outer: oneshot::Receiver> +/// A stream used by NetworkBridge in its implementation of Network. Given a oneshot that eventually returns a channel +/// which eventually returns messages, instead of: +/// +/// 1. polling the oneshot until it returns a message channel +/// +/// 2. polling the message channel for messages +/// +/// `NetworkStream` combines the two steps into one, requiring a consumer to only poll `NetworkStream` to retrieve +/// messages directly. +pub enum NetworkStream { + PollingOneshot(oneshot::Receiver), + PollingTopicNotifications(R), } -impl Stream for NetworkStream { - type Item = network_gossip::TopicNotification; +impl Stream for NetworkStream +where + R: Stream, +{ + type Item = R::Item; type Error = (); fn poll(&mut self) -> Poll, Self::Error> { - if let Some(ref mut inner) = self.inner { - return inner.poll(); - } - match self.outer.poll() { - Ok(futures::Async::Ready(mut inner)) => { - let poll_result = inner.poll(); - self.inner = Some(inner); - poll_result + match self { + NetworkStream::PollingOneshot(oneshot) => { + match oneshot.poll() { + Ok(futures::Async::Ready(mut stream)) => { + let poll_result = stream.poll(); + *self = NetworkStream::PollingTopicNotifications(stream); + poll_result + }, + Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady), + Err(_) => Err(()) + } + }, + NetworkStream::PollingTopicNotifications(stream) => { + stream.poll() }, - Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady), - Err(_) => Err(()) } } } @@ -245,17 +288,15 @@ impl> NetworkBridge { service: N, config: crate::Config, set_state: crate::environment::SharedVoterSetState, - on_exit: impl Future + Clone + Send + 'static, - catch_up_enabled: bool, + on_exit: impl Future + Clone + Send + 'static, ) -> ( Self, - impl futures::Future + Send + 'static, + impl Future + Send + 'static, ) { let (validator, report_stream) = GossipValidator::new( config, set_state.clone(), - catch_up_enabled, ); let validator = Arc::new(validator); @@ -275,8 +316,8 @@ impl> NetworkBridge { validator.note_round(Round(round.number), |_, _| {}); for signed in round.votes.iter() { - let message = gossip::GossipMessage::VoteOrPrecommit( - gossip::VoteOrPrecommitMessage:: { + let message = gossip::GossipMessage::Vote( + gossip::VoteMessage:: { message: signed.clone(), round: Round(round.number), set_id: SetId(set_id), @@ -341,7 +382,8 @@ impl> NetworkBridge { ); } - /// Get the round messages for a round in the current set ID. These are signature-checked. + /// Get a stream of signature-checked round messages from the network as well as a sink for round messages to the + /// network all within the current set. pub(crate) fn round_communication( &self, round: Round, @@ -379,7 +421,7 @@ impl> NetworkBridge { }) .and_then(move |msg| { match msg { - GossipMessage::VoteOrPrecommit(msg) => { + GossipMessage::Vote(msg) => { // check signature. if !voters.contains_key(&msg.message.id) { debug!(target: "afg", "Skipping message from unknown voter {}", msg.message.id); @@ -436,6 +478,9 @@ impl> NetworkBridge { 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); (incoming, outgoing) @@ -482,6 +527,16 @@ impl> NetworkBridge { (incoming, outgoing) } + + /// Notifies the sync service to try and sync the given block from the given + /// peers. + /// + /// If the given vector of peers is empty then the underlying implementation + /// should make a best effort to fetch the block from any peers it is + /// connected to (NOTE: this assumption will change in the future #3629). + pub(crate) fn set_sync_fork_request(&self, peers: Vec, hash: B::Hash, number: NumberFor) { + self.service.set_sync_fork_request(peers, hash, number) + } } fn incoming_global>( @@ -626,7 +681,7 @@ impl> Clone for NetworkBridge { } } -fn localized_payload(round: RoundNumber, set_id: SetIdNumber, message: &E) -> Vec { +pub(crate) fn localized_payload(round: RoundNumber, set_id: SetIdNumber, message: &E) -> Vec { (message, round, set_id).encode() } @@ -707,7 +762,7 @@ impl> Sink for OutgoingMessages id: local_id.clone(), }; - let message = GossipMessage::VoteOrPrecommit(VoteOrPrecommitMessage:: { + let message = GossipMessage::Vote(VoteMessage:: { message: signed.clone(), round: Round(self.round), set_id: SetId(self.set_id), diff --git a/core/finality-grandpa/src/communication/tests.rs b/core/finality-grandpa/src/communication/tests.rs index 6215e30b809660518f7945efe6f8de0fa4c25b1f..f918f47258d499c4deb8692e65946276ef3e9a20 100644 --- a/core/finality-grandpa/src/communication/tests.rs +++ b/core/finality-grandpa/src/communication/tests.rs @@ -25,6 +25,7 @@ use tokio::runtime::current_thread; use std::sync::Arc; use keyring::Ed25519Keyring; use codec::Encode; +use sr_primitives::traits::NumberFor; use crate::environment::SharedVoterSetState; use super::gossip::{self, GossipValidator}; @@ -91,6 +92,9 @@ impl super::Network for TestNetwork { fn announce(&self, block: Hash, _associated_data: Vec) { let _ = self.sender.unbounded_send(Event::Announce(block)); } + + /// Notify the sync service to try syncing the given chain. + fn set_sync_fork_request(&self, _peers: Vec, _hash: Hash, _number: NumberFor) {} } impl network_gossip::ValidatorContext for TestNetwork { @@ -135,6 +139,8 @@ fn config() -> crate::Config { justification_period: 256, keystore: None, name: None, + is_authority: true, + observer_enabled: true, } } @@ -182,7 +188,6 @@ fn make_test_network() -> ( config(), voter_set_state(), Exit, - true, ); ( @@ -217,7 +222,7 @@ fn good_commit_leads_to_relay() { let public = make_ids(&private[..]); let voter_set = Arc::new(public.iter().cloned().collect::>()); - let round = 0; + let round = 1; let set_id = 1; let commit = { @@ -332,7 +337,7 @@ fn bad_commit_leads_to_report() { let public = make_ids(&private[..]); let voter_set = Arc::new(public.iter().cloned().collect::>()); - let round = 0; + let round = 1; let set_id = 1; let commit = { diff --git a/core/finality-grandpa/src/environment.rs b/core/finality-grandpa/src/environment.rs index 70e45848be1dd10192cc477466cb81d5aa973cb7..149b00e80f92033ff73cafc90567113cd1d7e4a5 100644 --- a/core/finality-grandpa/src/environment.rs +++ b/core/finality-grandpa/src/environment.rs @@ -52,6 +52,7 @@ use crate::authorities::{AuthoritySet, SharedAuthoritySet}; use crate::consensus_changes::SharedConsensusChanges; use crate::justification::GrandpaJustification; use crate::until_imported::UntilVoteTargetImported; +use crate::voting_rule::VotingRule; use fg_primitives::{AuthorityId, AuthoritySignature, SetId, RoundNumber}; type HistoricalVotes = grandpa::HistoricalVotes< @@ -368,8 +369,8 @@ impl SharedVoterSetState { } /// The environment we run GRANDPA in. -pub(crate) struct Environment, RA, SC> { - pub(crate) inner: Arc>, +pub(crate) struct Environment, RA, SC, VR> { + pub(crate) client: Arc>, pub(crate) select_chain: SC, pub(crate) voters: Arc>, pub(crate) config: Config, @@ -378,9 +379,10 @@ pub(crate) struct Environment, RA, SC> { pub(crate) network: crate::communication::NetworkBridge, pub(crate) set_id: SetId, pub(crate) voter_set_state: SharedVoterSetState, + pub(crate) voting_rule: VR, } -impl, RA, SC> Environment { +impl, RA, SC, VR> Environment { /// Updates the voter set state using the given closure. The write lock is /// held during evaluation of the closure and the environment's voter set /// state is set to its result if successful. @@ -396,20 +398,22 @@ impl, RA, SC> Environment, B, E, N, RA, SC> +impl, B, E, N, RA, SC, VR> grandpa::Chain> -for Environment +for Environment where Block: 'static, B: Backend + 'static, - E: CallExecutor + 'static, + E: CallExecutor + Send + Sync + 'static, N: Network + 'static, N::In: 'static, SC: SelectChain + 'static, + VR: VotingRule>, + RA: Send + Sync, NumberFor: BlockNumberOps, { fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result, GrandpaError> { - ancestry(&self.inner, base, block) + ancestry(&self.client, base, block) } fn best_chain_containing(&self, block: Block::Hash) -> Option<(Block::Hash, NumberFor)> { @@ -429,8 +433,8 @@ where debug!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit); match self.select_chain.finality_target(block, None) { - Ok(Some(mut best_hash)) => { - let base_header = self.inner.header(&BlockId::Hash(block)).ok()? + Ok(Some(best_hash)) => { + let base_header = self.client.header(&BlockId::Hash(block)).ok()? .expect("Header known to exist after `best_containing` call; qed"); if let Some(limit) = limit { @@ -445,35 +449,51 @@ where } } - let mut best_header = self.inner.header(&BlockId::Hash(best_hash)).ok()? + let best_header = self.client.header(&BlockId::Hash(best_hash)).ok()? .expect("Header known to exist after `best_containing` call; qed"); - // we target a vote towards 3/4 of the unfinalized chain (rounding up) - let target = { - let two = NumberFor::::one() + One::one(); - let three = two + One::one(); - let four = three + One::one(); - - let diff = *best_header.number() - *base_header.number(); - let diff = ((diff * three) + two) / four; + // check if our vote is currently being limited due to a pending change + let limit = limit.filter(|limit| limit < best_header.number()); + let target; + + let target_header = if let Some(target_number) = limit { + let mut target_header = best_header.clone(); + + // walk backwards until we find the target block + loop { + if *target_header.number() < target_number { + unreachable!( + "we are traversing backwards from a known block; \ + blocks are stored contiguously; \ + qed" + ); + } + + if *target_header.number() == target_number { + break; + } + + target_header = self.client.header(&BlockId::Hash(*target_header.parent_hash())).ok()? + .expect("Header known to exist after `best_containing` call; qed"); + } - *base_header.number() + diff + target = target_header; + &target + } else { + // otherwise just use the given best as the target + &best_header }; - // unless our vote is currently being limited due to a pending change - let target = limit.map(|limit| limit.min(target)).unwrap_or(target); - - // walk backwards until we find the target block - loop { - if *best_header.number() < target { unreachable!(); } - if *best_header.number() == target { - return Some((best_hash, *best_header.number())); - } - - best_hash = *best_header.parent_hash(); - best_header = self.inner.header(&BlockId::Hash(best_hash)).ok()? - .expect("Header known to exist after `best_containing` call; qed"); - } + // restrict vote according to the given voting rule, if the + // voting rule doesn't restrict the vote then we keep the + // previous target. + // + // note that we pass the original `best_header`, i.e. before the + // authority set limit filter, which can be considered a + // mandatory/implicit voting rule. + self.voting_rule + .restrict_vote(&*self.client, &base_header, &best_header, target_header) + .or(Some((target_header.hash(), *target_header.number()))) }, Ok(None) => { debug!(target: "afg", "Encountered error finding best chain containing {:?}: couldn't find target block", block); @@ -519,9 +539,9 @@ pub(crate) fn ancestry, E, RA>( Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect()) } -impl, N, RA, SC> +impl, N, RA, SC, VR> voter::Environment> -for Environment +for Environment where Block: 'static, B: Backend + 'static, @@ -530,6 +550,7 @@ where N::In: 'static + Send, RA: 'static + Send + Sync, SC: SelectChain + 'static, + VR: VotingRule>, NumberFor: BlockNumberOps, { type Timer = Box + Send>; @@ -580,8 +601,9 @@ where // schedule incoming messages from the network to be held until // corresponding blocks are imported. let incoming = Box::new(UntilVoteTargetImported::new( - self.inner.import_notification_stream(), - self.inner.clone(), + self.client.import_notification_stream(), + self.network.clone(), + self.client.clone(), incoming, "round", ).map_err(Into::into)); @@ -629,7 +651,7 @@ where current_rounds, }; - crate::aux_schema::write_voter_set_state(&*self.inner, &set_state)?; + crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?; Ok(Some(set_state)) })?; @@ -670,7 +692,7 @@ where current_rounds, }; - crate::aux_schema::write_voter_set_state(&*self.inner, &set_state)?; + crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?; Ok(Some(set_state)) })?; @@ -721,7 +743,7 @@ where current_rounds, }; - crate::aux_schema::write_voter_set_state(&*self.inner, &set_state)?; + crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?; Ok(Some(set_state)) })?; @@ -779,7 +801,7 @@ where current_rounds, }; - crate::aux_schema::write_voter_set_state(&*self.inner, &set_state)?; + crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?; Ok(Some(set_state)) })?; @@ -795,7 +817,7 @@ where commit: Commit, ) -> Result<(), Self::Error> { finalize_block( - &*self.inner, + &*self.client, &self.authority_set, &self.consensus_changes, Some(self.config.justification_period.into()), diff --git a/core/finality-grandpa/src/finality_proof.rs b/core/finality-grandpa/src/finality_proof.rs index bae6c8ebc09ead035997f3b176187f50c3570396..bd22a7bbac287ecc383ce58508852ccb069bb204 100644 --- a/core/finality-grandpa/src/finality_proof.rs +++ b/core/finality-grandpa/src/finality_proof.rs @@ -40,7 +40,7 @@ use log::{trace, warn}; use client::{ backend::Backend, blockchain::Backend as BlockchainBackend, CallExecutor, Client, error::{Error as ClientError, Result as ClientResult}, - light::fetcher::{FetchChecker, RemoteCallRequest}, ExecutionStrategy, + light::fetcher::{FetchChecker, RemoteCallRequest, StorageProof}, ExecutionStrategy, }; use codec::{Encode, Decode}; use grandpa::BlockNumberOps; @@ -48,7 +48,7 @@ use sr_primitives::{ Justification, generic::BlockId, traits::{NumberFor, Block as BlockT, Header as HeaderT, One}, }; -use primitives::{H256, Blake2Hasher, offchain::NeverOffchainExt}; +use primitives::{H256, Blake2Hasher}; use substrate_telemetry::{telemetry, CONSENSUS_INFO}; use fg_primitives::AuthorityId; @@ -62,7 +62,7 @@ pub trait AuthoritySetForFinalityProver: Send + Sync { /// Call GrandpaApi::grandpa_authorities at given block. fn authorities(&self, block: &BlockId) -> ClientResult>; /// Prove call of GrandpaApi::grandpa_authorities at given block. - fn prove_authorities(&self, block: &BlockId) -> ClientResult>>; + fn prove_authorities(&self, block: &BlockId) -> ClientResult; } /// Client-based implementation of AuthoritySetForFinalityProver. @@ -78,14 +78,14 @@ impl, RA> AuthoritySetForFinalityProver fo "GrandpaApi_grandpa_authorities", &[], ExecutionStrategy::NativeElseWasm, - NeverOffchainExt::new(), + None, ).and_then(|call_result| Decode::decode(&mut &call_result[..]) .map_err(|err| ClientError::CallResultDecode( "failed to decode GRANDPA authorities set proof".into(), err ))) } - fn prove_authorities(&self, block: &BlockId) -> ClientResult>> { + fn prove_authorities(&self, block: &BlockId) -> ClientResult { self.execution_proof(block, "GrandpaApi_grandpa_authorities",&[]).map(|(_, proof)| proof) } } @@ -97,7 +97,7 @@ pub trait AuthoritySetForFinalityChecker: Send + Sync { &self, hash: Block::Hash, header: Block::Header, - proof: Vec>, + proof: StorageProof, ) -> ClientResult>; } @@ -107,7 +107,7 @@ impl AuthoritySetForFinalityChecker for Arc>, + proof: StorageProof, ) -> ClientResult> { let request = RemoteCallRequest { block: hash, @@ -207,7 +207,7 @@ struct FinalityProofFragment { /// The set of headers in the range (U; F] that we believe are unknown to the caller. Ordered. pub unknown_headers: Vec
, /// Optional proof of execution of GRANDPA::authorities(). - pub authorities_proof: Option>>, + pub authorities_proof: Option, } /// Proof of finality is the ordered set of finality fragments, where: @@ -582,13 +582,13 @@ pub(crate) mod tests { impl AuthoritySetForFinalityProver for (GetAuthorities, ProveAuthorities) where GetAuthorities: Send + Sync + Fn(BlockId) -> ClientResult>, - ProveAuthorities: Send + Sync + Fn(BlockId) -> ClientResult>>, + ProveAuthorities: Send + Sync + Fn(BlockId) -> ClientResult, { fn authorities(&self, block: &BlockId) -> ClientResult> { self.0(*block) } - fn prove_authorities(&self, block: &BlockId) -> ClientResult>> { + fn prove_authorities(&self, block: &BlockId) -> ClientResult { self.1(*block) } } @@ -597,13 +597,13 @@ pub(crate) mod tests { impl AuthoritySetForFinalityChecker for ClosureAuthoritySetForFinalityChecker where - Closure: Send + Sync + Fn(H256, Header, Vec>) -> ClientResult>, + Closure: Send + Sync + Fn(H256, Header, StorageProof) -> ClientResult>, { fn check_authorities_proof( &self, hash: H256, header: Header, - proof: Vec>, + proof: StorageProof, ) -> ClientResult> { self.0(hash, header, proof) } @@ -824,8 +824,8 @@ pub(crate) mod tests { _ => unreachable!("no other authorities should be fetched: {:?}", block_id), }, |block_id| match block_id { - BlockId::Number(4) => Ok(vec![vec![40]]), - BlockId::Number(6) => Ok(vec![vec![60]]), + BlockId::Number(4) => Ok(StorageProof::new(vec![vec![40]])), + BlockId::Number(6) => Ok(StorageProof::new(vec![vec![60]])), _ => unreachable!("no other authorities should be proved: {:?}", block_id), }, ), @@ -841,14 +841,14 @@ pub(crate) mod tests { block: header(5).hash(), justification: just5, unknown_headers: Vec::new(), - authorities_proof: Some(vec![vec![40]]), + authorities_proof: Some(StorageProof::new(vec![vec![40]])), }, // last fragment provides justification for #7 && unknown#7 FinalityProofFragment { block: header(7).hash(), justification: just7, unknown_headers: vec![header(7)], - authorities_proof: Some(vec![vec![60]]), + authorities_proof: Some(StorageProof::new(vec![vec![60]])), }, ]); } @@ -895,7 +895,7 @@ pub(crate) mod tests { block: header(4).hash(), justification: TestJustification(true, vec![7]).encode(), unknown_headers: vec![header(4)], - authorities_proof: Some(vec![vec![42]]), + authorities_proof: Some(StorageProof::new(vec![vec![42]])), }, FinalityProofFragment { block: header(5).hash(), justification: TestJustification(true, vec![8]).encode(), @@ -942,7 +942,7 @@ pub(crate) mod tests { block: header(2).hash(), justification: TestJustification(true, vec![7]).encode(), unknown_headers: Vec::new(), - authorities_proof: Some(vec![vec![42]]), + authorities_proof: Some(StorageProof::new(vec![vec![42]])), }, FinalityProofFragment { block: header(4).hash(), justification: TestJustification(true, vec![8]).encode(), diff --git a/core/finality-grandpa/src/import.rs b/core/finality-grandpa/src/import.rs index 758f6f18dbb0100b07c1ee4be85e9e1dd0e8ec5f..8fbe0791e8c058132e7c091f623b8162dadfc47e 100644 --- a/core/finality-grandpa/src/import.rs +++ b/core/finality-grandpa/src/import.rs @@ -465,17 +465,15 @@ impl, RA, SC> BlockImport _ => {}, } - if !needs_justification && !enacts_consensus_change { - return Ok(ImportResult::Imported(imported_aux)); - } - match justification { Some(justification) => { self.import_justification(hash, number, justification, needs_justification).unwrap_or_else(|err| { - debug!(target: "finality", "Imported block #{} that enacts authority set change with \ - invalid justification: {:?}, requesting justification from peers.", number, err); - imported_aux.bad_justification = true; - imported_aux.needs_justification = true; + if needs_justification || enacts_consensus_change { + debug!(target: "finality", "Imported block #{} that enacts authority set change with \ + invalid justification: {:?}, requesting justification from peers.", number, err); + imported_aux.bad_justification = true; + imported_aux.needs_justification = true; + } }); }, None => { diff --git a/core/finality-grandpa/src/justification.rs b/core/finality-grandpa/src/justification.rs index b4de8ff058684dbba3f452b7c65096143c685bb0..f5965df3e1228b38eea7b44d50929727872df688 100644 --- a/core/finality-grandpa/src/justification.rs +++ b/core/finality-grandpa/src/justification.rs @@ -39,7 +39,7 @@ use crate::communication; /// This is meant to be stored in the db and passed around the network to other /// nodes, and are used by syncing nodes to prove authority set handoffs. #[derive(Encode, Decode)] -pub(crate) struct GrandpaJustification { +pub struct GrandpaJustification { round: u64, pub(crate) commit: Commit, votes_ancestries: Vec, diff --git a/core/finality-grandpa/src/lib.rs b/core/finality-grandpa/src/lib.rs index e4b71acbec463243a4adfd56f075efc094a539db..0decea58117b0d9d6a90649f54b15f9e3f8f0ad6 100644 --- a/core/finality-grandpa/src/lib.rs +++ b/core/finality-grandpa/src/lib.rs @@ -22,11 +22,11 @@ //! //! # Usage //! -//! First, create a block-import wrapper with the `block_import` function. -//! The GRANDPA worker needs to be linked together with this block import object, -//! so a `LinkHalf` is returned as well. All blocks imported (from network or consensus or otherwise) -//! must pass through this wrapper, otherwise consensus is likely to break in -//! unexpected ways. +//! First, create a block-import wrapper with the `block_import` function. The +//! GRANDPA worker needs to be linked together with this block import object, so +//! a `LinkHalf` is returned as well. All blocks imported (from network or +//! consensus or otherwise) must pass through this wrapper, otherwise consensus +//! is likely to break in unexpected ways. //! //! Next, use the `LinkHalf` and a local configuration to `run_grandpa_voter`. //! This requires a `Network` implementation. The returned future should be @@ -92,11 +92,16 @@ mod justification; mod light_import; mod observer; mod until_imported; +mod voting_rule; pub use communication::Network; pub use finality_proof::FinalityProofProvider; +pub use justification::GrandpaJustification; pub use light_import::light_block_import; pub use observer::run_grandpa_observer; +pub use voting_rule::{ + BeforeBestBlock, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder +}; use aux_schema::PersistentData; use environment::{Environment, VoterSetState}; @@ -196,6 +201,13 @@ pub struct Config { /// at least every justification_period blocks. There are some other events which might cause /// justification generation. pub justification_period: u32, + /// Whether the GRANDPA observer protocol is live on the network and thereby + /// a full-node not running as a validator is running the GRANDPA observer + /// protocol (we will only issue catch-up requests to authorities when the + /// observer protocol is enabled). + pub observer_enabled: bool, + /// Whether the node is running as an authority (i.e. running the full GRANDPA protocol). + pub is_authority: bool, /// Some local identifier of the voter. pub name: Option, /// The keystore that manages the keys of this node. @@ -238,7 +250,7 @@ impl From for Error { } /// Something which can determine if a block is known. -pub trait BlockStatus { +pub(crate) trait BlockStatus { /// Return `Ok(Some(number))` or `Ok(None)` depending on whether the block /// is definitely known and has been imported. /// If an unexpected error occurs, return that. @@ -257,6 +269,26 @@ impl, RA> BlockStatus for Arc { + /// Notifies the sync service to try and sync the given block from the given + /// peers. + /// + /// If the given vector of peers is empty then the underlying implementation + /// should make a best effort to fetch the block from any peers it is + /// connected to (NOTE: this assumption will change in the future #3629). + fn set_sync_fork_request(&self, peers: Vec, hash: Block::Hash, number: NumberFor); +} + +impl BlockSyncRequester for NetworkBridge where + Block: BlockT, + N: communication::Network, +{ + fn set_sync_fork_request(&self, peers: Vec, hash: Block::Hash, number: NumberFor) { + NetworkBridge::set_sync_fork_request(self, peers, hash, number) + } +} + /// A new authority set along with the canonical block it changed at. #[derive(Debug)] pub(crate) struct NewAuthoritySet { @@ -425,6 +457,7 @@ fn global_communication, B, E, N, RA>( // block commit and catch up messages until relevant blocks are imported. let global_in = UntilGlobalMessageBlocksImported::new( client.import_notification_stream(), + network.clone(), client.clone(), global_in, "global", @@ -466,7 +499,7 @@ fn register_finality_tracker_inherent_data_provider, N, RA, SC, X> { +pub struct GrandpaParams, N, RA, SC, VR, X> { /// Configuration for the GRANDPA service. pub config: Config, /// A link to the block import worker. @@ -479,12 +512,14 @@ pub struct GrandpaParams, N, RA, SC, X> { pub on_exit: X, /// If supplied, can be used to hook on telemetry connection established events. pub telemetry_on_connect: Option>, + /// A voting rule used to potentially restrict target votes. + pub voting_rule: VR, } /// 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, N, RA, SC, X>( - grandpa_params: GrandpaParams, +pub fn run_grandpa_voter, N, RA, SC, VR, X>( + grandpa_params: GrandpaParams, ) -> client::error::Result + Send + 'static> where Block::Hash: Ord, B: Backend + 'static, @@ -492,6 +527,7 @@ pub fn run_grandpa_voter, N, RA, SC, X>( N: Network + Send + Sync + 'static, N::In: Send + 'static, SC: SelectChain + 'static, + VR: VotingRule> + Clone + 'static, NumberFor: BlockNumberOps, DigestFor: Encode, RA: Send + Sync + 'static, @@ -504,6 +540,7 @@ pub fn run_grandpa_voter, N, RA, SC, X>( inherent_data_providers, on_exit, telemetry_on_connect, + voting_rule, } = grandpa_params; let LinkHalf { @@ -518,7 +555,6 @@ pub fn run_grandpa_voter, N, RA, SC, X>( config.clone(), persistent_data.set_state.clone(), on_exit.clone(), - true, ); register_finality_tracker_inherent_data_provider(client.clone(), &inherent_data_providers)?; @@ -556,8 +592,9 @@ pub fn run_grandpa_voter, N, RA, SC, X>( config, network, select_chain, + voting_rule, persistent_data, - voter_commands_rx + voter_commands_rx, ); let voter_work = voter_work @@ -578,13 +615,13 @@ pub fn run_grandpa_voter, N, RA, SC, X>( /// Future that powers the voter. #[must_use] -struct VoterWork, RA, SC> { +struct VoterWork, RA, SC, VR> { voter: Box>> + Send>, - env: Arc>, + env: Arc>, voter_commands_rx: mpsc::UnboundedReceiver>>, } -impl VoterWork +impl VoterWork where Block: BlockT, N: Network + Sync, @@ -594,20 +631,23 @@ where E: CallExecutor + Send + Sync + 'static, B: Backend + 'static, SC: SelectChain + 'static, + VR: VotingRule> + Clone + 'static, { fn new( client: Arc>, config: Config, network: NetworkBridge, select_chain: SC, + voting_rule: VR, persistent_data: PersistentData, voter_commands_rx: mpsc::UnboundedReceiver>>, ) -> Self { let voters = persistent_data.authority_set.current_authorities(); let env = Arc::new(Environment { - inner: client, + client, select_chain, + voting_rule, voters: Arc::new(voters), config, network, @@ -644,7 +684,7 @@ where "authority_id" => authority_id.to_string(), ); - let chain_info = self.env.inner.info(); + let chain_info = self.env.client.info(); telemetry!(CONSENSUS_INFO; "afg.authority_set"; "number" => ?chain_info.chain.finalized_number, "hash" => ?chain_info.chain.finalized_hash, @@ -668,7 +708,7 @@ where let global_comms = global_communication( self.env.set_id, &self.env.voters, - &self.env.inner, + &self.env.client, &self.env.network, &self.env.config.keystore, ); @@ -716,7 +756,7 @@ where (new.canon_hash, new.canon_number), ); - aux_schema::write_voter_set_state(&*self.env.inner, &set_state)?; + aux_schema::write_voter_set_state(&*self.env.client, &set_state)?; Ok(Some(set_state)) })?; @@ -725,12 +765,13 @@ where set_id: new.set_id, voter_set_state: self.env.voter_set_state.clone(), // Fields below are simply transferred and not updated. - inner: self.env.inner.clone(), + client: self.env.client.clone(), select_chain: self.env.select_chain.clone(), config: self.env.config.clone(), authority_set: self.env.authority_set.clone(), consensus_changes: self.env.consensus_changes.clone(), network: self.env.network.clone(), + voting_rule: self.env.voting_rule.clone(), }); self.rebuild_voter(); @@ -744,7 +785,7 @@ where let completed_rounds = voter_set_state.completed_rounds(); let set_state = VoterSetState::Paused { completed_rounds }; - aux_schema::write_voter_set_state(&*self.env.inner, &set_state)?; + aux_schema::write_voter_set_state(&*self.env.client, &set_state)?; Ok(Some(set_state)) })?; @@ -755,7 +796,7 @@ where } } -impl Future for VoterWork +impl Future for VoterWork where Block: BlockT, N: Network + Sync, @@ -765,6 +806,7 @@ where E: CallExecutor + Send + Sync + 'static, B: Backend + 'static, SC: SelectChain + 'static, + VR: VotingRule> + Clone + 'static, { type Item = (); type Error = Error; @@ -808,9 +850,9 @@ where } } -#[deprecated(since = "1.1", note = "Please switch to run_grandpa_voter.")] -pub fn run_grandpa, N, RA, SC, X>( - grandpa_params: GrandpaParams, +#[deprecated(since = "1.1.0", note = "Please switch to run_grandpa_voter.")] +pub fn run_grandpa, N, RA, SC, VR, X>( + grandpa_params: GrandpaParams, ) -> ::client::error::Result + Send + 'static> where Block::Hash: Ord, B: Backend + 'static, @@ -821,6 +863,7 @@ pub fn run_grandpa, N, RA, SC, X>( NumberFor: BlockNumberOps, DigestFor: Encode, RA: Send + Sync + 'static, + VR: VotingRule> + Clone + 'static, X: Future + Clone + Send + 'static, { run_grandpa_voter(grandpa_params) diff --git a/core/finality-grandpa/src/observer.rs b/core/finality-grandpa/src/observer.rs index 39eeafcb1b141ebedc0f40087b05e5bb197ad51b..e4d90ddc22e77b658a1cca8478bd509bbe788c52 100644 --- a/core/finality-grandpa/src/observer.rs +++ b/core/finality-grandpa/src/observer.rs @@ -176,7 +176,6 @@ pub fn run_grandpa_observer, N, RA, SC>( config.clone(), persistent_data.set_state.clone(), on_exit.clone(), - false, ); let observer_work = ObserverWork::new( diff --git a/core/finality-grandpa/src/tests.rs b/core/finality-grandpa/src/tests.rs index 1957ab5c4fbb35abcb0b6232153dbd420ecc32c3..2339379a609d480de4c76b4205b474bd774907ef 100644 --- a/core/finality-grandpa/src/tests.rs +++ b/core/finality-grandpa/src/tests.rs @@ -27,7 +27,7 @@ use tokio::runtime::current_thread; use keyring::Ed25519Keyring; use client::{ error::Result, - runtime_api::{Core, RuntimeVersion, ApiExt}, + runtime_api::{Core, RuntimeVersion, ApiExt, StorageProof}, LongestChain, }; use test_client::{self, runtime::BlockNumber}; @@ -40,6 +40,7 @@ use sr_primitives::traits::{ApiRef, ProvideRuntimeApi, Header as HeaderT}; use sr_primitives::generic::{BlockId, DigestItem}; use primitives::{NativeOrEncoded, ExecutionContext, crypto::Public}; use fg_primitives::{GRANDPA_ENGINE_ID, AuthorityId}; +use state_machine::{backend::InMemory, prove_read, read_proof_check}; use authorities::AuthoritySet; use finality_proof::{FinalityProofProvider, AuthoritySetForFinalityProver, AuthoritySetForFinalityChecker}; @@ -258,7 +259,7 @@ impl ApiExt for RuntimeApi { unimplemented!("Not required for testing!") } - fn extract_proof(&mut self) -> Option>> { + fn extract_proof(&mut self) -> Option { unimplemented!("Not required for testing!") } } @@ -285,8 +286,14 @@ impl AuthoritySetForFinalityProver for TestApi { }) } - fn prove_authorities(&self, block: &BlockId) -> Result>> { - self.authorities(block).map(|auth| vec![auth.encode()]) + fn prove_authorities(&self, block: &BlockId) -> Result { + let authorities = self.authorities(block)?; + let backend = >::from(vec![ + (None, b"authorities".to_vec(), Some(authorities.encode())) + ]); + let proof = prove_read(backend, vec![b"authorities"]) + .expect("failure proving read from in-memory storage backend"); + Ok(proof) } } @@ -294,11 +301,20 @@ impl AuthoritySetForFinalityChecker for TestApi { fn check_authorities_proof( &self, _hash: ::Hash, - _header: ::Header, - proof: Vec>, + header: ::Header, + proof: StorageProof, ) -> Result> { - Decode::decode(&mut &proof[0][..]) - .map_err(|_| unreachable!("incorrect value is passed as GRANDPA authorities proof")) + let results = read_proof_check::( + *header.state_root(), proof, vec![b"authorities"] + ) + .expect("failure checking read proof for authorities"); + let encoded = results.get(&b"authorities"[..]) + .expect("returned map must contain all proof keys") + .as_ref() + .expect("authorities in proof is None"); + let authorities = Decode::decode(&mut &encoded[..]) + .expect("failure decoding authorities read from proof"); + Ok(authorities) } } @@ -379,12 +395,15 @@ fn run_to_completion_with( justification_period: 32, keystore: Some(keystore), name: Some(format!("peer#{}", peer_id)), + is_authority: true, + observer_enabled: true, }, link: link, network: net_service, inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, + voting_rule: (), }; let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network"); @@ -510,12 +529,15 @@ fn finalize_3_voters_1_full_observer() { justification_period: 32, keystore, name: Some(format!("peer#{}", peer_id)), + is_authority: true, + observer_enabled: true, }, link: link, network: net_service, inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, + voting_rule: (), }; let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network"); @@ -670,12 +692,15 @@ fn transition_3_voters_twice_1_full_observer() { justification_period: 32, keystore: Some(keystore), name: Some(format!("peer#{}", peer_id)), + is_authority: true, + observer_enabled: true, }, link: link, network: net_service, inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, + voting_rule: (), }; let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network"); @@ -861,30 +886,6 @@ fn finalizes_multiple_pending_changes_in_order() { run_to_completion(&mut runtime, 30, net.clone(), all_peers); } -#[test] -fn doesnt_vote_on_the_tip_of_the_chain() { - let mut runtime = current_thread::Runtime::new().unwrap(); - let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; - let voters = make_ids(peers_a); - let api = TestApi::new(voters); - let mut net = GrandpaTestNet::new(api, 3); - - // add 100 blocks - net.peer(0).push_blocks(100, false); - net.block_until_sync(&mut runtime); - - for i in 0..3 { - assert_eq!(net.peer(i).client().info().chain.best_number, 100, - "Peer #{} failed to sync", i); - } - - let net = Arc::new(Mutex::new(net)); - let highest = run_to_completion(&mut runtime, 75, net.clone(), peers_a); - - // the highest block to be finalized will be 3/4 deep in the unfinalized chain - assert_eq!(highest, 75); -} - #[test] fn force_change_to_new_set() { let _ = env_logger::try_init(); @@ -1116,12 +1117,15 @@ fn voter_persists_its_votes() { justification_period: 32, keystore: Some(self.keystore.clone()), name: Some(format!("peer#{}", 0)), + is_authority: true, + observer_enabled: true, }, link, network: self.net.lock().peers[0].network_service().clone(), inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, + voting_rule: VotingRulesBuilder::default().build(), }; let voter = run_grandpa_voter(grandpa_params) @@ -1170,6 +1174,8 @@ fn voter_persists_its_votes() { justification_period: 32, keystore: Some(keystore), name: Some(format!("peer#{}", 1)), + is_authority: true, + observer_enabled: true, }; let set_state = { @@ -1184,7 +1190,6 @@ fn voter_persists_its_votes() { config.clone(), set_state, Exit, - true, ); runtime.block_on(routing_work).unwrap(); @@ -1319,6 +1324,8 @@ fn finalize_3_voters_1_light_observer() { justification_period: 32, keystore: None, name: Some("observer".to_string()), + is_authority: false, + observer_enabled: true, }, link, net.lock().peers[3].network_service().clone(), @@ -1446,12 +1453,15 @@ fn voter_catches_up_to_latest_round_when_behind() { justification_period: 32, keystore, name: Some(format!("peer#{}", peer_id)), + is_authority: true, + observer_enabled: true, }, link, network: net.lock().peer(peer_id).network_service().clone(), inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, telemetry_on_connect: None, + voting_rule: (), }; Box::new(run_grandpa_voter(grandpa_params).expect("all in order with client and network")) @@ -1533,3 +1543,198 @@ 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(); } + +#[test] +fn grandpa_environment_respects_voting_rules() { + use grandpa::Chain; + use network::test::TestClient; + + let peers = &[Ed25519Keyring::Alice]; + let voters = make_ids(peers); + + let mut net = GrandpaTestNet::new(TestApi::new(voters), 1); + let peer = net.peer(0); + let network_service = peer.network_service().clone(); + let link = peer.data.lock().take().unwrap(); + + // create a voter environment with a given voting rule + let environment = |voting_rule: Box>| { + let PersistentData { + ref authority_set, + ref consensus_changes, + ref set_state, + .. + } = link.persistent_data; + + let config = Config { + gossip_duration: TEST_GOSSIP_DURATION, + justification_period: 32, + keystore: None, + name: None, + is_authority: true, + observer_enabled: true, + }; + + let (network, _) = NetworkBridge::new( + network_service.clone(), + config.clone(), + set_state.clone(), + Exit, + ); + + Environment { + authority_set: authority_set.clone(), + config: config.clone(), + consensus_changes: consensus_changes.clone(), + client: link.client.clone(), + select_chain: link.select_chain.clone(), + set_id: authority_set.set_id(), + voter_set_state: set_state.clone(), + voters: Arc::new(authority_set.current_authorities()), + network, + voting_rule, + } + }; + + // add 20 blocks + peer.push_blocks(20, false); + + // create an environment with no voting rule restrictions + let unrestricted_env = environment(Box::new(())); + + // another with 3/4 unfinalized chain voting rule restriction + let three_quarters_env = environment(Box::new( + voting_rule::ThreeQuartersOfTheUnfinalizedChain + )); + + // and another restricted with the default voting rules: i.e. 3/4 rule and + // always below best block + let default_env = environment(Box::new( + VotingRulesBuilder::default().build() + )); + + // the unrestricted environment should just return the best block + assert_eq!( + unrestricted_env.best_chain_containing( + peer.client().info().chain.finalized_hash + ).unwrap().1, + 20, + ); + + // both the other environments should return block 15, which is 3/4 of the + // way in the unfinalized chain + assert_eq!( + three_quarters_env.best_chain_containing( + peer.client().info().chain.finalized_hash + ).unwrap().1, + 15, + ); + + assert_eq!( + default_env.best_chain_containing( + peer.client().info().chain.finalized_hash + ).unwrap().1, + 15, + ); + + // we finalize block 19 with block 20 being the best block + peer.client().finalize_block(BlockId::Number(19), None, false).unwrap(); + + // the 3/4 environment should propose block 20 for voting + assert_eq!( + three_quarters_env.best_chain_containing( + peer.client().info().chain.finalized_hash + ).unwrap().1, + 20, + ); + + // while the default environment will always still make sure we don't vote + // on the best block + assert_eq!( + default_env.best_chain_containing( + peer.client().info().chain.finalized_hash + ).unwrap().1, + 19, + ); +} + +#[test] +fn imports_justification_for_regular_blocks_on_import() { + // NOTE: this is a regression test since initially we would only import + // justifications for authority change blocks, and would discard any + // existing justification otherwise. + let peers = &[Ed25519Keyring::Alice]; + let voters = make_ids(peers); + let api = TestApi::new(voters); + let mut net = GrandpaTestNet::new(api.clone(), 1); + + let client = net.peer(0).client().clone(); + let (mut block_import, ..) = net.make_block_import(client.clone()); + + let full_client = client.as_full().expect("only full clients are used in test"); + let builder = full_client.new_block_at(&BlockId::Number(0), Default::default()).unwrap(); + let block = builder.bake().unwrap(); + + let block_hash = block.hash(); + + // create a valid justification, with one precommit targeting the block + let justification = { + let round = 1; + let set_id = 0; + + let precommit = grandpa::Precommit { + target_hash: block_hash, + target_number: *block.header.number(), + }; + + let msg = grandpa::Message::Precommit(precommit.clone()); + let encoded = communication::localized_payload(round, set_id, &msg); + let signature = peers[0].sign(&encoded[..]).into(); + + let precommit = grandpa::SignedPrecommit { + precommit, + signature, + id: peers[0].public().into(), + }; + + let commit = grandpa::Commit { + target_hash: block_hash, + target_number: *block.header.number(), + precommits: vec![precommit], + }; + + GrandpaJustification::from_commit( + &full_client, + round, + commit, + ).unwrap() + }; + + // 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), + finalized: false, + auxiliary: Vec::new(), + fork_choice: ForkChoiceStrategy::LongestChain, + }; + + assert_eq!( + block_import.import_block(block, HashMap::new()).unwrap(), + ImportResult::Imported(ImportedAux { + needs_justification: false, + clear_justification_requests: false, + bad_justification: false, + is_new_best: true, + ..Default::default() + }), + ); + + // the justification should be imported and available from the client + assert!( + client.justification(&BlockId::Hash(block_hash)).unwrap().is_some(), + ); +} diff --git a/core/finality-grandpa/src/until_imported.rs b/core/finality-grandpa/src/until_imported.rs index b8568c1f8510b0f335e494a160ac3a34b9962fe2..5fca476a82b6a45ae47a5f63d143f8ce9ed0040d 100644 --- a/core/finality-grandpa/src/until_imported.rs +++ b/core/finality-grandpa/src/until_imported.rs @@ -20,7 +20,13 @@ //! //! This is used for votes and commit messages currently. -use super::{BlockStatus, CommunicationIn, Error, SignedMessage}; +use super::{ + BlockStatus as BlockStatusT, + BlockSyncRequester as BlockSyncRequesterT, + CommunicationIn, + Error, + SignedMessage, +}; use log::{debug, warn}; use client::{BlockImportNotification, ImportNotifications}; @@ -54,8 +60,8 @@ pub(crate) trait BlockUntilImported: Sized { wait: Wait, ready: Ready, ) -> Result<(), Error> where - S: BlockStatus, - Wait: FnMut(Block::Hash, Self), + S: BlockStatusT, + Wait: FnMut(Block::Hash, NumberFor, Self), Ready: FnMut(Self::Blocked); /// called when the wait has completed. The canonical number is passed through @@ -64,23 +70,31 @@ pub(crate) trait BlockUntilImported: Sized { } /// Buffering imported messages until blocks with given hashes are imported. -pub(crate) struct UntilImported> { +pub(crate) struct UntilImported> { import_notifications: Fuse, Error = ()> + Send>>, - status_check: Status, + block_sync_requester: BlockSyncRequester, + status_check: BlockStatus, inner: Fuse, ready: VecDeque, check_pending: Interval, - pending: HashMap)>, + /// 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. + pending: HashMap, Instant, Vec)>, identifier: &'static str, } -impl UntilImported - where Status: BlockStatus, M: BlockUntilImported +impl UntilImported where + Block: BlockT, + BlockStatus: BlockStatusT, + M: BlockUntilImported, + I: Stream, { /// Create a new `UntilImported` wrapper. pub(crate) fn new( import_notifications: ImportNotifications, - status_check: Status, + block_sync_requester: BlockSyncRequester, + status_check: BlockStatus, stream: I, identifier: &'static str, ) -> Self { @@ -98,6 +112,7 @@ impl UntilImported let stream = import_notifications.map::<_, fn(_) -> _>(|v| Ok::<_, ()>(v)).compat(); Box::new(stream) as Box + Send> }.fuse(), + block_sync_requester, status_check, inner: stream.fuse(), ready: VecDeque::new(), @@ -108,8 +123,10 @@ impl UntilImported } } -impl Stream for UntilImported where - Status: BlockStatus, +impl Stream for UntilImported where + Block: BlockT, + BStatus: BlockStatusT, + BSyncRequester: BlockSyncRequesterT, I: Stream, M: BlockUntilImported, { @@ -128,10 +145,10 @@ impl Stream for UntilImported M::schedule_wait( input, &self.status_check, - |target_hash, wait| pending + |target_hash, target_number, wait| pending .entry(target_hash) - .or_insert_with(|| (Instant::now(), Vec::new())) - .1 + .or_insert_with(|| (target_number, Instant::now(), Vec::new())) + .2 .push(wait), |ready_item| ready.push_back(ready_item), )?; @@ -146,7 +163,7 @@ impl Stream for UntilImported Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), Ok(Async::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)) = self.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)); @@ -165,19 +182,29 @@ impl Stream for UntilImported if update_interval { let mut known_keys = Vec::new(); - for (&block_hash, &mut (ref mut last_log, ref v)) in &mut self.pending { + 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)? { known_keys.push((block_hash, number)); } else { let next_log = *last_log + LOG_PENDING_INTERVAL; - if Instant::now() <= next_log { + if Instant::now() >= next_log { debug!( target: "afg", "Waiting to import block {} before {} {} messages can be imported. \ + Requesting network sync service to retrieve block from. \ Possible fork?", - self.identifier, block_hash, v.len(), + self.identifier, + ); + + // NOTE: when sending an empty vec of peers the + // underlying should make a best effort to sync the + // block from any peers it knows about. + self.block_sync_requester.set_sync_fork_request( + vec![], + block_hash, + block_number, ); *last_log = next_log; @@ -186,7 +213,7 @@ impl Stream for UntilImported } for (known_hash, canon_number) in known_keys { - if let Some((_, pending_messages)) = self.pending.remove(&known_hash) { + if let Some((_, _, pending_messages)) = self.pending.remove(&known_hash) { let ready_messages = pending_messages.into_iter() .filter_map(|m| m.wait_completed(canon_number)); @@ -220,14 +247,14 @@ fn warn_authority_wrong_target(hash: H, id: AuthorityId) impl BlockUntilImported for SignedMessage { type Blocked = Self; - fn schedule_wait( + fn schedule_wait( msg: Self::Blocked, - status_check: &S, + status_check: &BlockStatus, mut wait: Wait, mut ready: Ready, ) -> Result<(), Error> where - S: BlockStatus, - Wait: FnMut(Block::Hash, Self), + BlockStatus: BlockStatusT, + Wait: FnMut(Block::Hash, NumberFor, Self), Ready: FnMut(Self::Blocked), { let (&target_hash, target_number) = msg.target(); @@ -239,7 +266,7 @@ impl BlockUntilImported for SignedMessage { ready(msg); } } else { - wait(target_hash, msg) + wait(target_hash, target_number, msg) } Ok(()) @@ -259,7 +286,13 @@ impl BlockUntilImported for SignedMessage { /// Helper type definition for the stream which waits until vote targets for /// signed messages are imported. -pub(crate) type UntilVoteTargetImported = UntilImported>; +pub(crate) type UntilVoteTargetImported = UntilImported< + Block, + BlockStatus, + BlockSyncRequester, + I, + SignedMessage, +>; /// This blocks a global message import, i.e. a commit or catch up messages, /// until all blocks referenced in its votes are known. @@ -274,14 +307,14 @@ pub(crate) struct BlockGlobalMessage { impl BlockUntilImported for BlockGlobalMessage { type Blocked = CommunicationIn; - fn schedule_wait( + fn schedule_wait( input: Self::Blocked, - status_check: &S, + status_check: &BlockStatus, mut wait: Wait, mut ready: Ready, ) -> Result<(), Error> where - S: BlockStatus, - Wait: FnMut(Block::Hash, Self), + BlockStatus: BlockStatusT, + Wait: FnMut(Block::Hash, NumberFor, Self), Ready: FnMut(Self::Blocked), { use std::collections::hash_map::Entry; @@ -383,7 +416,7 @@ impl BlockUntilImported for BlockGlobalMessage { // if this is taking a long time. for (hash, is_known) in checked_hashes { if let KnownOrUnknown::Unknown(target_number) = is_known { - wait(hash, BlockGlobalMessage { + wait(hash, target_number, BlockGlobalMessage { inner: locked_global.clone(), target_number, }) @@ -425,9 +458,10 @@ impl BlockUntilImported for BlockGlobalMessage { /// A stream which gates off incoming global messages, i.e. commit and catch up /// messages, until all referenced block hashes have been imported. -pub(crate) type UntilGlobalMessageBlocksImported = UntilImported< +pub(crate) type UntilGlobalMessageBlocksImported = UntilImported< Block, - Status, + BlockStatus, + BlockSyncRequester, I, BlockGlobalMessage, >; @@ -485,12 +519,31 @@ mod tests { inner: Arc>>, } - impl BlockStatus for TestBlockStatus { + impl BlockStatusT for TestBlockStatus { fn block_number(&self, hash: Hash) -> Result, Error> { Ok(self.inner.lock().get(&hash).map(|x| x.clone())) } } + #[derive(Clone)] + struct TestBlockSyncRequester { + requests: Arc)>>>, + } + + impl Default for TestBlockSyncRequester { + fn default() -> Self { + TestBlockSyncRequester { + requests: Arc::new(Mutex::new(Vec::new())), + } + } + } + + impl BlockSyncRequesterT for TestBlockSyncRequester { + fn set_sync_fork_request(&self, _peers: Vec, hash: Hash, number: NumberFor) { + self.requests.lock().push((hash, number)); + } + } + fn make_header(number: u64) -> Header { Header::new( number, @@ -535,6 +588,7 @@ mod tests { let until_imported = UntilGlobalMessageBlocksImported::new( import_notifications, + TestBlockSyncRequester::default(), block_status, global_rx.map_err(|_| panic!("should never error")), "global", @@ -561,6 +615,7 @@ mod tests { let until_imported = UntilGlobalMessageBlocksImported::new( import_notifications, + TestBlockSyncRequester::default(), block_status, global_rx.map_err(|_| panic!("should never error")), "global", @@ -806,4 +861,80 @@ mod tests { unapply_catch_up(unknown_catch_up()), ); } + + #[test] + fn request_block_sync_for_needed_blocks() { + let (chain_state, import_notifications) = TestChainState::new(); + let block_status = chain_state.block_status(); + + let (global_tx, global_rx) = futures::sync::mpsc::unbounded(); + + let block_sync_requester = TestBlockSyncRequester::default(); + + let until_imported = UntilGlobalMessageBlocksImported::new( + import_notifications, + block_sync_requester.clone(), + block_status, + global_rx.map_err(|_| panic!("should never error")), + "global", + ); + + let h1 = make_header(5); + let h2 = make_header(6); + let h3 = make_header(7); + + // we create a commit message, with precommits for blocks 6 and 7 which + // we haven't imported. + let unknown_commit = CompactCommit:: { + target_hash: h1.hash(), + target_number: 5, + precommits: vec![ + Precommit { + target_hash: h2.hash(), + target_number: 6, + }, + Precommit { + target_hash: h3.hash(), + target_number: 7, + }, + ], + auth_data: Vec::new(), // not used + }; + + let unknown_commit = || voter::CommunicationIn::Commit( + 0, + unknown_commit.clone(), + voter::Callback::Blank, + ); + + // 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(|_| ())); + + // assert that we will make sync requests + 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(())); + } + + Ok(Async::NotReady) + }); + + // the `until_imported` stream doesn't request the blocks immediately, + // but it should request them after a small timeout + let timeout = Delay::new(Instant::now() + Duration::from_secs(60)); + let test = assert.select2(timeout).map(|res| match res { + Either::A(_) => {}, + Either::B(_) => panic!("timed out waiting for block sync request"), + }).map_err(|_| ()); + + runtime.block_on(test).unwrap(); + } } diff --git a/core/finality-grandpa/src/voting_rule.rs b/core/finality-grandpa/src/voting_rule.rs new file mode 100644 index 0000000000000000000000000000000000000000..355fa0cd2d08068d8b0de1197b0e01cd32e1a884 --- /dev/null +++ b/core/finality-grandpa/src/voting_rule.rs @@ -0,0 +1,270 @@ +// Copyright 2018-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 . + +//! Handling custom voting rules for GRANDPA. +//! +//! This exposes the `VotingRule` trait used to implement arbitrary voting +//! restrictions that are taken into account by the GRANDPA environment when +//! selecting a finality target to vote on. + +use std::sync::Arc; + +use client::blockchain::HeaderBackend; +use sr_primitives::generic::BlockId; +use sr_primitives::traits::{Block as BlockT, Header, NumberFor, One, Zero}; + +/// A trait for custom voting rules in GRANDPA. +pub trait VotingRule: Send + Sync where + Block: BlockT, + B: HeaderBackend, +{ + /// Restrict the given `current_target` vote, returning the block hash and + /// number of the block to vote on, and `None` in case the vote should not + /// be restricted. `base` is the block that we're basing our votes on in + /// order to pick our target (e.g. last round estimate), and `best_target` + /// is the initial best vote target before any vote rules were applied. When + /// applying multiple `VotingRule`s both `base` and `best_target` should + /// remain unchanged. + /// + /// The contract of this interface requires that when restricting a vote, the + /// returned value **must** be an ancestor of the given `current_target`, + /// this also means that a variant must be maintained throughout the + /// execution of voting rules wherein `current_target <= best_target`. + fn restrict_vote( + &self, + backend: &B, + base: &Block::Header, + best_target: &Block::Header, + current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)>; +} + +impl VotingRule for () where + Block: BlockT, + B: HeaderBackend, +{ + fn restrict_vote( + &self, + _backend: &B, + _base: &Block::Header, + _best_target: &Block::Header, + _current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)> { + None + } +} + +/// A custom voting rule that guarantees that our vote is always behind the best +/// block, in the best case exactly one block behind it. +#[derive(Clone)] +pub struct BeforeBestBlock; +impl VotingRule for BeforeBestBlock where + Block: BlockT, + B: HeaderBackend, +{ + fn restrict_vote( + &self, + _backend: &B, + _base: &Block::Header, + best_target: &Block::Header, + current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)> { + if current_target.number().is_zero() { + return None; + } + + if current_target.number() == best_target.number() { + return Some(( + current_target.parent_hash().clone(), + *current_target.number() - One::one(), + )); + } + + None + } +} + +/// A custom voting rule that limits votes towards 3/4 of the unfinalized chain, +/// using the given `base` and `best_target` to figure where the 3/4 target +/// should fall. +pub struct ThreeQuartersOfTheUnfinalizedChain; + +impl VotingRule for ThreeQuartersOfTheUnfinalizedChain where + Block: BlockT, + B: HeaderBackend, +{ + fn restrict_vote( + &self, + backend: &B, + base: &Block::Header, + best_target: &Block::Header, + current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)> { + // target a vote towards 3/4 of the unfinalized chain (rounding up) + let target_number = { + let two = NumberFor::::one() + One::one(); + let three = two + One::one(); + let four = three + One::one(); + + let diff = *best_target.number() - *base.number(); + let diff = ((diff * three) + two) / four; + + *base.number() + diff + }; + + // our current target is already lower than this rule would restrict + if target_number >= *current_target.number() { + return None; + } + + let mut target_header = current_target.clone(); + let mut target_hash = current_target.hash(); + + // walk backwards until we find the target block + loop { + if *target_header.number() < target_number { + unreachable!( + "we are traversing backwards from a known block; \ + blocks are stored contiguously; \ + qed" + ); + } + if *target_header.number() == target_number { + return Some((target_hash, target_number)); + } + + target_hash = *target_header.parent_hash(); + target_header = backend.header(BlockId::Hash(target_hash)).ok()? + .expect("Header known to exist due to the existence of one of its descendents; qed"); + } + } +} + +struct VotingRules { + rules: Arc>>>, +} + +impl Clone for VotingRules { + fn clone(&self) -> Self { + VotingRules { + rules: self.rules.clone(), + } + } +} + +impl VotingRule for VotingRules where + Block: BlockT, + B: HeaderBackend, +{ + fn restrict_vote( + &self, + backend: &B, + base: &Block::Header, + best_target: &Block::Header, + current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)> { + let restricted_target = self.rules.iter().fold( + current_target.clone(), + |current_target, rule| { + rule.restrict_vote( + backend, + base, + best_target, + ¤t_target, + ) + .and_then(|(hash, _)| backend.header(BlockId::Hash(hash)).ok()) + .and_then(std::convert::identity) + .unwrap_or(current_target) + }, + ); + + let restricted_hash = restricted_target.hash(); + + if restricted_hash != current_target.hash() { + Some((restricted_hash, *restricted_target.number())) + } else { + None + } + } +} + +/// A builder of a composite voting rule that applies a set of rules to +/// progressively restrict the vote. +pub struct VotingRulesBuilder { + rules: Vec>>, +} + +impl Default for VotingRulesBuilder where + Block: BlockT, + B: HeaderBackend, +{ + fn default() -> Self { + VotingRulesBuilder::new() + .add(BeforeBestBlock) + .add(ThreeQuartersOfTheUnfinalizedChain) + } +} + +impl VotingRulesBuilder where + Block: BlockT, + B: HeaderBackend, +{ + /// Return a new voting rule builder using the given backend. + pub fn new() -> Self { + VotingRulesBuilder { + rules: Vec::new(), + } + } + + /// Add a new voting rule to the builder. + pub fn add(mut self, rule: R) -> Self where + R: VotingRule + 'static, + { + self.rules.push(Box::new(rule)); + self + } + + /// Add all given voting rules to the builder. + pub fn add_all(mut self, rules: I) -> Self where + I: IntoIterator>>, + { + self.rules.extend(rules); + self + } + + /// Return a new `VotingRule` that applies all of the previously added + /// voting rules in-order. + pub fn build(self) -> impl VotingRule + Clone { + VotingRules { + rules: Arc::new(self.rules), + } + } +} + +impl VotingRule for Box> where + Block: BlockT, + B: HeaderBackend, +{ + fn restrict_vote( + &self, + backend: &B, + base: &Block::Header, + best_target: &Block::Header, + current_target: &Block::Header, + ) -> Option<(Block::Hash, NumberFor)> { + (**self).restrict_vote(backend, base, best_target, current_target) + } +} diff --git a/core/keyring/src/ed25519.rs b/core/keyring/src/ed25519.rs index 56bdb1ce8c0e60d3a43116631378b38592a8f762..c1a357fc0e427f16f58e39fd502ed7e2a56f8e33 100644 --- a/core/keyring/src/ed25519.rs +++ b/core/keyring/src/ed25519.rs @@ -20,6 +20,7 @@ use std::{collections::HashMap, ops::Deref}; use lazy_static::lazy_static; use primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256}; pub use primitives::ed25519; +use sr_primitives::AccountId32; /// Set of test accounts. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::Display, strum_macros::EnumIter)] @@ -39,6 +40,10 @@ impl Keyring { Self::iter().find(|&k| &Public::from(k) == who) } + pub fn from_account_id(who: &AccountId32) -> Option { + Self::iter().find(|&k| &k.to_account_id() == who) + } + pub fn from_raw_public(who: [u8; 32]) -> Option { Self::from_public(&Public::from_raw(who)) } @@ -59,6 +64,10 @@ impl Keyring { Public::from(self).to_raw_vec() } + pub fn to_account_id(self) -> AccountId32 { + self.to_raw_public().into() + } + pub fn sign(self, msg: &[u8]) -> Signature { Pair::from(self).sign(msg) } @@ -119,6 +128,12 @@ impl From for Public { } } +impl From for AccountId32 { + fn from(k: Keyring) -> Self { + k.to_account_id() + } +} + impl From for Pair { fn from(k: Keyring) -> Self { k.pair() diff --git a/core/keyring/src/sr25519.rs b/core/keyring/src/sr25519.rs index bb3aaa6b51db19569208bbad3128b269f5cb7707..b37b2bdf9b22a2f1e3f02c799f72eda5f447d719 100644 --- a/core/keyring/src/sr25519.rs +++ b/core/keyring/src/sr25519.rs @@ -21,6 +21,7 @@ use std::ops::Deref; use lazy_static::lazy_static; use primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256}; pub use primitives::sr25519; +use sr_primitives::AccountId32; /// Set of test accounts. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::Display, strum_macros::EnumIter)] @@ -40,6 +41,10 @@ impl Keyring { Self::iter().find(|&k| &Public::from(k) == who) } + pub fn from_account_id(who: &AccountId32) -> Option { + Self::iter().find(|&k| &k.to_account_id() == who) + } + pub fn from_raw_public(who: [u8; 32]) -> Option { Self::from_public(&Public::from_raw(who)) } @@ -60,6 +65,10 @@ impl Keyring { Public::from(self).to_raw_vec() } + pub fn to_account_id(self) -> AccountId32 { + self.to_raw_public().into() + } + pub fn sign(self, msg: &[u8]) -> Signature { Pair::from(self).sign(msg) } @@ -114,6 +123,12 @@ lazy_static! { }; } +impl From for AccountId32 { + fn from(k: Keyring) -> Self { + k.to_account_id() + } +} + impl From for Public { fn from(k: Keyring) -> Self { (*PUBLIC_KEYS).get(&k).unwrap().clone() diff --git a/core/keystore/Cargo.toml b/core/keystore/Cargo.toml index ff5696ab374b7130f05db0714991c739fb52be3e..cc491337cf83a0d861f1955b76c518b0514fa4be 100644 --- a/core/keystore/Cargo.toml +++ b/core/keystore/Cargo.toml @@ -10,7 +10,7 @@ primitives = { package = "substrate-primitives", path = "../primitives" } app-crypto = { package = "substrate-application-crypto", path = "../application-crypto" } hex = "0.3.2" rand = "0.7.2" -serde_json = "1.0.40" +serde_json = "1.0.41" subtle = "2.1.1" parking_lot = "0.9.0" diff --git a/core/network/Cargo.toml b/core/network/Cargo.toml index 453fbbeee350451549515975285c075dfb7f3e11..71e7d95bfa56ac63f96b6457800e277a83efa555 100644 --- a/core/network/Cargo.toml +++ b/core/network/Cargo.toml @@ -22,7 +22,7 @@ linked_hash_set = "0.1.3" lru-cache = "0.1.2" rustc-hex = "2.0.1" rand = "0.7.2" -libp2p = { version = "0.12.0", default-features = false, features = ["secp256k1", "libp2p-websocket"] } +libp2p = { version = "0.12.0", default-features = false, features = ["libp2p-websocket"] } fork-tree = { path = "../../core/utils/fork-tree" } consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common" } client = { package = "substrate-client", path = "../../core/client" } @@ -32,7 +32,7 @@ primitives = { package = "substrate-primitives", path = "../../core/primitives" codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } peerset = { package = "substrate-peerset", path = "../../core/peerset" } serde = { version = "1.0.101", features = ["derive"] } -serde_json = "1.0.40" +serde_json = "1.0.41" slog = { version = "2.5.2", features = ["nested-values"] } slog_derive = "0.1.1" smallvec = "0.6.10" @@ -48,7 +48,7 @@ zeroize = "0.10.1" babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives" } [dev-dependencies] -env_logger = "0.6.2" +env_logger = "0.7.0" keyring = { package = "substrate-keyring", path = "../../core/keyring" } quickcheck = "0.9.0" rand = "0.7.2" diff --git a/core/network/src/behaviour.rs b/core/network/src/behaviour.rs index 2471cbcaaf26aa79c1a9c727a0cb7ee2e75a5003..28830b326eaee87265ffaea3b066e244d65d8aac 100644 --- a/core/network/src/behaviour.rs +++ b/core/network/src/behaviour.rs @@ -26,7 +26,7 @@ use libp2p::core::{Multiaddr, PeerId, PublicKey}; use libp2p::kad::record; use libp2p::swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}; use libp2p::core::{nodes::Substream, muxing::StreamMuxerBox}; -use log::warn; +use log::{debug, warn}; use sr_primitives::traits::Block as BlockT; use std::iter; use void; @@ -133,7 +133,7 @@ impl, H: ExHashT> NetworkBehaviourEventPr warn!(target: "sub-libp2p", "Connected to a non-Substrate node: {:?}", info); } if info.listen_addrs.len() > 30 { - warn!(target: "sub-libp2p", "Node {:?} has reported more than 30 addresses; \ + debug!(target: "sub-libp2p", "Node {:?} has reported more than 30 addresses; \ it is identified by {:?} and {:?}", peer_id, info.protocol_version, info.agent_version ); diff --git a/core/network/src/chain.rs b/core/network/src/chain.rs index 1a1f649cae2dd62946678486a5131c357b89b0a9..2b32c07009fc7a732ff5818d5ca6364cb5ec9008 100644 --- a/core/network/src/chain.rs +++ b/core/network/src/chain.rs @@ -18,7 +18,7 @@ use client::{self, Client as SubstrateClient, ClientInfo, CallExecutor}; use client::error::Error; -use client::light::fetcher::ChangesProof; +use client::light::fetcher::{ChangesProof, StorageProof}; use consensus::{BlockImport, BlockStatus, Error as ConsensusError}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT}; use sr_primitives::generic::{BlockId}; @@ -46,10 +46,11 @@ pub trait Client: Send + Sync { fn justification(&self, id: &BlockId) -> Result, Error>; /// Get block header proof. - fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error>; + fn header_proof(&self, block_number: ::Number) + -> Result<(Block::Header, StorageProof), Error>; /// Get storage read execution proof. - fn read_proof(&self, block: &Block::Hash, keys: &[Vec]) -> Result>, Error>; + fn read_proof(&self, block: &Block::Hash, keys: &[Vec]) -> Result; /// Get child storage read execution proof. fn read_child_proof( @@ -57,10 +58,10 @@ pub trait Client: Send + Sync { block: &Block::Hash, storage_key: &[u8], keys: &[Vec], - ) -> Result>, Error>; + ) -> Result; /// Get method execution proof. - fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error>; + fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, StorageProof), Error>; /// Get key changes proof. fn key_changes_proof( @@ -83,6 +84,12 @@ pub trait FinalityProofProvider: Send + Sync { fn prove_finality(&self, for_block: Block::Hash, request: &[u8]) -> Result>, Error>; } +impl FinalityProofProvider for () { + fn prove_finality(&self, _for_block: Block::Hash, _request: &[u8]) -> Result>, Error> { + Ok(None) + } +} + impl Client for SubstrateClient where B: client::backend::Backend + Send + Sync + 'static, E: CallExecutor + Send + Sync + 'static, @@ -114,11 +121,13 @@ impl Client for SubstrateClient where (self as &SubstrateClient).justification(id) } - fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error> { + fn header_proof(&self, block_number: ::Number) + -> Result<(Block::Header, StorageProof), Error> + { (self as &SubstrateClient).header_proof(&BlockId::Number(block_number)) } - fn read_proof(&self, block: &Block::Hash, keys: &[Vec]) -> Result>, Error> { + fn read_proof(&self, block: &Block::Hash, keys: &[Vec]) -> Result { (self as &SubstrateClient).read_proof(&BlockId::Hash(block.clone()), keys) } @@ -127,12 +136,12 @@ impl Client for SubstrateClient where block: &Block::Hash, storage_key: &[u8], keys: &[Vec], - ) -> Result>, Error> { + ) -> Result { (self as &SubstrateClient) .read_child_proof(&BlockId::Hash(block.clone()), storage_key, keys) } - fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error> { + fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, StorageProof), Error> { (self as &SubstrateClient).execution_proof(&BlockId::Hash(block.clone()), method, data) } diff --git a/core/network/src/config.rs b/core/network/src/config.rs index 0582fcb300f1ea590811d143aea36f60b31b0586..be01b90c363636183f195f8dceb58b7505d7cd5d 100644 --- a/core/network/src/config.rs +++ b/core/network/src/config.rs @@ -28,12 +28,11 @@ use crate::service::{ExHashT, TransactionPool}; use bitflags::bitflags; use consensus::{block_validation::BlockAnnounceValidator, import_queue::ImportQueue}; use sr_primitives::traits::{Block as BlockT}; -use std::sync::Arc; -use libp2p::identity::{Keypair, secp256k1, ed25519}; +use libp2p::identity::{Keypair, ed25519}; use libp2p::wasm_ext; use libp2p::{PeerId, Multiaddr, multiaddr}; -use std::error::Error; -use std::{io::{self, Write}, iter, fmt, fs, net::Ipv4Addr, path::{Path, PathBuf}}; +use core::{fmt, iter}; +use std::{error::Error, fs, io::{self, Write}, net::Ipv4Addr, path::{Path, PathBuf}, sync::Arc}; use zeroize::Zeroize; /// Network initialization parameters. @@ -234,7 +233,7 @@ impl From for ParseErr { } /// Network service configuration. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct NetworkConfiguration { /// Directory path to store general network configuration. None means nothing will be saved. pub config_path: Option, @@ -317,7 +316,7 @@ impl NetworkConfiguration { } /// Configuration for the transport layer. -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum TransportConfig { /// Normal transport mode. Normal { @@ -362,17 +361,12 @@ impl NonReservedPeerMode { /// The configuration of a node's secret key, describing the type of key /// and how it is obtained. A node's identity keypair is the result of /// the evaluation of the node key configuration. -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum NodeKeyConfig { - /// A Secp256k1 secret key configuration. - Secp256k1(Secret), /// A Ed25519 secret key configuration. Ed25519(Secret) } -/// The options for obtaining a Secp256k1 secret key. -pub type Secp256k1Secret = Secret; - /// The options for obtaining a Ed25519 secret key. pub type Ed25519Secret = Secret; @@ -385,13 +379,22 @@ pub enum Secret { /// it is created with a newly generated secret key `K`. The format /// of the file is determined by `K`: /// - /// * `secp256k1::SecretKey`: An unencoded 32 bytes Secp256k1 secret key. /// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key. File(PathBuf), /// Always generate a new secret key `K`. New } +impl fmt::Debug for Secret { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Secret::Input(_) => f.debug_tuple("Secret::Input").finish(), + Secret::File(path) => f.debug_tuple("Secret::File").field(path).finish(), + Secret::New => f.debug_tuple("Secret::New").finish(), + } + } +} + impl NodeKeyConfig { /// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`: /// @@ -406,20 +409,6 @@ impl NodeKeyConfig { pub fn into_keypair(self) -> io::Result { use NodeKeyConfig::*; match self { - Secp256k1(Secret::New) => - Ok(Keypair::generate_secp256k1()), - - Secp256k1(Secret::Input(k)) => - Ok(Keypair::Secp256k1(k.into())), - - Secp256k1(Secret::File(f)) => - get_secret(f, - |mut b| secp256k1::SecretKey::from_bytes(&mut b), - secp256k1::SecretKey::generate, - |b| b.to_bytes().to_vec()) - .map(secp256k1::Keypair::from) - .map(Keypair::Secp256k1), - Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()), @@ -526,9 +515,9 @@ mod tests { #[test] fn test_secret_input() { - let sk = secp256k1::SecretKey::generate(); - let kp1 = NodeKeyConfig::Secp256k1(Secret::Input(sk.clone())).into_keypair().unwrap(); - let kp2 = NodeKeyConfig::Secp256k1(Secret::Input(sk)).into_keypair().unwrap(); + let sk = ed25519::SecretKey::generate(); + let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap(); + let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap(); assert!(secret_bytes(&kp1) == secret_bytes(&kp2)); } diff --git a/core/network/src/error.rs b/core/network/src/error.rs index 95a1dc5abe76b42b450835212ba9b823a2020101..c3f89e43c17107ecbcb0ae6b05f346c172d14fc2 100644 --- a/core/network/src/error.rs +++ b/core/network/src/error.rs @@ -18,16 +18,42 @@ use client; +use libp2p::{PeerId, Multiaddr}; + +use std::fmt; + /// Result type alias for the network. pub type Result = std::result::Result; /// Error type for the network. -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(derive_more::Display, derive_more::From)] pub enum Error { /// Io error Io(std::io::Error), /// Client error Client(client::error::Error), + /// The same bootnode (based on address) is registered with two different peer ids. + #[display( + fmt = "The same bootnode (`{}`) is registered with two different peer ids: `{}` and `{}`", + address, + first_id, + second_id, + )] + DuplicateBootnode { + /// The address of the bootnode. + address: Multiaddr, + /// The first peer id that was found for the bootnode. + first_id: PeerId, + /// The second peer id that was found for the bootnode. + second_id: PeerId, + }, +} + +// Make `Debug` use the `Display` implementation. +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } } impl std::error::Error for Error { @@ -35,6 +61,7 @@ impl std::error::Error for Error { match self { Error::Io(ref err) => Some(err), Error::Client(ref err) => Some(err), + Error::DuplicateBootnode { .. } => None, } } } diff --git a/core/network/src/lib.rs b/core/network/src/lib.rs index 7e9fd51a41533b12b3aa84f7ec1d9584bc2a1b70..d0977d90c90053fba40cddecd105d54bb83b9e02 100644 --- a/core/network/src/lib.rs +++ b/core/network/src/lib.rs @@ -24,9 +24,7 @@ //! # Node identities and addresses //! //! In a decentralized network, each node possesses a network private key and a network public key. -//! In Substrate, the keys are based on the ed25519 curve. As of the writing of this documentation, -//! the secp256k1 curve can also be used, but is deprecated. Our local node's keypair must be -//! passed as part of the network configuration. +//! In Substrate, the keys are based on the ed25519 curve. //! //! From a node's public key, we can derive its *identity*. In Substrate and libp2p, a node's //! identity is represented with the [`PeerId`] struct. All network communications between nodes on diff --git a/core/network/src/protocol.rs b/core/network/src/protocol.rs index dc9e6688e74ee8c25d30bc5b1a53bcd7977affeb..6afb4c8b116ae56087330ee22cc28392dddc000e 100644 --- a/core/network/src/protocol.rs +++ b/core/network/src/protocol.rs @@ -35,7 +35,6 @@ use sr_primitives::traits::{ }; use message::{BlockAnnounce, BlockAttributes, Direction, FromBlock, Message, RequestId}; use message::generic::{Message as GenericMessage, ConsensusMessage}; -use event::Event; use consensus_gossip::{ConsensusGossip, MessageRecipient as GossipMessageRecipient}; use light_dispatch::{LightDispatch, LightDispatchNetwork, RequestData}; use specialization::NetworkSpecialization; @@ -48,7 +47,7 @@ use std::sync::Arc; use std::{cmp, num::NonZeroUsize, time}; use log::{trace, debug, warn, error}; use crate::chain::{Client, FinalityProofProvider}; -use client::light::fetcher::{FetchChecker, ChangesProof}; +use client::light::fetcher::{FetchChecker, ChangesProof, StorageProof}; use crate::error; use util::LruHashSet; @@ -515,10 +514,6 @@ impl, H: ExHashT> Protocol { self.context_data.peers.iter().map(|(id, peer)| (id, &peer.info)) } - pub fn on_event(&mut self, event: Event) { - self.specialization.on_event(event); - } - pub fn on_custom_message( &mut self, who: PeerId, @@ -1121,18 +1116,13 @@ impl, H: ExHashT> Protocol { }; match self.sync.on_block_announce(who.clone(), hash, &announce, is_their_best) { - sync::OnBlockAnnounce::Request(peer, req) => { - self.send_message(peer, GenericMessage::BlockRequest(req)); - return CustomMessageOutcome::None - } sync::OnBlockAnnounce::Nothing => { - // try_import is only true when we have all data required to import block + // `on_block_announce` returns `OnBlockAnnounce::ImportHeader` + // when we have all data required to import the block // in the BlockAnnounce message. This is only when: // 1) we're on light client; // AND - // - EITHER 2.1) announced block is stale; - // - OR 2.2) announced block is NEW and we normally only want to download this single block (i.e. - // there are no ascendants of this block scheduled for retrieval) + // 2) parent block is already imported and not pruned. return CustomMessageOutcome::None } sync::OnBlockAnnounce::ImportHeader => () // We proceed with the import. @@ -1231,7 +1221,7 @@ impl, H: ExHashT> Protocol { error ); self.peerset_handle.report_peer(who.clone(), RPC_FAILED_REPUTATION_CHANGE); - Default::default() + StorageProof::empty() } }; @@ -1348,7 +1338,7 @@ impl, H: ExHashT> Protocol { request.block, error ); - Default::default() + StorageProof::empty() } }; self.send_message( @@ -1391,7 +1381,7 @@ impl, H: ExHashT> Protocol { request.block, error ); - Default::default() + StorageProof::empty() } }; self.send_message( @@ -1431,7 +1421,7 @@ impl, H: ExHashT> Protocol { request.block, error ); - (Default::default(), Default::default()) + (Default::default(), StorageProof::empty()) } }; self.send_message( @@ -1500,7 +1490,7 @@ impl, H: ExHashT> Protocol { max_block: Zero::zero(), proof: vec![], roots: BTreeMap::new(), - roots_proof: vec![], + roots_proof: StorageProof::empty(), } } }; diff --git a/core/network/src/protocol/consensus_gossip.rs b/core/network/src/protocol/consensus_gossip.rs index d916e9aace7044cd1a048b641ba91cd0696ef120..f3d4e536a788d2d238c01d5d7adb794e921e3402 100644 --- a/core/network/src/protocol/consensus_gossip.rs +++ b/core/network/src/protocol/consensus_gossip.rs @@ -48,7 +48,7 @@ use std::sync::Arc; use std::iter; use std::time; use log::{trace, debug}; -use futures::sync::mpsc; +use futures03::channel::mpsc; use lru_cache::LruCache; use libp2p::PeerId; use sr_primitives::traits::{Block as BlockT, Hash, HashFor}; @@ -89,6 +89,7 @@ struct MessageEntry { message_hash: B::Hash, topic: B::Hash, message: ConsensusMessage, + sender: Option, } /// Consensus message destination. @@ -322,12 +323,14 @@ impl ConsensusGossip { message_hash: B::Hash, topic: B::Hash, message: ConsensusMessage, + sender: Option, ) { if self.known_messages.insert(message_hash.clone(), ()).is_none() { self.messages.push(MessageEntry { message_hash, topic, message, + sender, }); } } @@ -343,7 +346,7 @@ impl ConsensusGossip { message: ConsensusMessage, ) { let message_hash = HashFor::::hash(&message.data[..]); - self.register_message_hashed(message_hash, topic, message); + self.register_message_hashed(message_hash, topic, message, None); } /// Call when a peer has been disconnected to stop tracking gossip status. @@ -429,7 +432,7 @@ impl ConsensusGossip { { tx.unbounded_send(TopicNotification { message: entry.message.data.clone(), - sender: None, + sender: entry.sender.clone(), }) .expect("receiver known to be live; qed"); } @@ -498,7 +501,7 @@ impl ConsensusGossip { } } if keep { - self.register_message_hashed(message_hash, topic, message); + self.register_message_hashed(message_hash, topic, message, Some(who.clone())); } } else { trace!(target:"gossip", "Ignored statement from unregistered peer {}", who); @@ -553,7 +556,7 @@ impl ConsensusGossip { force: bool, ) { let message_hash = HashFor::::hash(&message.data); - self.register_message_hashed(message_hash, topic, message.clone()); + self.register_message_hashed(message_hash, topic, message.clone(), None); let intent = if force { MessageIntent::ForcedBroadcast } else { MessageIntent::Broadcast }; propagate(protocol, iter::once((&message_hash, &topic, &message)), intent, &mut self.peers, &self.validators); } @@ -605,7 +608,7 @@ impl Validator for DiscardAll { #[cfg(test)] mod tests { use sr_primitives::testing::{H256, Block as RawBlock, ExtrinsicWrapper}; - use futures::Stream; + use futures03::executor::block_on_stream; use super::*; @@ -618,6 +621,7 @@ mod tests { message_hash: $hash, topic: $topic, message: ConsensusMessage { data: $m, engine_id: [0, 0, 0, 0]}, + sender: None, }); } } @@ -666,7 +670,7 @@ mod tests { let m2 = vec![4, 5, 6]; push_msg!(consensus, prev_hash, m1_hash, m1); - push_msg!(consensus, best_hash, m2_hash, m2.clone()); + push_msg!(consensus, best_hash, m2_hash, m2); consensus.known_messages.insert(m1_hash, ()); consensus.known_messages.insert(m2_hash, ()); @@ -688,8 +692,6 @@ mod tests { #[test] fn message_stream_include_those_sent_before_asking_for_stream() { - use futures::Stream; - let mut consensus = ConsensusGossip::::new(); consensus.register_validator_internal([0, 0, 0, 0], Arc::new(AllowAll)); @@ -697,9 +699,9 @@ mod tests { let topic = HashFor::::hash(&[1,2,3]); consensus.register_message(topic, message.clone()); - let stream = consensus.messages_for([0, 0, 0, 0], topic); + let mut stream = block_on_stream(consensus.messages_for([0, 0, 0, 0], topic)); - assert_eq!(stream.wait().next(), Some(Ok(TopicNotification { message: message.data, sender: None }))); + assert_eq!(stream.next(), Some(TopicNotification { message: message.data, sender: None })); } #[test] @@ -721,16 +723,17 @@ mod tests { let mut consensus = ConsensusGossip::::new(); consensus.register_validator_internal([0, 0, 0, 0], Arc::new(AllowAll)); - let message = ConsensusMessage { data: vec![4, 5, 6], engine_id: [0, 0, 0, 0] }; - let topic = HashFor::::hash(&[1,2,3]); + let data = vec![4, 5, 6]; + let message = ConsensusMessage { data: data.clone(), engine_id: [0, 0, 0, 0] }; + let topic = HashFor::::hash(&[1, 2, 3]); consensus.register_message(topic, message.clone()); - let stream1 = consensus.messages_for([0, 0, 0, 0], topic); - let stream2 = consensus.messages_for([0, 0, 0, 0], topic); + let mut stream1 = block_on_stream(consensus.messages_for([0, 0, 0, 0], topic)); + let mut stream2 = block_on_stream(consensus.messages_for([0, 0, 0, 0], topic)); - assert_eq!(stream1.wait().next(), Some(Ok(TopicNotification { message: message.data.clone(), sender: None }))); - assert_eq!(stream2.wait().next(), Some(Ok(TopicNotification { message: message.data, sender: None }))); + assert_eq!(stream1.next(), Some(TopicNotification { message: data.clone(), sender: None })); + assert_eq!(stream2.next(), Some(TopicNotification { message: data, sender: None })); } #[test] @@ -745,9 +748,10 @@ mod tests { consensus.register_message(topic, msg_a); consensus.register_message(topic, msg_b); - let mut stream = consensus.messages_for([0, 0, 0, 0], topic).wait(); + let mut stream = block_on_stream(consensus.messages_for([0, 0, 0, 0], topic)); + + assert_eq!(stream.next(), Some(TopicNotification { message: vec![1, 2, 3], sender: None })); - assert_eq!(stream.next(), Some(Ok(TopicNotification { message: vec![1, 2, 3], sender: None }))); let _ = consensus.live_message_sinks.remove(&([0, 0, 0, 0], topic)); assert_eq!(stream.next(), None); } diff --git a/core/network/src/protocol/light_dispatch.rs b/core/network/src/protocol/light_dispatch.rs index 33ff23c909d1060f7ede1b599008f1705966fb8f..3c0185da873467ef941662d8fcecb56d833974bd 100644 --- a/core/network/src/protocol/light_dispatch.rs +++ b/core/network/src/protocol/light_dispatch.rs @@ -28,7 +28,7 @@ use linked_hash_map::{Entry, LinkedHashMap}; use client::error::Error as ClientError; use client::light::fetcher::{FetchChecker, RemoteHeaderRequest, RemoteCallRequest, RemoteReadRequest, RemoteChangesRequest, ChangesProof, - RemoteReadChildRequest, RemoteBodyRequest}; + RemoteReadChildRequest, RemoteBodyRequest, StorageProof}; use crate::message::{self, BlockAttributes, Direction, FromBlock, RequestId}; use libp2p::PeerId; use crate::config::Roles; @@ -174,7 +174,7 @@ impl FetchChecker for AlwaysBadChecker { &self, _request: &RemoteHeaderRequest, _remote_header: Option, - _remote_proof: Vec> + _remote_proof: StorageProof, ) -> Result { Err(ClientError::Msg("AlwaysBadChecker".into())) } @@ -182,7 +182,7 @@ impl FetchChecker for AlwaysBadChecker { fn check_read_proof( &self, _request: &RemoteReadRequest, - _remote_proof: Vec> + _remote_proof: StorageProof, ) -> Result,Option>>, ClientError> { Err(ClientError::Msg("AlwaysBadChecker".into())) } @@ -190,7 +190,7 @@ impl FetchChecker for AlwaysBadChecker { fn check_read_child_proof( &self, _request: &RemoteReadChildRequest, - _remote_proof: Vec> + _remote_proof: StorageProof, ) -> Result, Option>>, ClientError> { Err(ClientError::Msg("AlwaysBadChecker".into())) } @@ -198,7 +198,7 @@ impl FetchChecker for AlwaysBadChecker { fn check_execution_proof( &self, _request: &RemoteCallRequest, - _remote_proof: Vec> + _remote_proof: StorageProof, ) -> Result, ClientError> { Err(ClientError::Msg("AlwaysBadChecker".into())) } @@ -684,7 +684,7 @@ pub mod tests { use crate::config::Roles; use crate::message::{self, BlockAttributes, Direction, FromBlock, RequestId}; use libp2p::PeerId; - use super::{REQUEST_TIMEOUT, LightDispatch, LightDispatchNetwork, RequestData}; + use super::{REQUEST_TIMEOUT, LightDispatch, LightDispatchNetwork, RequestData, StorageProof}; use test_client::runtime::{changes_trie_config, Block, Extrinsic, Header}; struct DummyFetchChecker { ok: bool } @@ -694,7 +694,7 @@ pub mod tests { &self, _request: &RemoteHeaderRequest
, header: Option
, - _remote_proof: Vec> + _remote_proof: StorageProof, ) -> ClientResult
{ match self.ok { true if header.is_some() => Ok(header.unwrap()), @@ -705,7 +705,7 @@ pub mod tests { fn check_read_proof( &self, request: &RemoteReadRequest
, - _: Vec>, + _: StorageProof, ) -> ClientResult, Option>>> { match self.ok { true => Ok(request.keys @@ -721,7 +721,7 @@ pub mod tests { fn check_read_child_proof( &self, request: &RemoteReadChildRequest
, - _: Vec> + _: StorageProof, ) -> ClientResult, Option>>> { match self.ok { true => Ok(request.keys @@ -734,7 +734,7 @@ pub mod tests { } } - fn check_execution_proof(&self, _: &RemoteCallRequest
, _: Vec>) -> ClientResult> { + fn check_execution_proof(&self, _: &RemoteCallRequest
, _: StorageProof) -> ClientResult> { match self.ok { true => Ok(vec![42]), false => Err(ClientError::Backend("Test error".into())), @@ -780,7 +780,7 @@ pub mod tests { ) { light_dispatch.on_remote_call_response(network_interface, peer, message::RemoteCallResponse { id: id, - proof: vec![vec![2]], + proof: StorageProof::empty(), }); } @@ -943,7 +943,7 @@ pub mod tests { light_dispatch.on_remote_read_response(&mut network_interface, peer0.clone(), message::RemoteReadResponse { id: 0, - proof: vec![vec![2]], + proof: StorageProof::empty(), }); assert_disconnected_peer(&network_interface); assert_eq!(light_dispatch.pending_requests.len(), 1); @@ -1013,7 +1013,7 @@ pub mod tests { light_dispatch.on_remote_read_response(&mut network_interface, peer0.clone(), message::RemoteReadResponse { id: 0, - proof: vec![vec![2]], + proof: StorageProof::empty(), }); assert_eq!(response.wait().unwrap().unwrap().remove(b":key".as_ref()).unwrap(), Some(vec![42])); } @@ -1037,7 +1037,7 @@ pub mod tests { light_dispatch.on_remote_read_response(&mut network_interface, peer0.clone(), message::RemoteReadResponse { id: 0, - proof: vec![vec![2]], + proof: StorageProof::empty(), }); assert_eq!(response.wait().unwrap().unwrap().remove(b":key".as_ref()).unwrap(), Some(vec![42])); } @@ -1065,7 +1065,7 @@ pub mod tests { extrinsics_root: Default::default(), digest: Default::default(), }), - proof: vec![vec![2]], + proof: StorageProof::empty(), }); assert_eq!( response.wait().unwrap().unwrap().hash(), @@ -1097,7 +1097,7 @@ pub mod tests { max: 1000, proof: vec![vec![2]], roots: vec![], - roots_proof: vec![], + roots_proof: StorageProof::empty(), }); assert_eq!(response.wait().unwrap().unwrap(), vec![(100, 2)]); } @@ -1145,7 +1145,7 @@ pub mod tests { light_dispatch.on_remote_header_response(&mut network_interface, peer1.clone(), message::RemoteHeaderResponse { id: 0, header: Some(dummy_header()), - proof: vec![], + proof: StorageProof::empty(), }); assert!(!light_dispatch.idle_peers.iter().any(|_| true)); diff --git a/core/network/src/protocol/message.rs b/core/network/src/protocol/message.rs index 6560ed0f13a52c92c172e42b5f2796e6ee55a4d8..ef5e4dbb1db0f4ad90e844b17d72851187528008 100644 --- a/core/network/src/protocol/message.rs +++ b/core/network/src/protocol/message.rs @@ -26,6 +26,7 @@ pub use self::generic::{ FinalityProofRequest, FinalityProofResponse, FromBlock, RemoteReadChildRequest, }; +use client::light::fetcher::StorageProof; /// A unique ID of a request. pub type RequestId = u64; @@ -122,7 +123,7 @@ pub struct RemoteCallResponse { /// Id of a request this response was made for. pub id: RequestId, /// Execution proof. - pub proof: Vec>, + pub proof: StorageProof, } #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] @@ -131,7 +132,7 @@ pub struct RemoteReadResponse { /// Id of a request this response was made for. pub id: RequestId, /// Read proof. - pub proof: Vec>, + pub proof: StorageProof, } /// Generic types. @@ -142,7 +143,7 @@ pub mod generic { use super::{ RemoteReadResponse, Transactions, Direction, RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId, - BlockState, + BlockState, StorageProof, }; /// Consensus is mostly opaque to us #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] @@ -359,7 +360,7 @@ pub mod generic { /// Header. None if proof generation has failed (e.g. header is unknown). pub header: Option
, /// Header proof. - pub proof: Vec>, + pub proof: StorageProof, } #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] @@ -395,7 +396,7 @@ pub mod generic { /// Changes tries roots missing on the requester' node. pub roots: Vec<(N, H)>, /// Missing changes tries roots proof. - pub roots_proof: Vec>, + pub roots_proof: StorageProof, } #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] diff --git a/core/network/src/protocol/specialization.rs b/core/network/src/protocol/specialization.rs index e2c444ea889d6b8d336394c6c1cdff67119bd520..1a15c47c87d3a45d9653e029831367c56c33efff 100644 --- a/core/network/src/protocol/specialization.rs +++ b/core/network/src/protocol/specialization.rs @@ -42,7 +42,8 @@ pub trait NetworkSpecialization: Send + Sync + 'static { ); /// Called when a network-specific event arrives. - fn on_event(&mut self, event: Event); + #[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")] diff --git a/core/network/src/protocol/sync.rs b/core/network/src/protocol/sync.rs index 9f9db92289cb6a0f6777b089453208974407690e..4f08c942def1bfc1f118aa90aa8c4a2e0cb4beb5 100644 --- a/core/network/src/protocol/sync.rs +++ b/core/network/src/protocol/sync.rs @@ -69,9 +69,6 @@ const MAJOR_SYNC_BLOCKS: u8 = 5; /// Number of recently announced blocks to track for each peer. const ANNOUNCE_HISTORY_SIZE: usize = 64; -/// Max number of blocks to download for unknown forks. -const MAX_UNKNOWN_FORK_DOWNLOAD_LEN: u32 = 32; - /// Reputation change when a peer sent us a status message that led to a /// database read error. const BLOCKCHAIN_STATUS_READ_ERROR_REPUTATION_CHANGE: i32 = -(1 << 16); @@ -125,8 +122,8 @@ pub struct ChainSync { best_importing_number: NumberFor, /// Finality proof handler. request_builder: Option>, - /// Explicit sync requests. - sync_requests: HashMap>, + /// Fork sync targets. + fork_targets: HashMap>, /// A flag that caches idle state with no pending requests. is_idle: bool, /// A type to check incoming block announcements. @@ -160,8 +157,9 @@ pub struct PeerInfo { pub best_number: NumberFor } -struct SyncRequest { +struct ForkTarget { number: NumberFor, + parent_hash: Option, peers: HashSet, } @@ -242,13 +240,11 @@ pub enum OnBlockData { /// Result of [`ChainSync::on_block_announce`]. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum OnBlockAnnounce { +pub enum OnBlockAnnounce { /// The announcement does not require further handling. Nothing, /// The announcement header should be imported. ImportHeader, - /// Another block request to the given peer is necessary. - Request(PeerId, BlockRequest) } /// Result of [`ChainSync::on_block_justification`]. @@ -307,7 +303,7 @@ impl ChainSync { queue_blocks: Default::default(), best_importing_number: Zero::zero(), request_builder, - sync_requests: Default::default(), + fork_targets: Default::default(), is_idle: false, block_announce_validator, } @@ -460,14 +456,24 @@ impl ChainSync { /// Request syncing for the given block from given set of peers. // The implementation is similar to on_block_announce with unknown parent hash. - pub fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor) { + pub fn set_sync_fork_request(&mut self, mut peers: Vec, hash: &B::Hash, number: NumberFor) { if peers.is_empty() { - if let Some(_) = self.sync_requests.remove(hash) { - debug!(target: "sync", "Cleared sync request for block {:?} with {:?}", hash, peers); - } - return; + debug!( + target: "sync", + "Explicit sync request for block {:?} with no peers specified. \ + Syncing from all connected peers {:?} instead.", + hash, peers, + ); + + peers = self.peers.iter() + // Only request blocks from peers who are ahead or on a par. + .filter(|(_, peer)| peer.best_number >= number) + .map(|(id, _)| id.clone()) + .collect(); + } else { + debug!(target: "sync", "Explicit sync request for block {:?} with {:?}", hash, peers); } - debug!(target: "sync", "Explicit sync request for block {:?} with {:?}", hash, peers); + if self.is_known(&hash) { debug!(target: "sync", "Refusing to sync known hash {:?}", hash); return; @@ -494,11 +500,12 @@ impl ChainSync { } } - self.sync_requests + self.fork_targets .entry(hash.clone()) - .or_insert_with(|| SyncRequest { + .or_insert_with(|| ForkTarget { number, peers: Default::default(), + parent_hash: None, }) .peers.extend(peers); } @@ -562,17 +569,30 @@ impl ChainSync { } let blocks = &mut self.blocks; let attrs = &self.required_block_attributes; - let sync_requests = &self.sync_requests; + let fork_targets = &self.fork_targets; let mut have_requests = false; let last_finalized = self.client.info().chain.finalized_number; let best_queued = self.best_queued_number; + let client = &self.client; + let queue = &self.queue_blocks; let iter = self.peers.iter_mut().filter_map(move |(id, peer)| { if !peer.state.is_available() { trace!(target: "sync", "Peer {} is busy", id); return None } - if let Some((hash, req)) = explicit_sync_request(id, sync_requests, best_queued, last_finalized, attrs) { - trace!(target: "sync", "Downloading explicitly requested block {:?} from {}", hash, id); + if let Some((hash, req)) = fork_sync_request( + id, + fork_targets, + best_queued, + last_finalized, + attrs, + |hash| if queue.contains(hash) { + BlockStatus::Queued + } else { + client.block_status(&BlockId::Hash(*hash)).unwrap_or(BlockStatus::Unknown) + }, + ) { + trace!(target: "sync", "Downloading fork {:?} from {}", hash, id); peer.state = PeerSyncState::DownloadingStale(hash); have_requests = true; Some((id.clone(), req)) @@ -665,6 +685,26 @@ impl ChainSync { peer.state = PeerSyncState::AncestorSearch(next_num, next_state); return Ok(OnBlockData::Request(who, ancestry_request::(next_num))) } else { + // Ancestry search is complete. Check if peer is on a stale fork unknown to us and + // add it to sync targets if necessary. + trace!(target: "sync", "Ancestry search complete. Ours={} ({}), Theirs={} ({}), Common={}", + self.best_queued_hash, + self.best_queued_number, + peer.best_hash, + peer.best_number, + peer.common_number + ); + if peer.common_number < peer.best_number && peer.best_number < self.best_queued_number { + trace!(target: "sync", "Added fork target {} for {}" , peer.best_hash, who); + self.fork_targets + .entry(peer.best_hash.clone()) + .or_insert_with(|| ForkTarget { + number: peer.best_number, + parent_hash: None, + peers: Default::default(), + }) + .peers.insert(who); + } peer.state = PeerSyncState::Available; Vec::new() } @@ -922,14 +962,14 @@ impl ChainSync { self.best_queued_number = number; self.best_queued_hash = *hash; } - if let Some(_) = self.sync_requests.remove(&hash) { - trace!(target: "sync", "Completed explicit sync request {:?}", hash); + if let Some(_) = self.fork_targets.remove(&hash) { + trace!(target: "sync", "Completed fork sync {:?}", hash); } // Update common blocks for (n, peer) in self.peers.iter_mut() { if let PeerSyncState::AncestorSearch(_, _) = peer.state { - // Abort search. - peer.state = PeerSyncState::Available; + // Wait for ancestry search to complete first. + continue; } let new_common_number = if peer.best_number >= number { number @@ -952,12 +992,12 @@ impl ChainSync { /// Call when a node announces a new block. /// - /// If true is returned, then the caller MUST try to import passed + /// If `OnBlockAnnounce::ImportHeader` is returned, then the caller MUST try to import passed /// header (call `on_block_data`). The network request isn't sent /// in this case. Both hash and header is passed as an optimization /// to avoid rehashing the header. pub fn on_block_announce(&mut self, who: PeerId, hash: B::Hash, announce: &BlockAnnounce, is_best: bool) - -> OnBlockAnnounce + -> OnBlockAnnounce { let header = &announce.header; let number = *header.number(); @@ -1001,6 +1041,9 @@ impl ChainSync { // known block case if known || self.is_already_downloading(&hash) { trace!(target: "sync", "Known block announce from {}: {}", who, hash); + if let Some(target) = self.fork_targets.get_mut(&hash) { + target.peers.insert(who); + } return OnBlockAnnounce::Nothing } @@ -1009,79 +1052,42 @@ impl ChainSync { match self.block_announce_validator.validate(&header, assoc_data) { Ok(Validation::Success) => (), Ok(Validation::Failure) => { - debug!(target: "sync", "block announcement validation of block {} from {} failed", hash, who); + debug!(target: "sync", "Block announcement validation of block {} from {} failed", hash, who); return OnBlockAnnounce::Nothing } Err(e) => { - error!(target: "sync", "block announcement validation errored: {}", e); + error!(target: "sync", "Block announcement validation errored: {}", e); return OnBlockAnnounce::Nothing } } - // stale block case - let requires_additional_data = !self.role.is_light(); - if number <= self.best_queued_number { - if !(known_parent || self.is_already_downloading(header.parent_hash())) { - let block_status = self.client.block_status(&BlockId::Number(*header.number())) - .unwrap_or(BlockStatus::Unknown); - if block_status == BlockStatus::InChainPruned { - trace!( - target: "sync", - "Ignored unknown ancient block announced from {}: {} {:?}", who, hash, header - ); - return OnBlockAnnounce::Nothing - } - trace!( - target: "sync", - "Considering new unknown stale block announced from {}: {} {:?}", who, hash, header - ); - if let Some(request) = self.download_unknown_stale(&who, &hash) { - if requires_additional_data { - return OnBlockAnnounce::Request(who, request) - } else { - return OnBlockAnnounce::ImportHeader - } - } else { - return OnBlockAnnounce::Nothing - } - } else { - if ancient_parent { - trace!(target: "sync", "Ignored ancient stale block announced from {}: {} {:?}", who, hash, header); - return OnBlockAnnounce::Nothing - } - if let Some(request) = self.download_stale(&who, &hash) { - if requires_additional_data { - return OnBlockAnnounce::Request(who, request) - } else { - return OnBlockAnnounce::ImportHeader - } - } else { - return OnBlockAnnounce::Nothing - } - } - } - if ancient_parent { trace!(target: "sync", "Ignored ancient block announced from {}: {} {:?}", who, hash, header); return OnBlockAnnounce::Nothing } - trace!(target: "sync", "Considering new block announced from {}: {} {:?}", who, hash, header); - - let (range, request) = match self.select_new_blocks(who.clone()) { - Some((range, request)) => (range, request), - None => return OnBlockAnnounce::Nothing - }; - - let is_required_data_available = !requires_additional_data - && range.end - range.start == One::one() - && range.start == *header.number(); + let requires_additional_data = !self.role.is_light() || !known_parent; + if !requires_additional_data { + trace!(target: "sync", "Importing new header announced from {}: {} {:?}", who, hash, header); + return OnBlockAnnounce::ImportHeader + } - if !is_required_data_available { - return OnBlockAnnounce::Request(who, request) + if number <= self.best_queued_number { + trace!( + target: "sync", + "Added sync target for block announced from {}: {} {:?}", who, hash, header + ); + self.fork_targets + .entry(hash.clone()) + .or_insert_with(|| ForkTarget { + number, + parent_hash: Some(header.parent_hash().clone()), + peers: Default::default(), + }) + .peers.insert(who); } - OnBlockAnnounce::ImportHeader + OnBlockAnnounce::Nothing } /// Call when a peer has disconnected. @@ -1117,40 +1123,6 @@ impl ChainSync { }) } - /// Download old block with known parent. - fn download_stale(&mut self, who: &PeerId, hash: &B::Hash) -> Option> { - let peer = self.peers.get_mut(who)?; - if !peer.state.is_available() { - return None - } - peer.state = PeerSyncState::DownloadingStale(*hash); - Some(message::generic::BlockRequest { - id: 0, - fields: self.required_block_attributes.clone(), - from: message::FromBlock::Hash(*hash), - to: None, - direction: message::Direction::Ascending, - max: Some(1), - }) - } - - /// Download old block with unknown parent. - fn download_unknown_stale(&mut self, who: &PeerId, hash: &B::Hash) -> Option> { - let peer = self.peers.get_mut(who)?; - if !peer.state.is_available() { - return None - } - peer.state = PeerSyncState::DownloadingStale(*hash); - Some(message::generic::BlockRequest { - id: 0, - fields: self.required_block_attributes.clone(), - from: message::FromBlock::Hash(*hash), - to: None, - direction: message::Direction::Descending, - max: Some(MAX_UNKNOWN_FORK_DOWNLOAD_LEN), - }) - } - /// Select a range of new blocks to download from the given peer. fn select_new_blocks(&mut self, who: PeerId) -> Option<(Range>, BlockRequest)> { // when there are too many blocks in the queue => do not try to download new blocks @@ -1298,28 +1270,35 @@ fn peer_block_request( } } -/// Get pending explicit sync request for a peer. -fn explicit_sync_request( +/// Get pending fork sync targets for a peer. +fn fork_sync_request( id: &PeerId, - requests: &HashMap>, + targets: &HashMap>, best_num: NumberFor, finalized: NumberFor, attributes: &message::BlockAttributes, + check_block: impl Fn(&B::Hash) -> BlockStatus, ) -> Option<(B::Hash, BlockRequest)> { - for (hash, r) in requests { + for (hash, r) in targets { if !r.peers.contains(id) { continue } if r.number <= best_num { trace!(target: "sync", "Downloading requested fork {:?} from {}", hash, id); + let parent_status = r.parent_hash.as_ref().map_or(BlockStatus::Unknown, check_block); + let mut count = (r.number - finalized).saturated_into::(); // up to the last finalized block + if parent_status != BlockStatus::Unknown { + // request only single block + count = 1; + } return Some((hash.clone(), message::generic::BlockRequest { id: 0, fields: attributes.clone(), from: message::FromBlock::Hash(hash.clone()), to: None, direction: message::Direction::Descending, - max: Some((r.number - finalized).saturated_into::()), // up to the last finalized block + max: Some(count), })) } } diff --git a/core/network/src/service.rs b/core/network/src/service.rs index 5e8d41340c1bfc1b523fb5510a142114922387c8..a88c163a691e953eae581179049e6d7c443baed2 100644 --- a/core/network/src/service.rs +++ b/core/network/src/service.rs @@ -42,7 +42,7 @@ use sr_primitives::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId}; use crate::{behaviour::{Behaviour, BehaviourOut}, config::{parse_str_addr, parse_addr}}; use crate::{NetworkState, NetworkStateNotConnectedPeer, NetworkStatePeer}; -use crate::{transport, config::NodeKeyConfig, config::NonReservedPeerMode}; +use crate::{transport, config::NonReservedPeerMode}; use crate::config::{Params, TransportConfig}; use crate::error::Error; use crate::protocol::{self, Protocol, Context, CustomMessageOutcome, PeerInfo}; @@ -52,14 +52,11 @@ use crate::protocol::specialization::NetworkSpecialization; use crate::protocol::sync::SyncState; /// Minimum Requirements for a Hash within Networking -pub trait ExHashT: - ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static -{ -} +pub trait ExHashT: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync + 'static {} + impl ExHashT for T where - T: ::std::hash::Hash + Eq + ::std::fmt::Debug + Clone + Send + Sync + 'static -{ -} + T: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync + 'static +{} /// Transaction pool interface pub trait TransactionPool: Send + Sync { @@ -152,6 +149,23 @@ impl, H: ExHashT> NetworkWorker } } + // Check for duplicate bootnodes. + known_addresses.iter() + .try_for_each(|(peer_id, addr)| + if let Some(other) = known_addresses + .iter() + .find(|o| o.1 == *addr && o.0 != *peer_id) + { + Err(Error::DuplicateBootnode { + address: addr.clone(), + first_id: peer_id.clone(), + second_id: other.0.clone(), + }) + } else { + Ok(()) + } + )?; + // Initialize the reserved peers. for reserved in params.network_config.reserved_nodes.iter() { if let Ok((peer_id, addr)) = parse_str_addr(reserved) { @@ -171,9 +185,6 @@ impl, H: ExHashT> NetworkWorker }; // Private and public keys configuration. - if let NodeKeyConfig::Secp256k1(_) = params.network_config.node_key { - warn!(target: "sub-libp2p", "Secp256k1 keys are deprecated in favour of ed25519"); - } let local_identity = params.network_config.node_key.clone().into_keypair()?; let local_public = local_identity.public(); let local_peer_id = local_public.clone().into_peer_id(); @@ -469,8 +480,8 @@ impl, H: ExHashT> NetworkServic /// Start getting a value from the DHT. /// - /// This will generate either a `ValueFound` or a `ValueNotFound` event and pass it to - /// `on_event` on the network specialization. + /// This will generate either a `ValueFound` or a `ValueNotFound` event and pass it as an + /// item on the [`NetworkWorker`] stream. pub fn get_value(&self, key: &record::Key) { let _ = self .to_worker @@ -479,8 +490,8 @@ impl, H: ExHashT> NetworkServic /// Start putting a value in the DHT. /// - /// This will generate either a `ValuePut` or a `ValuePutFailed` event and pass it to - /// `on_event` on the network specialization. + /// This will generate either a `ValuePut` or a `ValuePutFailed` event and pass it as an + /// item on the [`NetworkWorker`] stream. pub fn put_value(&self, key: record::Key, value: Vec) { let _ = self .to_worker @@ -553,8 +564,9 @@ impl, H: ExHashT> NetworkServic } } -impl, H: ExHashT> - ::consensus::SyncOracle for NetworkService { +impl, H: ExHashT> consensus::SyncOracle + for NetworkService +{ fn is_major_syncing(&mut self) -> bool { NetworkService::is_major_syncing(self) } @@ -564,8 +576,9 @@ impl, H: ExHashT> } } -impl<'a, B: BlockT + 'static, S: NetworkSpecialization, H: ExHashT> - ::consensus::SyncOracle for &'a NetworkService { +impl<'a, B: BlockT + 'static, S: NetworkSpecialization, H: ExHashT> consensus::SyncOracle + for &'a NetworkService +{ fn is_major_syncing(&mut self) -> bool { NetworkService::is_major_syncing(self) } @@ -705,12 +718,8 @@ impl, H: ExHashT> Stream for Ne let outcome = match poll_value { Ok(Async::NotReady) => break, Ok(Async::Ready(Some(BehaviourOut::SubstrateAction(outcome)))) => outcome, - Ok(Async::Ready(Some(BehaviourOut::Dht(ev)))) => { - self.network_service.user_protocol_mut() - .on_event(Event::Dht(ev.clone())); - - return Ok(Async::Ready(Some(Event::Dht(ev)))); - }, + Ok(Async::Ready(Some(BehaviourOut::Dht(ev)))) => + return Ok(Async::Ready(Some(Event::Dht(ev)))), Ok(Async::Ready(None)) => CustomMessageOutcome::None, Err(err) => { error!(target: "sync", "Error in the network: {:?}", err); diff --git a/core/network/src/test/mod.rs b/core/network/src/test/mod.rs index 2c87ba1ac8593e9d8cacf0e1830cd164c0a6439a..0c50179f10aceddf948f9d785d04753600783e7a 100644 --- a/core/network/src/test/mod.rs +++ b/core/network/src/test/mod.rs @@ -124,11 +124,6 @@ impl NetworkSpecialization for DummySpecialization { _peer_id: PeerId, _message: Vec, ) {} - - fn on_event( - &mut self, - _event: crate::specialization::Event - ) {} } pub type PeersFullClient = @@ -704,7 +699,9 @@ pub trait TestNetFactory: Sized { fn poll(&mut self) { self.mut_peers(|peers| { for peer in peers { + trace!(target: "sync", "-- Polling {}", peer.id()); peer.network.poll().unwrap(); + trace!(target: "sync", "-- Polling complete {}", peer.id()); // We poll `imported_blocks_stream`. while let Ok(Async::Ready(Some(notification))) = peer.imported_blocks_stream.poll() { diff --git a/core/network/src/test/sync.rs b/core/network/src/test/sync.rs index b5b137a31ac85cef77adeac95301e2cf1ad4329c..b1b2b9d407262c7a32f25638120c4ec76aa14354 100644 --- a/core/network/src/test/sync.rs +++ b/core/network/src/test/sync.rs @@ -457,6 +457,17 @@ fn can_sync_small_non_best_forks() { } Ok(Async::Ready(())) })).unwrap(); + net.block_until_sync(&mut runtime); + + let another_fork = net.peer(0).push_blocks_at(BlockId::Number(35), 2, true); + net.peer(0).announce_block(another_fork, Vec::new()); + runtime.block_on(futures::future::poll_fn::<(), (), _>(|| -> Result<_, ()> { + net.poll(); + if net.peer(1).client().header(&BlockId::Hash(another_fork)).unwrap().is_none() { + return Ok(Async::NotReady) + } + Ok(Async::Ready(())) + })).unwrap(); } #[test] diff --git a/core/offchain/Cargo.toml b/core/offchain/Cargo.toml index 8f342ee60243bfcae9b292945344d0f6f5393625..b52118aae7b70ed3b8a15cec681c2ac7214cb0d0 100644 --- a/core/offchain/Cargo.toml +++ b/core/offchain/Cargo.toml @@ -13,9 +13,9 @@ fnv = "1.0.6" futures01 = { package = "futures", version = "0.1" } futures-preview = "0.3.0-alpha.19" futures-timer = "0.4.0" -hyper = "0.12.35" -hyper-tls = "0.3.2" log = "0.4.8" +threadpool = "1.7" +num_cpus = "1.10" offchain-primitives = { package = "substrate-offchain-primitives", path = "./primitives" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } parking_lot = "0.9.0" @@ -26,8 +26,12 @@ transaction_pool = { package = "substrate-transaction-pool", path = "../../core/ network = { package = "substrate-network", path = "../../core/network" } keystore = { package = "substrate-keystore", path = "../keystore" } +[target.'cfg(not(target_os = "unknown"))'.dependencies] +hyper = "0.12.35" +hyper-tls = "0.3.2" + [dev-dependencies] -env_logger = "0.6.2" +env_logger = "0.7.0" client-db = { package = "substrate-client-db", path = "../../core/client/db/", default-features = true } test-client = { package = "substrate-test-runtime-client", path = "../../core/test-runtime/client" } tokio = "0.1.22" diff --git a/core/offchain/primitives/src/lib.rs b/core/offchain/primitives/src/lib.rs index dda08ae43f570afda5886e6bd6dd9518f0a5c511..79fed8cb34150a45f4a3b357fd446e536dfc0613 100644 --- a/core/offchain/primitives/src/lib.rs +++ b/core/offchain/primitives/src/lib.rs @@ -22,6 +22,9 @@ use client::decl_runtime_apis; use sr_primitives::traits::NumberFor; +/// Local Storage Prefix used by the Offchain Worker API to +pub const STORAGE_PREFIX: &[u8] = b"storage"; + decl_runtime_apis! { /// The offchain worker api. pub trait OffchainWorkerApi { diff --git a/core/offchain/src/api.rs b/core/offchain/src/api.rs index d17a892d975e10a5f4fff54018671ac6a321a332..b4d55e5f06823b1304cda8a6d3304b17e7ad0730 100644 --- a/core/offchain/src/api.rs +++ b/core/offchain/src/api.rs @@ -30,10 +30,18 @@ use primitives::offchain::{ Externalities as OffchainExt, HttpRequestId, Timestamp, HttpRequestStatus, HttpError, OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, StorageKind, }; +pub use offchain_primitives::STORAGE_PREFIX; use sr_primitives::{generic::BlockId, traits::{self, Extrinsic}}; use transaction_pool::txpool::{Pool, ChainApi}; +#[cfg(not(target_os = "unknown"))] mod http; + +#[cfg(target_os = "unknown")] +use http_dummy as http; +#[cfg(target_os = "unknown")] +mod http_dummy; + mod timestamp; /// A message between the offchain extension and the processing thread. @@ -64,7 +72,6 @@ fn unavailable_yet(name: &str) -> R { } const LOCAL_DB: &str = "LOCAL (fork-aware) DB"; -const STORAGE_PREFIX: &[u8] = b"storage"; impl OffchainExt for Api where @@ -317,12 +324,12 @@ impl AsyncApi { }, }; - info!("Submitting to the pool: {:?} (isSigned: {:?})", xt, xt.is_signed()); + info!("Submitting transaction to the pool: {:?} (isSigned: {:?})", xt, xt.is_signed()); future::Either::Right(self.transaction_pool .submit_one(&self.at, xt.clone()) .map(|result| match result { Ok(hash) => { debug!("[{:?}] Offchain transaction added to the pool.", hash); }, - Err(e) => { debug!("Couldn't submit transaction: {:?}", e); }, + Err(e) => { warn!("Couldn't submit offchain transaction: {:?}", e); }, })) } } diff --git a/core/offchain/src/api/http_dummy.rs b/core/offchain/src/api/http_dummy.rs new file mode 100644 index 0000000000000000000000000000000000000000..e3cb272a0d07583ac669a04d6ba6b1afb425bda3 --- /dev/null +++ b/core/offchain/src/api/http_dummy.rs @@ -0,0 +1,109 @@ +// 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 . + +//! Contains the same API as the `http` module, except that everything returns an error. + +use primitives::offchain::{HttpRequestId, Timestamp, HttpRequestStatus, HttpError}; +use std::{future::Future, pin::Pin, task::Context, task::Poll}; + +/// Creates a pair of [`HttpApi`] and [`HttpWorker`]. +pub fn http() -> (HttpApi, HttpWorker) { + (HttpApi, HttpWorker) +} + +/// Dummy implementation of HTTP capabilities. +#[derive(Debug)] +pub struct HttpApi; + +/// Dummy implementation of HTTP capabilities. +#[derive(Debug)] +pub struct HttpWorker; + +impl HttpApi { + /// Mimicks the corresponding method in the offchain API. + pub fn request_start( + &mut self, + _: &str, + _: &str + ) -> Result { + /// Because this always returns an error, none of the other methods should ever be called. + Err(()) + } + + /// Mimicks the corresponding method in the offchain API. + pub fn request_add_header( + &mut self, + _: HttpRequestId, + _: &str, + _: &str + ) -> Result<(), ()> { + unreachable!("Creating a request always fails, thus this function will \ + never be called; qed") + } + + /// Mimicks the corresponding method in the offchain API. + pub fn request_write_body( + &mut self, + _: HttpRequestId, + _: &[u8], + _: Option + ) -> Result<(), HttpError> { + unreachable!("Creating a request always fails, thus this function will \ + never be called; qed") + } + + /// Mimicks the corresponding method in the offchain API. + pub fn response_wait( + &mut self, + requests: &[HttpRequestId], + _: Option + ) -> Vec { + if requests.is_empty() { + Vec::new() + } else { + unreachable!("Creating a request always fails, thus the list of requests should \ + always be empty; qed") + } + } + + /// Mimicks the corresponding method in the offchain API. + pub fn response_headers( + &mut self, + _: HttpRequestId + ) -> Vec<(Vec, Vec)> { + unreachable!("Creating a request always fails, thus this function will \ + never be called; qed") + } + + /// Mimicks the corresponding method in the offchain API. + pub fn response_read_body( + &mut self, + _: HttpRequestId, + _: &mut [u8], + _: Option + ) -> Result { + unreachable!("Creating a request always fails, thus this function will \ + never be called; qed") + } +} + +impl Future for HttpWorker { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(()) + } +} diff --git a/core/offchain/src/lib.rs b/core/offchain/src/lib.rs index 79c6df04ea1099d9700ee51922214278c60e753d..69147295896bacca2eeb74dea1ef6b357337cbba 100644 --- a/core/offchain/src/lib.rs +++ b/core/offchain/src/lib.rs @@ -39,6 +39,8 @@ use std::{ sync::Arc, }; +use parking_lot::Mutex; +use threadpool::ThreadPool; use client::runtime_api::ApiExt; use futures::future::Future; use log::{debug, warn}; @@ -51,13 +53,14 @@ mod api; pub mod testing; -pub use offchain_primitives::OffchainWorkerApi; +pub use offchain_primitives::{OffchainWorkerApi, STORAGE_PREFIX}; /// An offchain workers manager. pub struct OffchainWorkers { client: Arc, db: Storage, _block: PhantomData, + thread_pool: Mutex, } impl OffchainWorkers { @@ -67,6 +70,7 @@ impl OffchainWorkers OffchainWorkers< debug!("Spawning offchain workers at {:?}", at); let number = *number; let client = self.client.clone(); - spawn_worker(move || { + self.spawn_worker(move || { let runtime = client.runtime_api(); let api = Box::new(api); debug!("Running offchain workers at {:?}", at); @@ -134,19 +138,18 @@ impl OffchainWorkers< futures::future::Either::Right(futures::future::ready(())) } } -} -/// Spawns a new offchain worker. -/// -/// We spawn offchain workers for each block in a separate thread, -/// since they can run for a significant amount of time -/// in a blocking fashion and we don't want to block the runtime. -/// -/// Note that we should avoid that if we switch to future-based runtime in the future, -/// alternatively: -/// TODO [ToDr] (#1458) we can consider using a thread pool instead. -fn spawn_worker(f: impl FnOnce() -> () + Send + 'static) { - std::thread::spawn(f); + /// Spawns a new offchain worker. + /// + /// We spawn offchain workers for each block in a separate thread, + /// since they can run for a significant amount of time + /// in a blocking fashion and we don't want to block the runtime. + /// + /// Note that we should avoid that if we switch to future-based runtime in the future, + /// alternatively: + fn spawn_worker(&self, f: impl FnOnce() -> () + Send + 'static) { + self.thread_pool.lock().execute(f); + } } #[cfg(test)] diff --git a/core/offchain/src/testing.rs b/core/offchain/src/testing.rs index e1cc7f71a38114abfef620b7844ea3a3b90faf92..3f4415efa7a1d01fabc73b25a4cd22c58bf19a17 100644 --- a/core/offchain/src/testing.rs +++ b/core/offchain/src/testing.rs @@ -119,7 +119,8 @@ impl State { impl Drop for State { fn drop(&mut self) { - if !self.expected_requests.is_empty() { + // If we panic! while we are already in a panic, the test dies with an illegal instruction. + if !self.expected_requests.is_empty() && !std::thread::panicking() { panic!("Unfulfilled expected requests: {:?}", self.expected_requests); } } diff --git a/core/peerset/Cargo.toml b/core/peerset/Cargo.toml index b11e59c77050e2f5e63c347077facd05a5b8ef8d..96b721b41caf0054c817555e65ed9b7855b1073e 100644 --- a/core/peerset/Cargo.toml +++ b/core/peerset/Cargo.toml @@ -13,7 +13,7 @@ libp2p = { version = "0.12.0", default-features = false } linked-hash-map = "0.5.2" log = "0.4.8" lru-cache = "0.1.2" -serde_json = "1.0.40" +serde_json = "1.0.41" [dev-dependencies] rand = "0.7.2" diff --git a/core/phragmen/Cargo.toml b/core/phragmen/Cargo.toml index e10be1d92d908129129043a4a68a8587564dc1fd..5ee6d3b3c24bcaec7f6e3cdad090dbc02c48a7ee 100644 --- a/core/phragmen/Cargo.toml +++ b/core/phragmen/Cargo.toml @@ -5,8 +5,9 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -sr-primitives = { path = "../sr-primitives", default-features = false } +serde = { version = "1.0.101", optional = true, features = ["derive"] } rstd = { package = "sr-std", path = "../sr-std", default-features = false } +sr-primitives = { path = "../sr-primitives", default-features = false } [dev-dependencies] runtime-io ={ package = "sr-io", path = "../sr-io" } @@ -16,6 +17,7 @@ rand = "0.7.2" [features] default = ["std"] std = [ + "serde", "rstd/std", "sr-primitives/std", ] diff --git a/core/phragmen/src/lib.rs b/core/phragmen/src/lib.rs index 5295d0f422506e570ee9bb02fb8dc742ddcd883b..a4287773b5aed57c407bf37525976900fa080d76 100644 --- a/core/phragmen/src/lib.rs +++ b/core/phragmen/src/lib.rs @@ -34,8 +34,9 @@ #![cfg_attr(not(feature = "std"), no_std)] use rstd::{prelude::*, collections::btree_map::BTreeMap}; -use sr_primitives::{helpers_128bit::multiply_by_rational_best_effort, Perbill, Rational128}; -use sr_primitives::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating}; +use sr_primitives::RuntimeDebug; +use sr_primitives::{helpers_128bit::multiply_by_rational, Perbill, Rational128}; +use sr_primitives::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bounded}; mod mock; mod tests; @@ -54,8 +55,7 @@ pub type ExtendedBalance = u128; const DEN: u128 = u128::max_value(); /// A candidate entity for phragmen election. -#[derive(Clone, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Default, RuntimeDebug)] pub struct Candidate { /// Identifier. pub who: AccountId, @@ -68,8 +68,7 @@ pub struct Candidate { } /// A voter entity. -#[derive(Clone, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Default, RuntimeDebug)] pub struct Voter { /// Identifier. who: AccountId, @@ -82,8 +81,7 @@ pub struct Voter { } /// A candidate being backed by a voter. -#[derive(Clone, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Default, RuntimeDebug)] pub struct Edge { /// Identifier. who: AccountId, @@ -100,7 +98,7 @@ pub type PhragmenAssignment = (AccountId, Perbill); pub type PhragmenStakedAssignment = (AccountId, ExtendedBalance); /// Final result of the phragmen election. -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(RuntimeDebug)] 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. @@ -117,8 +115,8 @@ pub struct PhragmenResult { /// /// This, at the current version, resembles the `Exposure` defined in the staking SRML module, yet /// they do not necessarily have to be the same. -#[derive(Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Default, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct Support { /// The amount of support as the effect of self-vote. pub own: ExtendedBalance, @@ -253,11 +251,11 @@ pub fn elect( for e in &n.edges { let c = &mut candidates[e.candidate_index]; if !c.elected && !c.approval_stake.is_zero() { - let temp_n = multiply_by_rational_best_effort( + let temp_n = multiply_by_rational( n.load.n(), n.budget, c.approval_stake, - ); + ).unwrap_or(Bounded::max_value()); let temp_d = n.load.d(); let temp = Rational128::from(temp_n, temp_d); c.score = c.score.lazy_saturating_add(temp); @@ -293,7 +291,9 @@ pub fn elect( let mut assignment = (n.who.clone(), vec![]); for e in &mut n.edges { if let Some(c) = elected_candidates.iter().cloned().find(|(c, _)| *c == e.who) { - if c.0 != n.who { + // if self_vote == false, this branch should always be executed as we want to + // collect all nominations + if c.0 != n.who || !self_vote { let per_bill_parts = { if n.load == e.load { @@ -303,11 +303,11 @@ pub fn elect( if e.load.d() == n.load.d() { // return e.load / n.load. let desired_scale: u128 = Perbill::accuracy().into(); - multiply_by_rational_best_effort( + multiply_by_rational( desired_scale, e.load.n(), n.load.n(), - ) + ).unwrap_or(Bounded::max_value()) } else { // defensive only. Both edge and nominator loads are built from // scores, hence MUST have the same denominator. @@ -363,6 +363,47 @@ pub fn elect( }) } +/// Build the support map from the given phragmen result. +pub fn build_support_map( + elected_stashes: &Vec, + assignments: &Vec<(AccountId, Vec>)>, + stake_of: FS, + assume_self_vote: bool, +) -> SupportMap where + AccountId: Default + Ord + Member, + Balance: Default + Copy + SimpleArithmetic, + C: Convert + Convert, + for<'r> FS: Fn(&'r AccountId) -> Balance, +{ + let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; + // Initialize the support of each candidate. + let mut supports = >::new(); + elected_stashes + .iter() + .map(|e| (e, if assume_self_vote { to_votes(stake_of(e)) } else { Zero::zero() } )) + .for_each(|(e, s)| { + let item = Support { own: s, total: s, ..Default::default() }; + supports.insert(e.clone(), item); + }); + + // build support struct. + for (n, assignment) in assignments.iter() { + for (c, per_thing) in assignment.iter() { + let nominator_stake = to_votes(stake_of(n)); + // AUDIT: it is crucially important for the `Mul` implementation of all + // per-things to be sound. + let other_stake = *per_thing * nominator_stake; + if let Some(support) = supports.get_mut(c) { + // For an astronomically rich validator with more astronomically rich + // set of nominators, this might saturate. + support.total = support.total.saturating_add(other_stake); + support.others.push((n.clone(), other_stake)); + } + } + } + supports +} + /// Performs equalize post-processing to the output of the election algorithm. This happens in /// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input /// parameters. diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index 461a8fe7e3d5a7777a34e9cd51208cce9a4449fb..30c1aab29fd7d7dd6ad816672535f3523fcf769c 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -8,12 +8,12 @@ edition = "2018" rstd = { package = "sr-std", path = "../sr-std", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } 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"] } twox-hash = { version = "1.5.0", optional = true } byteorder = { version = "1.3.2", default-features = false } primitive-types = { version = "0.5.1", default-features = false, features = ["codec"] } -impl-serde = { version = "0.2.1", optional = true } -log = { version = "0.4.8", optional = true } +impl-serde = { version = "0.2.3", optional = true } wasmi = { version = "0.5.1", optional = true } hash-db = { version = "0.15.2", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } @@ -31,6 +31,11 @@ num-traits = { version = "0.2.8", default-features = false } zeroize = "0.10.1" lazy_static = { version = "1.4.0", optional = true } parking_lot = { version = "0.9.0", optional = true } +libsecp256k1 = { version = "0.3.0", optional = true } +tiny-keccak = { version = "1.5.0", optional = true } +substrate-debug-derive = { version = "2.0.0", path = "./debug-derive" } +externalities = { package = "substrate-externalities", path = "../externalities", optional = true } +primitives-storage = { package = "substrate-primitives-storage", path = "storage", default-features = false } [dev-dependencies] substrate-serializer = { path = "../serializer" } @@ -40,7 +45,7 @@ rand = "0.7.2" criterion = "0.2.11" [[bench]] -name = "benches" +name = "bench" harness = false [lib] @@ -49,10 +54,10 @@ bench = false [features] default = ["std"] std = [ - "log", + "log/std", "wasmi", "lazy_static", - "parking_lot", + "parking_lot", "primitive-types/std", "primitive-types/serde", "primitive-types/byteorder", @@ -79,4 +84,9 @@ std = [ "schnorrkel", "regex", "num-traits/std", + "libsecp256k1", + "tiny-keccak", + "substrate-debug-derive/std", + "externalities", + "primitives-storage/std", ] diff --git a/core/primitives/benches/benches.rs b/core/primitives/benches/bench.rs similarity index 100% rename from core/primitives/benches/benches.rs rename to core/primitives/benches/bench.rs diff --git a/core/primitives/debug-derive/Cargo.toml b/core/primitives/debug-derive/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9c7b7aa1bad4908e5949e3f9ed667b153cdd62d7 --- /dev/null +++ b/core/primitives/debug-derive/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "substrate-debug-derive" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.2" +syn = "1.0.5" +proc-macro2 = "1.0" + +[features] +std = [] + +[dev-dependencies] + diff --git a/core/primitives/debug-derive/src/impls.rs b/core/primitives/debug-derive/src/impls.rs new file mode 100644 index 0000000000000000000000000000000000000000..decceca044c1b5ac9fda8d749b720db79ddd0382 --- /dev/null +++ b/core/primitives/debug-derive/src/impls.rs @@ -0,0 +1,217 @@ +// 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 . + +use quote::quote; +use proc_macro2::TokenStream; +use syn::{Data, DeriveInput, parse_quote}; + +pub fn debug_derive(ast: DeriveInput) -> proc_macro::TokenStream { + let name_str = ast.ident.to_string(); + let implementation = implementation::derive(&name_str, &ast.data); + let name = &ast.ident; + let mut generics = ast.generics.clone(); + let (impl_generics, ty_generics, where_clause) = { + let wh = generics.make_where_clause(); + for t in ast.generics.type_params() { + let name = &t.ident; + wh.predicates.push(parse_quote!{ #name : core::fmt::Debug }); + } + generics.split_for_impl() + }; + let gen = quote!{ + impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + #implementation + } + } + }; + + gen.into() +} + +#[cfg(not(feature = "std"))] +mod implementation { + use super::*; + + /// Derive the inner implementation of `Debug::fmt` function. + /// + /// Non-std environment. We do nothing to prevent bloating the size of runtime. + /// Implement `Printable` if you need to print the details. + pub fn derive(_name_str: &str, _data: &Data) -> TokenStream { + quote! { + fmt.write_str("") + } + } +} + +#[cfg(feature = "std")] +mod implementation { + use super::*; + use proc_macro2::Span; + use syn::{Ident, Index, token::SelfValue}; + + /// Derive the inner implementation of `Debug::fmt` function. + pub fn derive(name_str: &str, data: &Data) -> TokenStream { + match *data { + Data::Struct(ref s) => derive_struct(&name_str, &s.fields), + Data::Union(ref u) => derive_fields(&name_str, Fields::new(u.fields.named.iter(), None)), + Data::Enum(ref e) => derive_enum(&name_str, &e), + } + } + + enum Fields { + Indexed { + indices: Vec, + }, + Unnamed { + vars: Vec, + }, + Named { + names: Vec, + this: Option, + }, + } + + impl Fields { + fn new<'a>(fields: impl Iterator, this: Option) -> Self { + let mut indices = vec![]; + let mut names = vec![]; + + for (i, f) in fields.enumerate() { + if let Some(ident) = f.ident.clone() { + names.push(ident); + } else { + indices.push(Index::from(i)); + } + } + + if names.is_empty() { + Self::Indexed { + indices, + } + } else { + Self::Named { + names, + this, + } + } + } + } + + fn derive_fields<'a>( + name_str: &str, + fields: Fields, + ) -> TokenStream { + match fields { + Fields::Named { names, this } => { + let names_str: Vec<_> = names.iter() + .map(|x| x.to_string()) + .collect(); + + let fields = match this { + None => quote! { #( .field(#names_str, #names) )* }, + Some(this) => quote! { #( .field(#names_str, &#this.#names) )* }, + }; + + quote! { + fmt.debug_struct(#name_str) + #fields + .finish() + } + + }, + Fields::Indexed { indices } => { + quote! { + fmt.debug_tuple(#name_str) + #( .field(&self.#indices) )* + .finish() + } + }, + Fields::Unnamed { vars } => { + quote! { + fmt.debug_tuple(#name_str) + #( .field(#vars) )* + .finish() + } + }, + } + } + + fn derive_enum( + name: &str, + e: &syn::DataEnum, + ) -> TokenStream { + let v = e.variants + .iter() + .map(|v| { + let name = format!("{}::{}", name, v.ident); + let ident = &v.ident; + match v.fields { + syn::Fields::Named(ref f) => { + let names: Vec<_> = f.named.iter().flat_map(|f| f.ident.clone()).collect(); + let fields_impl = derive_fields(&name, Fields::Named { + names: names.clone(), + this: None, + }); + (ident, (quote!{ { #( ref #names ),* } }, fields_impl)) + }, + syn::Fields::Unnamed(ref f) => { + let names = f.unnamed.iter() + .enumerate() + .map(|(id, _)| Ident::new(&format!("a{}", id), Span::call_site())) + .collect::>(); + let fields_impl = derive_fields(&name, Fields::Unnamed { vars: names.clone() }); + (ident, (quote! { ( #( ref #names ),* ) }, fields_impl)) + }, + syn::Fields::Unit => { + let fields_impl = derive_fields(&name, Fields::Indexed { indices: vec![] }); + (ident, (quote! { }, fields_impl)) + }, + } + }); + + type Vecs = (Vec, Vec); + let (variants, others): Vecs<_, _> = v.unzip(); + let (match_fields, variants_impl): Vecs<_, _> = others.into_iter().unzip(); + + quote! { + match self { + #( Self::#variants #match_fields => #variants_impl, )* + _ => Ok(()), + } + } + } + + fn derive_struct( + name_str: &str, + fields: &syn::Fields, + ) -> TokenStream { + match *fields { + syn::Fields::Named(ref f) => derive_fields( + name_str, + Fields::new(f.named.iter(), Some(syn::Token!(self)(Span::call_site()))), + ), + syn::Fields::Unnamed(ref f) => derive_fields( + name_str, + Fields::new(f.unnamed.iter(), None), + ), + syn::Fields::Unit => derive_fields( + name_str, + Fields::Indexed { indices: vec![] }, + ), + } + } +} diff --git a/core/primitives/debug-derive/src/lib.rs b/core/primitives/debug-derive/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e6cf6098b30e4f0b69c7a3dd4c1a26b86e0832d --- /dev/null +++ b/core/primitives/debug-derive/src/lib.rs @@ -0,0 +1,44 @@ +// 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 . + +//! Macros to derive runtime debug implementation. +//! +//! This custom derive implements a `core::fmt::Debug` trait, +//! but in case the `std` feature is enabled the implementation +//! will actually print out the structure as regular `derive(Debug)` +//! would do. If `std` is disabled the implementation will be empty. +//! +//! This behaviour is useful to prevent bloating the runtime WASM +//! blob from unneeded code. +//! +//! ```rust +//! #[derive(substrate_debug_derive::RuntimeDebug)] +//! struct MyStruct; +//! +//! assert_eq!(format!("{:?}", MyStruct), "MyStruct"); +//! ``` + +extern crate proc_macro; + +mod impls; + +use proc_macro::TokenStream; + +#[proc_macro_derive(RuntimeDebug)] +pub fn debug_derive(input: TokenStream) -> TokenStream { + impls::debug_derive(syn::parse_macro_input!(input)) +} + diff --git a/core/primitives/debug-derive/tests/tests.rs b/core/primitives/debug-derive/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..63ad6a3e8d49718f8db818ee9cb4cd3370a9a8c6 --- /dev/null +++ b/core/primitives/debug-derive/tests/tests.rs @@ -0,0 +1,63 @@ +// 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 . + +use substrate_debug_derive::RuntimeDebug; + +#[derive(RuntimeDebug)] +struct Unnamed(u64, String); + +#[derive(RuntimeDebug)] +struct Named { + a: u64, + b: String, +} + +#[derive(RuntimeDebug)] +enum EnumLongName { + A, + B(A, String), + VariantLongName { + a: A, + b: String, + }, +} + + +#[test] +fn should_display_proper_debug() { + use self::EnumLongName as Enum; + + assert_eq!( + format!("{:?}", Unnamed(1, "abc".into())), + "Unnamed(1, \"abc\")" + ); + assert_eq!( + format!("{:?}", Named { a: 1, b: "abc".into() }), + "Named { a: 1, b: \"abc\" }" + ); + assert_eq!( + format!("{:?}", Enum::::A), + "EnumLongName::A" + ); + assert_eq!( + format!("{:?}", Enum::B(1, "abc".into())), + "EnumLongName::B(1, \"abc\")" + ); + assert_eq!( + format!("{:?}", Enum::VariantLongName { a: 1, b: "abc".into() }), + "EnumLongName::VariantLongName { a: 1, b: \"abc\" }" + ); +} diff --git a/core/primitives/src/child_storage_key.rs b/core/primitives/src/child_storage_key.rs deleted file mode 100644 index eba34c1ef9797a5f5e5f0e205682cfc17711bc05..0000000000000000000000000000000000000000 --- a/core/primitives/src/child_storage_key.rs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 . - -//! Provides a wrapper around a child storage key. - -use crate::storage::well_known_keys::is_child_trie_key_valid; -use rstd::{borrow::Cow, vec::Vec}; - -/// A wrapper around a child storage key. -/// -/// This wrapper ensures that the child storage key is correct and properly used. It is -/// impossible to create an instance of this struct without providing a correct `storage_key`. -pub struct ChildStorageKey<'a> { - storage_key: Cow<'a, [u8]>, -} - -impl<'a> ChildStorageKey<'a> { - fn new(storage_key: Cow<'a, [u8]>) -> Option { - if is_child_trie_key_valid(&storage_key) { - Some(ChildStorageKey { storage_key }) - } else { - None - } - } - - /// Create a new `ChildStorageKey` from a vector. - /// - /// `storage_key` need to start with `:child_storage:default:` - /// See `is_child_trie_key_valid` for more details. - pub fn from_vec(key: Vec) -> Option { - Self::new(Cow::Owned(key)) - } - - /// Create a new `ChildStorageKey` from a slice. - /// - /// `storage_key` need to start with `:child_storage:default:` - /// See `is_child_trie_key_valid` for more details. - pub fn from_slice(key: &'a [u8]) -> Option { - Self::new(Cow::Borrowed(key)) - } - - /// Get access to the byte representation of the storage key. - /// - /// This key is guaranteed to be correct. - pub fn as_ref(&self) -> &[u8] { - &*self.storage_key - } - - /// Destruct this instance into an owned vector that represents the storage key. - /// - /// This key is guaranteed to be correct. - pub fn into_owned(self) -> Vec { - self.storage_key.into_owned() - } -} diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index 8a336783b79652cafc74163f5a9d46159c5e2a7e..93f499b230d5a5e814c51457eb7403853d0d6dbf 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -43,7 +43,7 @@ pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold r pub const DEV_ADDRESS: &str = "5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV"; /// The infallible type. -#[derive(Debug)] +#[derive(crate::RuntimeDebug)] pub enum Infallible {} /// The length of the junction identifier. Note that this is also referred to as the @@ -255,7 +255,7 @@ pub enum PublicError { /// Key that can be encoded to/from SS58. #[cfg(feature = "std")] -pub trait Ss58Codec: Sized { +pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + Default { /// Some if the string is a properly encoded SS58Check address. fn from_ss58check(s: &str) -> Result { Self::from_ss58check_with_version(s) @@ -269,7 +269,23 @@ pub trait Ss58Codec: Sized { }) } /// Some if the string is a properly encoded SS58Check address. - fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError>; + fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { + let mut res = Self::default(); + let len = res.as_mut().len(); + let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding. + if d.len() != len + 3 { + // Invalid length. + return Err(PublicError::BadLength); + } + let ver = d[0].try_into().map_err(|_: ()| PublicError::UnknownVersion)?; + + if d[len + 1..len + 3] != ss58hash(&d[0..len + 1]).as_bytes()[0..2] { + // Invalid checksum. + return Err(PublicError::InvalidChecksum); + } + res.as_mut().copy_from_slice(&d[1..len + 1]); + Ok((res, ver)) + } /// Some if the string is a properly encoded SS58Check address, optionally with /// a derivation path following. fn from_string(s: &str) -> Result { @@ -285,7 +301,13 @@ pub trait Ss58Codec: Sized { } /// Return the ss58-check string for this key. - fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String; + fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String { + let mut v = vec![version.into()]; + v.extend(self.as_ref()); + let r = ss58hash(&v); + v.extend(&r.as_bytes()[0..2]); + v.to_base58() + } /// Return the ss58-check string for this key. fn to_ss58check(&self) -> String { self.to_ss58check_with_version(*DEFAULT_VERSION.lock()) } /// Some if the string is a properly encoded SS58Check address, optionally with @@ -408,44 +430,28 @@ pub fn set_default_ss58_version(version: Ss58AddressFormat) { } #[cfg(feature = "std")] -impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { - fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { - let mut res = T::default(); - let len = res.as_mut().len(); - let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding. - if d.len() != len + 3 { - // Invalid length. - return Err(PublicError::BadLength); - } - let ver = d[0].try_into().map_err(|_: ()| PublicError::UnknownVersion)?; - - if d[len+1..len+3] != ss58hash(&d[0..len+1]).as_bytes()[0..2] { - // Invalid checksum. - return Err(PublicError::InvalidChecksum); - } - res.as_mut().copy_from_slice(&d[1..len+1]); - Ok((res, ver)) - } - - fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String { - let mut v = vec![version.into()]; - v.extend(self.as_ref()); - let r = ss58hash(&v); - v.extend(&r.as_bytes()[0..2]); - v.to_base58() - } - +impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { fn from_string(s: &str) -> Result { - let re = Regex::new(r"^(?P[\w\d]+)?(?P(//?[^/]+)*)$") + let re = Regex::new(r"^(?P[\w\d ]+)?(?P(//?[^/]+)*)$") .expect("constructed from known-good static value; qed"); let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?; let re_junction = Regex::new(r"/(/?[^/]+)") .expect("constructed from known-good static value; qed"); - let addr = Self::from_ss58check( - cap.name("ss58") - .map(|r| r.as_str()) - .unwrap_or(DEV_ADDRESS) - )?; + let s = cap.name("ss58") + .map(|r| r.as_str()) + .unwrap_or(DEV_ADDRESS); + let addr = if s.starts_with("0x") { + let d = hex::decode(&s[2..]).map_err(|_| PublicError::InvalidFormat)?; + let mut r = Self::default(); + if d.len() == r.as_ref().len() { + r.as_mut().copy_from_slice(&d); + r + } else { + Err(PublicError::BadLength)? + } + } else { + Self::from_ss58check(s)? + }; if cap["path"].is_empty() { Ok(addr) } else { @@ -457,7 +463,7 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { } fn from_string_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { - let re = Regex::new(r"^(?P[\w\d]+)?(?P(//?[^/]+)*)$") + let re = Regex::new(r"^(?P[\w\d ]+)?(?P(//?[^/]+)*)$") .expect("constructed from known-good static value; qed"); let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?; let re_junction = Regex::new(r"/(/?[^/]+)") @@ -495,6 +501,103 @@ pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + Pa fn as_slice(&self) -> &[u8] { self.as_ref() } } +/// An opaque 32-byte cryptographic identifier. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Default, Encode, Decode)] +pub struct AccountId32([u8; 32]); + +impl UncheckedFrom for AccountId32 { + fn unchecked_from(h: crate::hash::H256) -> Self { + AccountId32(h.into()) + } +} + +#[cfg(feature = "std")] +impl Ss58Codec for AccountId32 {} + +impl AsRef<[u8]> for AccountId32 { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for AccountId32 { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl AsRef<[u8; 32]> for AccountId32 { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl AsMut<[u8; 32]> for AccountId32 { + fn as_mut(&mut self) -> &mut [u8; 32] { + &mut self.0 + } +} + +impl From<[u8; 32]> for AccountId32 { + fn from(x: [u8; 32]) -> AccountId32 { + AccountId32(x) + } +} + +impl<'a> rstd::convert::TryFrom<&'a [u8]> for AccountId32 { + type Error = (); + fn try_from(x: &'a [u8]) -> Result { + if x.len() == 32 { + let mut r = AccountId32::default(); + r.0.copy_from_slice(x); + Ok(r) + } else { + Err(()) + } + } +} + +impl From for [u8; 32] { + fn from(x: AccountId32) -> [u8; 32] { + x.0 + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for AccountId32 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_ss58check()) + } +} + +impl rstd::fmt::Debug for AccountId32 { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + let s = self.to_ss58check(); + write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } +} + +#[cfg(feature = "std")] +impl serde::Serialize for AccountId32 { + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { + serializer.serialize_str(&self.to_ss58check()) + } +} + +#[cfg(feature = "std")] +impl<'de> serde::Deserialize<'de> for AccountId32 { + fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { + Ss58Codec::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| serde::de::Error::custom(format!("{:?}", e))) + } +} + #[cfg(feature = "std")] pub use self::dummy::*; @@ -544,17 +647,10 @@ mod dummy { Ok(Default::default()) } fn derive< - Iter: Iterator - >(&self, _: Iter) -> Result { Ok(Self) } + Iter: Iterator, + >(&self, _: Iter, _: Option) -> Result<(Self, Option), Self::DeriveError> { Ok((Self, None)) } fn from_seed(_: &Self::Seed) -> Self { Self } fn from_seed_slice(_: &[u8]) -> Result { Ok(Self) } - fn from_standard_components< - I: Iterator - >( - _: &str, - _: Option<&str>, - _: I - ) -> Result { Ok(Self) } fn sign(&self, _: &[u8]) -> Self::Signature { Self } fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true } fn verify_weak, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { true } @@ -604,7 +700,10 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Self, Self::Seed), SecretStringError>; /// Derive a child key from a series of given junctions. - fn derive>(&self, path: Iter) -> Result; + fn derive>(&self, + path: Iter, + seed: Option, + ) -> Result<(Self, Option), Self::DeriveError>; /// Generate new key pair from the provided `seed`. /// @@ -619,11 +718,6 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// by an attacker then they can also derive your key. fn from_seed_slice(seed: &[u8]) -> Result; - /// Construct a key from a phrase, password and path. - fn from_standard_components< - I: Iterator - >(phrase: &str, password: Option<&str>, path: I) -> Result; - /// Sign a message. fn sign(&self, message: &[u8]) -> Self::Signature; @@ -636,7 +730,9 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// Get the public key. fn public(&self) -> Self::Public; - /// Interprets the string `s` in order to generate a key Pair. + /// Interprets the string `s` in order to generate a key Pair. Returns both the pair and an optional seed, in the + /// case that the pair can be expressed as a direct derivation from a seed (some cases, such as Sr25519 derivations + /// with path components, cannot). /// /// This takes a helper function to do the key generation from a phrase, password and /// junction iterator. @@ -662,31 +758,43 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// be equivalent to no password at all. /// /// `None` is returned if no matches are found. - fn from_string(s: &str, password_override: Option<&str>) -> Result { - let hex_seed = if s.starts_with("0x") { - &s[2..] - } else { - s - }; - - if let Ok(d) = hex::decode(hex_seed) { - if let Ok(r) = Self::from_seed_slice(&d) { - return Ok(r) - } - } - - let re = Regex::new(r"^(?P\w+( \w+)*)?(?P(//?[^/]+)*)(///(?P.*))?$") + fn from_string_with_seed(s: &str, password_override: Option<&str>) -> Result<(Self, Option), SecretStringError> { + let re = Regex::new(r"^(?P[\d\w ]+)?(?P(//?[^/]+)*)(///(?P.*))?$") .expect("constructed from known-good static value; qed"); let cap = re.captures(s).ok_or(SecretStringError::InvalidFormat)?; + let re_junction = Regex::new(r"/(/?[^/]+)") .expect("constructed from known-good static value; qed"); let path = re_junction.captures_iter(&cap["path"]) .map(|f| DeriveJunction::from(&f[1])); - Self::from_standard_components( - cap.name("phrase").map(|r| r.as_str()).unwrap_or(DEV_PHRASE), - password_override.or_else(|| cap.name("password").map(|m| m.as_str())), - path, - ) + + let phrase = cap.name("phrase").map(|r| r.as_str()).unwrap_or(DEV_PHRASE); + let password = password_override.or_else(|| cap.name("password").map(|m| m.as_str())); + + let (root, seed) = if phrase.starts_with("0x") { + hex::decode(&phrase[2..]).ok() + .and_then(|seed_vec| { + let mut seed = Self::Seed::default(); + if seed.as_ref().len() == seed_vec.len() { + seed.as_mut().copy_from_slice(&seed_vec); + Some((Self::from_seed(&seed), seed)) + } else { + None + } + }) + .ok_or(SecretStringError::InvalidSeed)? + } else { + Self::from_phrase(phrase, password) + .map_err(|_| SecretStringError::InvalidPhrase)? + }; + root.derive(path, Some(seed)).map_err(|_| SecretStringError::InvalidPath) + } + + /// Interprets the string `s` in order to generate a key pair. + /// + /// See [`from_string_with_seed`](Self::from_string_with_seed) for more extensive documentation. + fn from_string(s: &str, password_override: Option<&str>) -> Result { + Self::from_string_with_seed(s, password_override).map(|x| x.0) } /// Return a vec filled with raw data. @@ -743,7 +851,7 @@ pub trait CryptoType { /// Values whose first character is `_` are reserved for private use and won't conflict with any /// public modules. #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub struct KeyTypeId(pub [u8; 4]); impl From for KeyTypeId { @@ -790,7 +898,6 @@ pub mod key_types { /// Key type for ImOnline module, built-in. pub const IM_ONLINE: KeyTypeId = KeyTypeId(*b"imon"); /// A key type ID useful for tests. - #[cfg(feature = "std")] pub const DUMMY: KeyTypeId = KeyTypeId(*b"dumy"); } @@ -846,13 +953,13 @@ mod tests { } impl Pair for TestPair { type Public = TestPublic; - type Seed = [u8; 0]; + type Seed = [u8; 8]; type Signature = [u8; 0]; type DeriveError = (); - fn generate() -> (Self, ::Seed) { (TestPair::Generated, []) } + fn generate() -> (Self, ::Seed) { (TestPair::Generated, [0u8; 8]) } fn generate_with_phrase(_password: Option<&str>) -> (Self, String, ::Seed) { - (TestPair::GeneratedWithPhrase, "".into(), []) + (TestPair::GeneratedWithPhrase, "".into(), [0u8; 8]) } fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Self, ::Seed), SecretStringError> @@ -860,14 +967,20 @@ mod tests { Ok((TestPair::GeneratedFromPhrase { phrase: phrase.to_owned(), password: password.map(Into::into) - }, [])) + }, [0u8; 8])) } - fn derive>(&self, _path: Iter) - -> Result + fn derive>(&self, path_iter: Iter, _: Option<[u8; 8]>) + -> Result<(Self, Option<[u8; 8]>), Self::DeriveError> { - Err(()) + Ok((match self.clone() { + TestPair::Standard {phrase, password, path} => + TestPair::Standard { phrase, password, path: path.into_iter().chain(path_iter).collect() }, + TestPair::GeneratedFromPhrase {phrase, password} => + TestPair::Standard { phrase, password, path: path_iter.collect() }, + x => if path_iter.count() == 0 { x } else { return Err(()) }, + }, None)) } - fn from_seed(_seed: &::Seed) -> Self { TestPair::Seed(vec![]) } + fn from_seed(_seed: &::Seed) -> Self { TestPair::Seed(_seed.as_ref().to_owned()) } fn sign(&self, _message: &[u8]) -> Self::Signature { [] } fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true } fn verify_weak, M: AsRef<[u8]>>( @@ -876,17 +989,6 @@ mod tests { _pubkey: P ) -> bool { true } fn public(&self) -> Self::Public { TestPublic } - fn from_standard_components>( - phrase: &str, - password: Option<&str>, - path: I - ) -> Result { - Ok(TestPair::Standard { - phrase: phrase.to_owned(), - password: password.map(ToOwned::to_owned), - path: path.collect() - }) - } fn from_seed_slice(seed: &[u8]) -> Result { @@ -903,10 +1005,6 @@ mod tests { TestPair::from_string("0x0123456789abcdef", None), Ok(TestPair::Seed(hex!["0123456789abcdef"][..].to_owned())) ); - assert_eq!( - TestPair::from_string("0123456789abcdef", None), - Ok(TestPair::Seed(hex!["0123456789abcdef"][..].to_owned())) - ); } #[test] diff --git a/core/primitives/src/ecdsa.rs b/core/primitives/src/ecdsa.rs new file mode 100644 index 0000000000000000000000000000000000000000..abff460c91034613c982b46b7d16d33d6113404d --- /dev/null +++ b/core/primitives/src/ecdsa.rs @@ -0,0 +1,608 @@ +// Copyright 2017-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 . + +// tag::description[] +//! Simple ECDSA API. +// end::description[] + +use rstd::cmp::Ordering; +use codec::{Encode, Decode}; + +#[cfg(feature = "std")] +use std::convert::{TryFrom, TryInto}; +#[cfg(feature = "std")] +use substrate_bip39::seed_from_entropy; +#[cfg(feature = "std")] +use bip39::{Mnemonic, Language, MnemonicType}; +#[cfg(feature = "std")] +use crate::{hashing::blake2_256, crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Ss58Codec}}; +#[cfg(feature = "std")] +use serde::{de, Serializer, Serialize, Deserializer, Deserialize}; +use crate::crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}; +#[cfg(feature = "std")] +use secp256k1::{PublicKey, SecretKey}; + +/// A secret seed (which is bytewise essentially equivalent to a SecretKey). +/// +/// We need it as a different type because `Seed` is expected to be AsRef<[u8]>. +#[cfg(feature = "std")] +type Seed = [u8; 32]; + +/// The ECDSA 33-byte compressed public key. +#[derive(Clone, Encode, Decode)] +pub struct Public(pub [u8; 33]); + +impl PartialOrd for Public { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Public { + fn cmp(&self, other: &Self) -> Ordering { + self.0[..].cmp(&other.0[..]) + } +} + +impl PartialEq for Public { + fn eq(&self, other: &Self) -> bool { + &self.0[..] == &other.0[..] + } +} + +impl Eq for Public {} + +impl Default for Public { + fn default() -> Self { + Public([0u8; 33]) + } +} + +/// A key pair. +#[cfg(feature = "std")] +#[derive(Clone)] +pub struct Pair { + public: PublicKey, + secret: SecretKey, +} + +impl AsRef<[u8; 33]> for Public { + fn as_ref(&self) -> &[u8; 33] { + &self.0 + } +} + +impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Public { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl rstd::convert::TryFrom<&[u8]> for Public { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 33 { + let mut inner = [0u8; 33]; + inner.copy_from_slice(data); + Ok(Public(inner)) + } else { + Err(()) + } + } +} + +impl From for [u8; 33] { + fn from(x: Public) -> Self { + x.0 + } +} + +#[cfg(feature = "std")] +impl From for Public { + fn from(x: Pair) -> Self { + x.public() + } +} + +impl UncheckedFrom<[u8; 33]> for Public { + fn unchecked_from(x: [u8; 33]) -> Self { + Public(x) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_ss58check()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let s = self.to_ss58check(); + write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&&self.0[..]), &s[0..8]) + } +} + +#[cfg(feature = "std")] +impl Serialize for Public { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + serializer.serialize_str(&self.to_ss58check()) + } +} + +#[cfg(feature = "std")] +impl<'de> Deserialize<'de> for Public { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + Public::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } +} + +#[cfg(feature = "std")] +impl std::hash::Hash for Public { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +/// A signature (a 512-bit value). +#[derive(Encode, Decode)] +pub struct Signature([u8; 65]); + +impl rstd::convert::TryFrom<&[u8]> for Signature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 65 { + let mut inner = [0u8; 65]; + inner.copy_from_slice(data); + Ok(Signature(inner)) + } else { + Err(()) + } + } +} + +impl Clone for Signature { + fn clone(&self) -> Self { + let mut r = [0u8; 65]; + r.copy_from_slice(&self.0[..]); + Signature(r) + } +} + +impl Default for Signature { + fn default() -> Self { + Signature([0u8; 65]) + } +} + +impl PartialEq for Signature { + fn eq(&self, b: &Self) -> bool { + self.0[..] == b.0[..] + } +} + +impl Eq for Signature {} + +impl From for [u8; 65] { + fn from(v: Signature) -> [u8; 65] { + v.0 + } +} + +impl AsRef<[u8; 65]> for Signature { + fn as_ref(&self) -> &[u8; 65] { + &self.0 + } +} + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Signature { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) + } +} + +#[cfg(feature = "std")] +impl std::hash::Hash for Signature { + fn hash(&self, state: &mut H) { + std::hash::Hash::hash(&self.0[..], state); + } +} + +impl Signature { + /// A new instance from the given 65-byte `data`. + /// + /// NOTE: No checking goes on to ensure this is a real signature. Only use it if + /// you are certain that the array actually is a signature. GIGO! + pub fn from_raw(data: [u8; 65]) -> Signature { + Signature(data) + } + + /// Recover the public key from this signature and a message. + #[cfg(feature = "std")] + pub fn recover>(&self, message: M) -> Option { + let message = secp256k1::Message::parse(&blake2_256(message.as_ref())); + let sig: (_, _) = self.try_into().ok()?; + secp256k1::recover(&message, &sig.0, &sig.1).ok() + .map(|recovered| Public(recovered.serialize_compressed())) + } +} + +#[cfg(feature = "std")] +impl From<(secp256k1::Signature, secp256k1::RecoveryId)> for Signature { + fn from(x: (secp256k1::Signature, secp256k1::RecoveryId)) -> Signature { + let mut r = Self::default(); + r.0[0..64].copy_from_slice(&x.0.serialize()[..]); + r.0[64] = x.1.serialize(); + r + } +} + +#[cfg(feature = "std")] +impl<'a> TryFrom<&'a Signature> for (secp256k1::Signature, secp256k1::RecoveryId) { + type Error = (); + fn try_from(x: &'a Signature) -> Result<(secp256k1::Signature, secp256k1::RecoveryId), Self::Error> { + Ok(( + secp256k1::Signature::parse_slice(&x.0[0..64]).expect("hardcoded to 64 bytes; qed"), + secp256k1::RecoveryId::parse(x.0[64]).map_err(|_| ())?, + )) + } +} + +/// An error type for SS58 decoding. +#[cfg(feature = "std")] +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum PublicError { + /// Bad alphabet. + BadBase58, + /// Bad length. + BadLength, + /// Unknown version. + UnknownVersion, + /// Invalid checksum. + InvalidChecksum, +} + +impl Public { + /// A new instance from the given 33-byte `data`. + /// + /// NOTE: No checking goes on to ensure this is a real public key. Only use it if + /// you are certain that the array actually is a pubkey. GIGO! + pub fn from_raw(data: [u8; 33]) -> Self { + Public(data) + } + + /// Return a slice filled with raw data. + pub fn as_array_ref(&self) -> &[u8; 33] { + self.as_ref() + } +} + +impl TraitPublic for Public { + /// A new instance from the given slice that should be 33 bytes long. + /// + /// NOTE: No checking goes on to ensure this is a real public key. Only use it if + /// you are certain that the array actually is a pubkey. GIGO! + fn from_slice(data: &[u8]) -> Self { + let mut r = [0u8; 33]; + r.copy_from_slice(data); + Public(r) + } +} + +impl Derive for Public {} + +/// Derive a single hard junction. +#[cfg(feature = "std")] +fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { + ("Secp256k1HDKD", secret_seed, cc).using_encoded(|data| { + let mut res = [0u8; 32]; + res.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes()); + res + }) +} + +/// An error when deriving a key. +#[cfg(feature = "std")] +pub enum DeriveError { + /// A soft key was found in the path (and is unsupported). + SoftKeyInPath, +} + +#[cfg(feature = "std")] +impl TraitPair for Pair { + type Public = Public; + type Seed = Seed; + type Signature = Signature; + type DeriveError = DeriveError; + + /// Generate new secure (random) key pair and provide the recovery phrase. + /// + /// You can recover the same key later with `from_phrase`. + fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let phrase = mnemonic.phrase(); + let (pair, seed) = Self::from_phrase(phrase, password) + .expect("All phrases generated by Mnemonic are valid; qed"); + ( + pair, + phrase.to_owned(), + seed, + ) + } + + /// Generate key pair from given recovery phrase and password. + fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Pair, Seed), SecretStringError> { + let big_seed = seed_from_entropy( + Mnemonic::from_phrase(phrase, Language::English) + .map_err(|_| SecretStringError::InvalidPhrase)?.entropy(), + password.unwrap_or(""), + ).map_err(|_| SecretStringError::InvalidSeed)?; + let mut seed = Seed::default(); + seed.copy_from_slice(&big_seed[0..32]); + Self::from_seed_slice(&big_seed[0..32]).map(|x| (x, seed)) + } + + /// Make a new key pair from secret seed material. + /// + /// 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") + } + + /// Make a new key pair from secret seed material. The slice must be 32 bytes long or it + /// will return `None`. + /// + /// You should never need to use this; generate(), generate_with_phrase + fn from_seed_slice(seed_slice: &[u8]) -> Result { + let secret = SecretKey::parse_slice(seed_slice) + .map_err(|_| SecretStringError::InvalidSeedLength)?; + let public = PublicKey::from_secret_key(&secret); + Ok(Pair{ secret, public }) + } + + /// Derive a child key from a series of given junctions. + fn derive>(&self, + path: Iter, + _seed: Option + ) -> Result<(Pair, Option), DeriveError> { + let mut acc = self.secret.serialize(); + for j in path { + match j { + DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath), + DeriveJunction::Hard(cc) => acc = derive_hard_junction(&acc, &cc), + } + } + Ok((Self::from_seed(&acc), Some(acc))) + } + + /// Get the public key. + fn public(&self) -> Public { + Public(self.public.serialize_compressed()) + } + + /// Sign a message. + fn sign(&self, message: &[u8]) -> Signature { + let message = secp256k1::Message::parse(&blake2_256(message)); + secp256k1::sign(&message, &self.secret).into() + } + + /// Verify a signature on a message. Returns true if the signature is good. + fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { + let message = secp256k1::Message::parse(&blake2_256(message.as_ref())); + let sig: (_, _) = match sig.try_into() { Ok(x) => x, _ => return false }; + match secp256k1::recover(&message, &sig.0, &sig.1) { + Ok(actual) => &pubkey.0[..] == &actual.serialize_compressed()[..], + _ => false, + } + } + + /// Verify a signature on a message. Returns true if the signature is good. + /// + /// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct + /// size. Use it only if you're coming from byte buffers and need the speed. + fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { + let message = secp256k1::Message::parse(&blake2_256(message.as_ref())); + if sig.len() != 65 { return false } + let ri = match secp256k1::RecoveryId::parse(sig[64]) { Ok(x) => x, _ => return false }; + let sig = match secp256k1::Signature::parse_slice(&sig[0..64]) { Ok(x) => x, _ => return false }; + match secp256k1::recover(&message, &sig, &ri) { + Ok(actual) => pubkey.as_ref() == &actual.serialize_compressed()[..], + _ => false, + } + } + + /// Return a vec filled with raw data. + fn to_raw_vec(&self) -> Vec { + self.seed().to_vec() + } +} + +#[cfg(feature = "std")] +impl Pair { + /// Get the seed for this key. + pub fn seed(&self) -> Seed { + self.secret.serialize() + } + + /// Exactly as `from_string` except that if no matches are found then, the the first 32 + /// characters are taken (padded with spaces as necessary) and used as the MiniSecretKey. + pub fn from_legacy_string(s: &str, password_override: Option<&str>) -> Pair { + Self::from_string(s, password_override).unwrap_or_else(|_| { + let mut padded_seed: Seed = [' ' as u8; 32]; + let len = s.len().min(32); + padded_seed[..len].copy_from_slice(&s.as_bytes()[..len]); + Self::from_seed(&padded_seed) + }) + } +} + +impl CryptoType for Public { + #[cfg(feature="std")] + type Pair = Pair; +} + +impl CryptoType for Signature { + #[cfg(feature="std")] + type Pair = Pair; +} + +#[cfg(feature = "std")] +impl CryptoType for Pair { + type Pair = Pair; +} + +#[cfg(test)] +mod test { + use super::*; + use hex_literal::hex; + use crate::crypto::DEV_PHRASE; + + #[test] + fn default_phrase_should_be_used() { + assert_eq!( + Pair::from_string("//Alice///password", None).unwrap().public(), + Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password")).unwrap().public(), + ); + } + + #[test] + fn seed_and_derive_should_work() { + let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"); + let pair = Pair::from_seed(&seed); + assert_eq!(pair.seed(), seed); + let path = vec![DeriveJunction::Hard([0u8; 32])]; + let derived = pair.derive(path.into_iter(), None).ok().unwrap(); + assert_eq!( + derived.0.seed(), + hex!("b8eefc4937200a8382d00050e050ced2d4ab72cc2ef1b061477afb51564fdd61") + ); + } + + #[test] + fn test_vector_should_work() { + let pair = Pair::from_seed( + &hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60") + ); + let public = pair.public(); + assert_eq!(public, Public::from_raw( + hex!("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91") + )); + let message = b""; + let signature = hex!("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00"); + let signature = Signature::from_raw(signature); + assert!(&pair.sign(&message[..]) == &signature); + assert!(Pair::verify(&signature, &message[..], &public)); + } + + #[test] + fn test_vector_by_string_should_work() { + let pair = Pair::from_string( + "0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + None + ).unwrap(); + let public = pair.public(); + assert_eq!(public, Public::from_raw( + hex!("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91") + )); + let message = b""; + let signature = hex!("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00"); + let signature = Signature::from_raw(signature); + assert!(&pair.sign(&message[..]) == &signature); + assert!(Pair::verify(&signature, &message[..], &public)); + } + + #[test] + fn generated_pair_should_work() { + let (pair, _) = Pair::generate(); + let public = pair.public(); + let message = b"Something important"; + let signature = pair.sign(&message[..]); + assert!(Pair::verify(&signature, &message[..], &public)); + assert!(!Pair::verify(&signature, b"Something else", &public)); + } + + #[test] + fn seeded_pair_should_work() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + assert_eq!(public, Public::from_raw( + hex!("035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9") + )); + let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"); + let signature = pair.sign(&message[..]); + println!("Correct signature: {:?}", signature); + assert!(Pair::verify(&signature, &message[..], &public)); + assert!(!Pair::verify(&signature, "Other message", &public)); + } + + #[test] + fn generate_with_phrase_recovery_possible() { + let (pair1, phrase, _) = Pair::generate_with_phrase(None); + let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); + + assert_eq!(pair1.public(), pair2.public()); + } + + #[test] + fn generate_with_password_phrase_recovery_possible() { + let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); + let (pair2, _) = Pair::from_phrase(&phrase, Some("password")).unwrap(); + + assert_eq!(pair1.public(), pair2.public()); + } + + #[test] + fn password_does_something() { + let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); + let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); + + assert_ne!(pair1.public(), pair2.public()); + } + + #[test] + fn ss58check_roundtrip_works() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let s = public.to_ss58check(); + println!("Correct: {}", s); + let cmp = Public::from_ss58check(&s).unwrap(); + assert_eq!(cmp, public); + } +} diff --git a/core/primitives/src/ed25519.rs b/core/primitives/src/ed25519.rs index c40499a3a182e84d33a9e21ee30411047232f0bf..8c3aa9f89dbdc0de8375bb16d8866ee546726e8a 100644 --- a/core/primitives/src/ed25519.rs +++ b/core/primitives/src/ed25519.rs @@ -41,6 +41,7 @@ use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}}; type Seed = [u8; 32]; /// A public key. +#[cfg_attr(feature = "std", derive(Hash))] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default)] pub struct Public(pub [u8; 32]); @@ -129,12 +130,17 @@ impl std::fmt::Display for Public { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl rstd::fmt::Debug for Public { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { let s = self.to_ss58check(); write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } #[cfg(feature = "std")] @@ -152,13 +158,6 @@ impl<'de> Deserialize<'de> for Public { } } -#[cfg(feature = "std")] -impl std::hash::Hash for Public { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - /// A signature (a 512-bit value). #[derive(Encode, Decode)] pub struct Signature(pub [u8; 64]); @@ -229,11 +228,16 @@ impl AsMut<[u8]> for Signature { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for Signature { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl rstd::fmt::Debug for Signature { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } #[cfg(feature = "std")] @@ -402,7 +406,10 @@ impl TraitPair for Pair { } /// Derive a child key from a series of given junctions. - fn derive>(&self, path: Iter) -> Result { + fn derive>(&self, + path: Iter, + _seed: Option, + ) -> Result<(Pair, Option), DeriveError> { let mut acc = self.0.secret.to_bytes(); for j in path { match j { @@ -410,18 +417,7 @@ impl TraitPair for Pair { DeriveJunction::Hard(cc) => acc = derive_hard_junction(&acc, &cc), } } - Ok(Self::from_seed(&acc)) - } - - /// Generate a key from the phrase, password and derivation path. - fn from_standard_components>( - phrase: &str, - password: Option<&str>, - path: I - ) -> Result { - Self::from_phrase(phrase, password)?.0 - .derive(path) - .map_err(|_| SecretStringError::InvalidPath) + Ok((Self::from_seed(&acc), Some(acc))) } /// Get the public key. @@ -524,7 +520,7 @@ mod test { let pair = Pair::from_seed(&seed); assert_eq!(pair.seed(), &seed); let path = vec![DeriveJunction::Hard([0u8; 32])]; - let derived = pair.derive(path.into_iter()).ok().unwrap(); + let derived = pair.derive(path.into_iter(), None).ok().unwrap().0; assert_eq!( derived.seed(), &hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c") diff --git a/core/primitives/src/hexdisplay.rs b/core/primitives/src/hexdisplay.rs index 87be587a7d92d0a6a214974a3b591d685bd2cf9f..2c8533e25b5f88808d2e973bc2223fe3dc534ae4 100644 --- a/core/primitives/src/hexdisplay.rs +++ b/core/primitives/src/hexdisplay.rs @@ -24,8 +24,8 @@ impl<'a> HexDisplay<'a> { pub fn from(d: &'a R) -> Self { HexDisplay(d.as_bytes_ref()) } } -impl<'a> ::core::fmt::Display for HexDisplay<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { +impl<'a> rstd::fmt::Display for HexDisplay<'a> { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> Result<(), rstd::fmt::Error> { if self.0.len() < 1027 { for byte in self.0 { f.write_fmt(format_args!("{:02x}", byte))?; @@ -43,8 +43,8 @@ impl<'a> ::core::fmt::Display for HexDisplay<'a> { } } -impl<'a> core::fmt::Debug for HexDisplay<'a> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { +impl<'a> rstd::fmt::Debug for HexDisplay<'a> { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> Result<(), rstd::fmt::Error> { for byte in self.0 { f.write_fmt(format_args!("{:02x}", byte))?; } @@ -80,7 +80,7 @@ macro_rules! impl_non_endians { impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], - [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); + [u8; 48], [u8; 56], [u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); /// Format into ASCII + # + hex, suitable for storage key preimages. pub fn ascii_format(asciish: &[u8]) -> String { diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 8086c9832a357158714818abda888c6529c8ae88..02f28c797c0027b4a42d30c160e2ff95b36d56e7 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -26,9 +26,9 @@ /// Can be used to create a `HashMap`. #[macro_export] macro_rules! map { - ($( $name:expr => $value:expr ),*) => ( + ($( $name:expr => $value:expr ),* $(,)? ) => ( vec![ $( ( $name, $value ) ),* ].into_iter().collect() - ) + ); } use rstd::prelude::*; @@ -39,8 +39,11 @@ use std::borrow::Cow; use serde::{Serialize, Deserialize}; #[cfg(feature = "std")] pub use serde;// << for macro +#[doc(hidden)] pub use codec::{Encode, Decode};// << for macro +pub use substrate_debug_derive::RuntimeDebug; + #[cfg(feature = "std")] pub use impl_serde::serialize as bytes; @@ -54,16 +57,16 @@ pub mod crypto; pub mod u32_trait; -pub mod child_storage_key; pub mod ed25519; pub mod sr25519; +pub mod ecdsa; pub mod hash; mod hasher; pub mod offchain; pub mod sandbox; -pub mod storage; pub mod uint; mod changes_trie; +#[cfg(feature = "std")] pub mod traits; pub mod testing; @@ -81,6 +84,11 @@ pub use hash_db::Hasher; // pub use self::hasher::blake::BlakeHasher; pub use self::hasher::blake2::Blake2Hasher; +pub use primitives_storage as storage; + +#[doc(hidden)] +pub use rstd; + /// Context for executing a call into the runtime. pub enum ExecutionContext { /// Context for general importing (including own blocks). @@ -111,8 +119,8 @@ impl ExecutionContext { } /// Hex-serialized shim for `Vec`. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord))] pub struct Bytes(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); impl From> for Bytes { @@ -157,8 +165,8 @@ pub enum NativeOrEncoded { } #[cfg(feature = "std")] -impl ::std::fmt::Debug for NativeOrEncoded { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl rstd::fmt::Debug for NativeOrEncoded { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { hexdisplay::HexDisplay::from(&self.as_encoded().as_ref()).fmt(f) } } @@ -219,3 +227,85 @@ impl codec::Decode for NeverNativeValue { } } +/// Provide a simple 4 byte identifier for a type. +pub trait TypeId { + /// Simple 4 byte identifier. + const TYPE_ID: [u8; 4]; +} + +/// A log level matching the one from `log` crate. +/// +/// Used internally by `runtime_io::log` method. +#[repr(u32)] +pub enum LogLevel { + /// `Error` log level. + Error = 1, + /// `Warn` log level. + Warn = 2, + /// `Info` log level. + Info = 3, + /// `Debug` log level. + Debug = 4, + /// `Trace` log level. + Trace = 5, +} + +impl From for LogLevel { + fn from(val: u32) -> Self { + match val { + x if x == LogLevel::Warn as u32 => LogLevel::Warn, + x if x == LogLevel::Info as u32 => LogLevel::Info, + x if x == LogLevel::Debug as u32 => LogLevel::Debug, + x if x == LogLevel::Trace as u32 => LogLevel::Trace, + _ => LogLevel::Error, + } + } +} + +impl From for LogLevel { + fn from(l: log::Level) -> Self { + use log::Level::*; + match l { + Error => Self::Error, + Warn => Self::Warn, + Info => Self::Info, + Debug => Self::Debug, + Trace => Self::Trace, + } + } +} + +impl From for log::Level { + fn from(l: LogLevel) -> Self { + use self::LogLevel::*; + match l { + Error => Self::Error, + Warn => Self::Warn, + Info => Self::Info, + Debug => Self::Debug, + Trace => Self::Trace, + } + } +} + +/// Encodes the given value into a buffer and returns the pointer and the length as a single `u64`. +/// +/// When Substrate calls into Wasm it expects a fixed signature for functions exported +/// from the Wasm blob. The return value of this signature is always a `u64`. +/// This `u64` stores the pointer to the encoded return value and the length of this encoded value. +/// The low `32bits` are reserved for the pointer, followed by `32bit` for the length. +#[cfg(not(feature = "std"))] +pub fn to_substrate_wasm_fn_return_value(value: &impl Encode) -> u64 { + let encoded = value.encode(); + + let ptr = encoded.as_ptr() as u64; + let length = encoded.len() as u64; + let res = ptr | (length << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + rstd::mem::forget(encoded); + + res +} diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index 6403a20dbd54676faa3c7d054f1a9cd8ec8c0db3..27bd29a00df8db696404b9324f49b77deecd8820 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -17,14 +17,13 @@ //! Offchain workers types use codec::{Encode, Decode}; -use rstd::prelude::{Vec, Box}; -use rstd::convert::TryFrom; +use rstd::{prelude::{Vec, Box}, convert::TryFrom}; +use crate::RuntimeDebug; pub use crate::crypto::KeyTypeId; /// A type of supported crypto. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug)] #[repr(C)] pub enum StorageKind { /// Persistent storage is non-revertible and not fork-aware. It means that any value @@ -60,8 +59,8 @@ impl From for u32 { } /// Opaque type for offchain http requests. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "std", derive(Debug, Hash))] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] pub struct HttpRequestId(pub u16); impl From for u32 { @@ -71,8 +70,7 @@ impl From for u32 { } /// An error enum returned by some http methods. -#[derive(Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] #[repr(C)] pub enum HttpError { /// The requested action couldn't been completed within a deadline. @@ -103,8 +101,7 @@ impl From for u32 { } /// Status of the HTTP request -#[derive(Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] pub enum HttpRequestStatus { /// Deadline was reached while we waited for this request to finish. /// @@ -150,8 +147,7 @@ impl TryFrom for HttpRequestStatus { /// A blob to hold information about the local node's network state /// without committing to its format. -#[derive(Clone, Eq, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] pub struct OpaqueNetworkState { /// PeerId of the local node. pub peer_id: OpaquePeerId, @@ -160,8 +156,7 @@ pub struct OpaqueNetworkState { } /// Simple blob to hold a `PeerId` without committing to its format. -#[derive(Default, Clone, Eq, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Default, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] pub struct OpaquePeerId(pub Vec); impl OpaquePeerId { @@ -172,8 +167,7 @@ impl OpaquePeerId { } /// Simple blob to hold a `Multiaddr` without committing to its format. -#[derive(Clone, Eq, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] pub struct OpaqueMultiaddr(pub Vec); impl OpaqueMultiaddr { @@ -184,13 +178,11 @@ impl OpaqueMultiaddr { } /// Opaque timestamp type -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug)] pub struct Timestamp(u64); /// Duration type -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug)] pub struct Duration(u64); impl Duration { @@ -663,110 +655,17 @@ impl Externalities for LimitedExternalities { } } -/// An implementation of offchain extensions that should never be triggered. -pub enum NeverOffchainExt {} - -impl NeverOffchainExt { - /// Create new offchain extensions. - pub fn new<'a>() -> Option<&'a mut Self> { - None - } +#[cfg(feature = "std")] +externalities::decl_extension! { + /// The offchain extension that will be registered at the Substrate externalities. + pub struct OffchainExt(Box); } -impl Externalities for NeverOffchainExt { - fn is_validator(&self) -> bool { - unreachable!() - } - - fn submit_transaction(&mut self, _extrinsic: Vec) -> Result<(), ()> { - unreachable!() - } - - fn network_state( - &self, - ) -> Result { - unreachable!() - } - - fn timestamp(&mut self) -> Timestamp { - unreachable!() - } - - fn sleep_until(&mut self, _deadline: Timestamp) { - unreachable!() - } - - fn random_seed(&mut self) -> [u8; 32] { - unreachable!() - } - - fn local_storage_set(&mut self, _kind: StorageKind, _key: &[u8], _value: &[u8]) { - unreachable!() - } - - fn local_storage_compare_and_set( - &mut self, - _kind: StorageKind, - _key: &[u8], - _old_value: Option<&[u8]>, - _new_value: &[u8], - ) -> bool { - unreachable!() - } - - fn local_storage_get(&mut self, _kind: StorageKind, _key: &[u8]) -> Option> { - unreachable!() - } - - fn http_request_start( - &mut self, - _method: &str, - _uri: &str, - _meta: &[u8] - ) -> Result { - unreachable!() - } - - fn http_request_add_header( - &mut self, - _request_id: HttpRequestId, - _name: &str, - _value: &str - ) -> Result<(), ()> { - unreachable!() - } - - fn http_request_write_body( - &mut self, - _request_id: HttpRequestId, - _chunk: &[u8], - _deadline: Option - ) -> Result<(), HttpError> { - unreachable!() - } - - fn http_response_wait( - &mut self, - _ids: &[HttpRequestId], - _deadline: Option - ) -> Vec { - unreachable!() - } - - fn http_response_headers( - &mut self, - _request_id: HttpRequestId - ) -> Vec<(Vec, Vec)> { - unreachable!() - } - - fn http_response_read_body( - &mut self, - _request_id: HttpRequestId, - _buffer: &mut [u8], - _deadline: Option - ) -> Result { - unreachable!() +#[cfg(feature = "std")] +impl OffchainExt { + /// Create a new instance of `Self`. + pub fn new(offchain: O) -> Self { + Self(Box::new(offchain)) } } diff --git a/core/primitives/src/sandbox.rs b/core/primitives/src/sandbox.rs index e47a30ca5bbb7abb91c4901d9e05d0df33836b2d..dd91ad6a1f5c0ef00966c57b4a20ab93359118af 100644 --- a/core/primitives/src/sandbox.rs +++ b/core/primitives/src/sandbox.rs @@ -21,12 +21,12 @@ use rstd::vec::Vec; /// Error error that can be returned from host function. #[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub struct HostError; /// Representation of a typed wasm value. #[derive(Clone, Copy, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub enum TypedValue { /// Value of 32-bit signed or unsigned integer. #[codec(index = "1")] @@ -86,7 +86,7 @@ impl From for ::wasmi::RuntimeValue { /// /// Basically a `TypedValue` plus `Unit`, for functions which return nothing. #[derive(Clone, Copy, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub enum ReturnValue { /// For returning nothing. Unit, @@ -119,7 +119,7 @@ fn return_value_encoded_max_size() { /// Describes an entity to define or import into the environment. #[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub enum ExternEntity { /// Function that is specified by an index in a default table of /// a module that creates the sandbox. @@ -137,7 +137,7 @@ pub enum ExternEntity { /// Each entry has a two-level name and description of an entity /// being defined. #[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub struct Entry { /// Module name of which corresponding entity being defined. pub module_name: Vec, @@ -149,7 +149,7 @@ pub struct Entry { /// Definition of runtime that could be used by sandboxed code. #[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(crate::RuntimeDebug)] pub struct EnvironmentDefinition { /// Vector of all entries in the environment definition. pub entries: Vec, diff --git a/core/primitives/src/sr25519.rs b/core/primitives/src/sr25519.rs index 0e573f49ce34c3de29565df0fc318b6214aae2d3..ba7f480128081cc74e0810f4ad28719bc5d4c4c9 100644 --- a/core/primitives/src/sr25519.rs +++ b/core/primitives/src/sr25519.rs @@ -47,6 +47,7 @@ use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH}; const SIGNING_CTX: &[u8] = b"substrate"; /// An Schnorrkel/Ristretto x25519 ("sr25519") public key. +#[cfg_attr(feature = "std", derive(Hash))] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default)] pub struct Public(pub [u8; 32]); @@ -128,12 +129,17 @@ impl std::fmt::Display for Public { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for Public { - fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result { +impl rstd::fmt::Debug for Public { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { let s = self.to_ss58check(); write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } #[cfg(feature = "std")] @@ -151,13 +157,6 @@ impl<'de> Deserialize<'de> for Public { } } -#[cfg(feature = "std")] -impl std::hash::Hash for Public { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - /// An Schnorrkel/Ristretto x25519 ("sr25519") signature. /// /// Instead of importing it for the local module, alias it to be available as a public type @@ -237,11 +236,16 @@ impl From for Signature { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for Signature { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl rstd::fmt::Debug for Signature { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } #[cfg(feature = "std")] @@ -381,8 +385,8 @@ impl AsRef for Pair { /// Derive a single hard junction. #[cfg(feature = "std")] -fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> SecretKey { - secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0.expand(ExpansionMode::Ed25519) +fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> MiniSecretKey { + secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0 } /// The raw secret seed, which can be used to recreate the `Pair`. @@ -437,17 +441,6 @@ impl TraitPair for Pair { } } - /// Generate a key from the phrase, password and derivation path. - fn from_standard_components>( - phrase: &str, - password: Option<&str>, - path: I - ) -> Result { - Self::from_phrase(phrase, password)?.0 - .derive(path) - .map_err(|_| SecretStringError::InvalidPath) - } - fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); let phrase = mnemonic.phrase(); @@ -466,13 +459,27 @@ impl TraitPair for Pair { .map(|m| Self::from_entropy(m.entropy(), password)) } - fn derive>(&self, path: Iter) -> Result { + fn derive>(&self, + path: Iter, + seed: Option, + ) -> Result<(Pair, Option), Self::DeriveError> { + let seed = if let Some(s) = seed { + if let Ok(msk) = MiniSecretKey::from_bytes(&s) { + if msk.expand(ExpansionMode::Ed25519) == self.0.secret { + Some(msk) + } else { None } + } else { None } + } else { None }; let init = self.0.secret.clone(); - let result = path.fold(init, |acc, j| match j { - DeriveJunction::Soft(cc) => acc.derived_key_simple(ChainCode(cc), &[]).0, - DeriveJunction::Hard(cc) => derive_hard_junction(&acc, &cc), + let (result, seed) = path.fold((init, seed), |(acc, acc_seed), j| match (j, acc_seed) { + (DeriveJunction::Soft(cc), _) => + (acc.derived_key_simple(ChainCode(cc), &[]).0, None), + (DeriveJunction::Hard(cc), maybe_seed) => { + let seed = derive_hard_junction(&acc, &cc); + (seed.expand(ExpansionMode::Ed25519), maybe_seed.map(|_| seed)) + } }); - Ok(Self(result.into())) + Ok((Self(result.into()), seed.map(|s| MiniSecretKey::to_bytes(&s)))) } fn sign(&self, message: &[u8]) -> Signature { @@ -614,9 +621,9 @@ mod test { let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); - let derive_1 = pair.derive(Some(DeriveJunction::soft(1)).into_iter()).unwrap(); - let derive_1b = pair.derive(Some(DeriveJunction::soft(1)).into_iter()).unwrap(); - let derive_2 = pair.derive(Some(DeriveJunction::soft(2)).into_iter()).unwrap(); + let derive_1 = pair.derive(Some(DeriveJunction::soft(1)).into_iter(), None).unwrap().0; + let derive_1b = pair.derive(Some(DeriveJunction::soft(1)).into_iter(), None).unwrap().0; + let derive_2 = pair.derive(Some(DeriveJunction::soft(2)).into_iter(), None).unwrap().0; assert_eq!(derive_1.public(), derive_1b.public()); assert_ne!(derive_1.public(), derive_2.public()); } @@ -626,9 +633,9 @@ mod test { let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); - let derive_1 = pair.derive(Some(DeriveJunction::hard(1)).into_iter()).unwrap(); - let derive_1b = pair.derive(Some(DeriveJunction::hard(1)).into_iter()).unwrap(); - let derive_2 = pair.derive(Some(DeriveJunction::hard(2)).into_iter()).unwrap(); + let derive_1 = pair.derive(Some(DeriveJunction::hard(1)).into_iter(), None).unwrap().0; + let derive_1b = pair.derive(Some(DeriveJunction::hard(1)).into_iter(), None).unwrap().0; + let derive_2 = pair.derive(Some(DeriveJunction::hard(2)).into_iter(), None).unwrap().0; assert_eq!(derive_1.public(), derive_1b.public()); assert_ne!(derive_1.public(), derive_2.public()); } @@ -639,7 +646,7 @@ mod test { "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); let path = Some(DeriveJunction::soft(1)); - let pair_1 = pair.derive(path.clone().into_iter()).unwrap(); + let pair_1 = pair.derive(path.clone().into_iter(), None).unwrap().0; let public_1 = pair.public().derive(path.into_iter()).unwrap(); assert_eq!(pair_1.public(), public_1); } diff --git a/core/primitives/src/testing.rs b/core/primitives/src/testing.rs index ccfc36af7326111fc490f435454ae48327a628b8..e5d301008e3d4fb3344ad7306b797f9b70727964 100644 --- a/core/primitives/src/testing.rs +++ b/core/primitives/src/testing.rs @@ -18,8 +18,7 @@ #[cfg(feature = "std")] use crate::{ed25519, sr25519, crypto::{Public, Pair}}; -use crate::crypto::KeyTypeId; // No idea why this import had to move from - // the previous line, but now the compiler is happy +use crate::crypto::KeyTypeId; /// Key type for generic Ed25519 key. pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25"); @@ -37,7 +36,7 @@ pub struct KeyStore { #[cfg(feature = "std")] impl KeyStore { /// Creates a new instance of `Self`. - pub fn new() -> std::sync::Arc> { + pub fn new() -> crate::traits::BareCryptoStorePtr { std::sync::Arc::new(parking_lot::RwLock::new(Self::default())) } } @@ -130,13 +129,112 @@ impl crate::traits::BareCryptoStore for KeyStore { } } +/// Macro for exporting functions from wasm in with the expected signature for using it with the +/// wasm executor. This is useful for tests where you need to call a function in wasm. +/// +/// The input parameters are expected to be SCALE encoded and will be automatically decoded for you. +/// The output value is also SCALE encoded when returned back to the host. +/// +/// The functions are feature-gated with `#[cfg(not(feature = "std"))]`, so they are only available +/// from within wasm. +/// +/// # Example +/// +/// ``` +/// # use substrate_primitives::wasm_export_functions; +/// +/// wasm_export_functions! { +/// fn test_in_wasm(value: bool, another_value: Vec) -> bool { +/// value && another_value.is_empty() +/// } +/// +/// fn without_return_value() { +/// // do something +/// } +/// } +/// ``` +#[macro_export] +macro_rules! wasm_export_functions { + ( + $( + fn $name:ident ( + $( $arg_name:ident: $arg_ty:ty ),* $(,)? + ) $( -> $ret_ty:ty )? { $( $fn_impl:tt )* } + )* + ) => { + $( + $crate::wasm_export_functions! { + @IMPL + fn $name ( + $( $arg_name: $arg_ty ),* + ) $( -> $ret_ty )? { $( $fn_impl )* } + } + )* + }; + (@IMPL + fn $name:ident ( + $( $arg_name:ident: $arg_ty:ty ),* + ) { $( $fn_impl:tt )* } + ) => { + #[no_mangle] + #[allow(unreachable_code)] + #[cfg(not(feature = "std"))] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input: &[u8] = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::rstd::slice::from_raw_parts(input_data, input_len) + } + }; + + { + let ($( $arg_name ),*) : ($( $arg_ty ),*) = $crate::Decode::decode( + &mut &input[..], + ).expect("Input data is correctly encoded"); + + $( $fn_impl )* + } + + $crate::to_substrate_wasm_fn_return_value(&()) + } + }; + (@IMPL + fn $name:ident ( + $( $arg_name:ident: $arg_ty:ty ),* + ) $( -> $ret_ty:ty )? { $( $fn_impl:tt )* } + ) => { + #[no_mangle] + #[allow(unreachable_code)] + #[cfg(not(feature = "std"))] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input: &[u8] = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::rstd::slice::from_raw_parts(input_data, input_len) + } + }; + + let output $( : $ret_ty )? = { + let ($( $arg_name ),*) : ($( $arg_ty ),*) = $crate::Decode::decode( + &mut &input[..], + ).expect("Input data is correctly encoded"); + + $( $fn_impl )* + }; + + $crate::to_substrate_wasm_fn_return_value(&output) + } + }; +} + #[cfg(test)] mod tests { use super::*; - use crate::{sr25519, traits::BareCryptoStore}; + use crate::sr25519; use crate::testing::{ED25519, SR25519}; - #[test] fn store_key_and_extract() { let store = KeyStore::new(); diff --git a/core/primitives/src/traits.rs b/core/primitives/src/traits.rs index b173d8512cfc4abc288c0cb7a71d0509421b34f7..1ef665032eed4cb9cba4787450035c4d36c5c752 100644 --- a/core/primitives/src/traits.rs +++ b/core/primitives/src/traits.rs @@ -16,15 +16,13 @@ //! Shareable Substrate traits. -#[cfg(feature = "std")] -use crate::{crypto::KeyTypeId, ed25519, sr25519, child_storage_key::ChildStorageKey}; -#[cfg(feature = "std")] +use crate::{crypto::KeyTypeId, ed25519, sr25519}; + use std::{fmt::{Debug, Display}, panic::UnwindSafe}; -#[cfg(feature = "std")] -use hash_db::Hasher; + +pub use externalities::{Externalities, ExternalitiesExt}; /// Something that generates, stores and provides access to keys. -#[cfg(feature = "std")] pub trait BareCryptoStore: Send + Sync { /// Returns all sr25519 public keys for the given key type. fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec; @@ -70,128 +68,22 @@ pub trait BareCryptoStore: Send + Sync { } /// A pointer to the key store. -#[cfg(feature = "std")] pub type BareCryptoStorePtr = std::sync::Arc>; -/// Externalities: pinned to specific active address. -#[cfg(feature = "std")] -pub trait Externalities { - /// Read runtime storage. - fn storage(&self, key: &[u8]) -> Option>; - - /// Get storage value hash. This may be optimized for large values. - fn storage_hash(&self, key: &[u8]) -> Option { - self.storage(key).map(|v| H::hash(&v)) - } - - /// Get child storage value hash. This may be optimized for large values. - fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { - self.child_storage(storage_key, key).map(|v| H::hash(&v)) - } - - /// Read original runtime storage, ignoring any overlayed changes. - fn original_storage(&self, key: &[u8]) -> Option>; - - /// Read original runtime child storage, ignoring any overlayed changes. - fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option>; - - /// Get original storage value hash, ignoring any overlayed changes. - /// This may be optimized for large values. - fn original_storage_hash(&self, key: &[u8]) -> Option { - self.original_storage(key).map(|v| H::hash(&v)) - } - - /// Get original child storage value hash, ignoring any overlayed changes. - /// This may be optimized for large values. - fn original_child_storage_hash( - &self, - storage_key: ChildStorageKey, - key: &[u8], - ) -> Option { - self.original_child_storage(storage_key, key).map(|v| H::hash(&v)) - } - - /// Read child runtime storage. - fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option>; - - /// Set storage entry `key` of current contract being called (effective immediately). - fn set_storage(&mut self, key: Vec, value: Vec) { - self.place_storage(key, Some(value)); - } - - /// Set child storage entry `key` of current contract being called (effective immediately). - fn set_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Vec) { - self.place_child_storage(storage_key, key, Some(value)) - } - - /// Clear a storage entry (`key`) of current contract being called (effective immediately). - fn clear_storage(&mut self, key: &[u8]) { - self.place_storage(key.to_vec(), None); - } - - /// Clear a child storage entry (`key`) of current contract being called (effective immediately). - fn clear_child_storage(&mut self, storage_key: ChildStorageKey, key: &[u8]) { - self.place_child_storage(storage_key, key.to_vec(), None) - } - - /// Whether a storage entry exists. - fn exists_storage(&self, key: &[u8]) -> bool { - self.storage(key).is_some() - } - - /// Whether a child storage entry exists. - fn exists_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> bool { - self.child_storage(storage_key, key).is_some() - } - - /// Clear an entire child storage. - fn kill_child_storage(&mut self, storage_key: ChildStorageKey); - - /// Clear storage entries which keys are start with the given prefix. - fn clear_prefix(&mut self, prefix: &[u8]); - - /// Clear child storage entries which keys are start with the given prefix. - fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]); - - /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). - fn place_storage(&mut self, key: Vec, value: Option>); - - /// Set or clear a child storage entry. Return whether the operation succeeds. - fn place_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Option>); - - /// Get the identity of the chain. - fn chain_id(&self) -> u64; - - /// Get the trie root of the current storage map. This will also update all child storage keys - /// in the top-level storage map. - fn storage_root(&mut self) -> H::Out where H::Out: Ord; - - /// Get the trie root of a child storage map. This will also update the value of the child - /// storage keys in the top-level storage map. - /// If the storage root equals the default hash as defined by the trie, the key in the top-level - /// storage map will be removed. - fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec; - - /// Get the change trie root of the current storage overlay at a block with given parent. - fn storage_changes_root(&mut self, parent: H::Out) -> Result, ()> where H::Out: Ord; - - /// Returns offchain externalities extension if present. - fn offchain(&mut self) -> Option<&mut dyn crate::offchain::Externalities>; - - /// Returns the keystore. - fn keystore(&self) -> Option; +externalities::decl_extension! { + /// The keystore extension to register/retrieve from the externalities. + pub struct KeystoreExt(BareCryptoStorePtr); } /// Code execution engine. -#[cfg(feature = "std")] -pub trait CodeExecutor: Sized + Send + Sync { +pub trait CodeExecutor: Sized + Send + Sync { /// Externalities error type. type Error: Display + Debug + Send + 'static; /// Call a given method in the runtime. Returns a tuple of the result (either the output data /// or an execution error) together with a `bool`, which is true if native execution was used. fn call< - E: Externalities, + E: Externalities, R: codec::Codec + PartialEq, NC: FnOnce() -> Result + UnwindSafe, >( diff --git a/core/primitives/storage/Cargo.toml b/core/primitives/storage/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..1e5d7ee8b4548ff8641c75e220756f8d499287a2 --- /dev/null +++ b/core/primitives/storage/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "substrate-primitives-storage" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +description = "Storage related primitives" + +[dependencies] +rstd = { package = "sr-std", path = "../../sr-std", default-features = false } +serde = { version = "1.0.101", optional = true, features = ["derive"] } +impl-serde = { version = "0.2.3", optional = true } +substrate-debug-derive = { version = "2.0.0", path = "../debug-derive" } + +[features] +default = [ "std" ] +std = [ "rstd/std", "serde", "impl-serde" ] diff --git a/core/primitives/src/storage.rs b/core/primitives/storage/src/lib.rs similarity index 55% rename from core/primitives/src/storage.rs rename to core/primitives/storage/src/lib.rs index 14c49bfaa96096ef63355f930a678660bb4fe88d..dcdc223994e269af7903df68e13830658e4d5e71 100644 --- a/core/primitives/src/storage.rs +++ b/core/primitives/storage/src/lib.rs @@ -14,40 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Contract execution data. +//! Primitive types for storage related stuff. + +#![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; -#[cfg(feature = "std")] -use crate::bytes; -use rstd::vec::Vec; - -/// Contract storage key. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] -pub struct StorageKey(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); - -/// Contract storage entry data. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord, Clone))] -pub struct StorageData(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); +use substrate_debug_derive::RuntimeDebug; + +use rstd::{vec::Vec, borrow::Cow}; + +/// Storage key. +#[derive(PartialEq, Eq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone))] +pub struct StorageKey( + #[cfg_attr(feature = "std", serde(with="impl_serde::serialize"))] + pub Vec, +); + +/// Storage data associated to a [`StorageKey`]. +#[derive(PartialEq, Eq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone))] +pub struct StorageData( + #[cfg_attr(feature = "std", serde(with="impl_serde::serialize"))] + pub Vec, +); /// Storage change set -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, PartialEq, Eq))] +#[derive(RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, PartialEq, Eq))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct StorageChangeSet { /// Block hash pub block: Hash, /// A list of changes - pub changes: Vec<( - StorageKey, - Option, - )>, + pub changes: Vec<(StorageKey, Option)>, } /// List of all well known keys and prefixes in storage. pub mod well_known_keys { - /// Wasm code of the runtime. /// /// Stored as a raw byte vector. Required by substrate. @@ -94,3 +99,52 @@ pub mod well_known_keys { has_right_prefix } } + +/// A wrapper around a child storage key. +/// +/// This wrapper ensures that the child storage key is correct and properly used. It is +/// impossible to create an instance of this struct without providing a correct `storage_key`. +pub struct ChildStorageKey<'a> { + storage_key: Cow<'a, [u8]>, +} + +impl<'a> ChildStorageKey<'a> { + /// Create new instance of `Self`. + fn new(storage_key: Cow<'a, [u8]>) -> Option { + if well_known_keys::is_child_trie_key_valid(&storage_key) { + Some(ChildStorageKey { storage_key }) + } else { + None + } + } + + /// Create a new `ChildStorageKey` from a vector. + /// + /// `storage_key` need to start with `:child_storage:default:` + /// See `is_child_trie_key_valid` for more details. + pub fn from_vec(key: Vec) -> Option { + Self::new(Cow::Owned(key)) + } + + /// Create a new `ChildStorageKey` from a slice. + /// + /// `storage_key` need to start with `:child_storage:default:` + /// See `is_child_trie_key_valid` for more details. + pub fn from_slice(key: &'a [u8]) -> Option { + Self::new(Cow::Borrowed(key)) + } + + /// Get access to the byte representation of the storage key. + /// + /// This key is guaranteed to be correct. + pub fn as_ref(&self) -> &[u8] { + &*self.storage_key + } + + /// Destruct this instance into an owned vector that represents the storage key. + /// + /// This key is guaranteed to be correct. + pub fn into_owned(self) -> Vec { + self.storage_key.into_owned() + } +} diff --git a/core/rpc-servers/Cargo.toml b/core/rpc-servers/Cargo.toml index 27601d8cc148e6a82fa979de5e185059c0cce155..80e16bc5ae5be385c03ed0c5f4fa0cda1729ee06 100644 --- a/core/rpc-servers/Cargo.toml +++ b/core/rpc-servers/Cargo.toml @@ -9,7 +9,7 @@ jsonrpc-core = "13.2.0" pubsub = { package = "jsonrpc-pubsub", version = "13.2.0" } log = "0.4.8" serde = "1.0.101" -serde_json = "1.0" +serde_json = "1.0.41" sr-primitives = { path = "../sr-primitives" } [target.'cfg(not(target_os = "unknown"))'.dependencies] diff --git a/core/rpc-servers/src/lib.rs b/core/rpc-servers/src/lib.rs index a23b4a08997341efe64e49a5b67b4fb8c5fec436..8d39386f93c4ae9c79c0504beb7397914a3477c8 100644 --- a/core/rpc-servers/src/lib.rs +++ b/core/rpc-servers/src/lib.rs @@ -16,7 +16,7 @@ //! Substrate RPC servers. -#[warn(missing_docs)] +#![warn(missing_docs)] use std::io; use jsonrpc_core::IoHandlerExtension; @@ -60,7 +60,9 @@ pub fn rpc_handler( mod inner { use super::*; + /// Type alias for http server pub type HttpServer = http::Server; + /// Type alias for ws server pub type WsServer = ws::Server; /// Start HTTP server listening on given address. diff --git a/core/rpc/Cargo.toml b/core/rpc/Cargo.toml index 5178fc56d8188946ee680d2e833ba198b3451d33..85998feb1b88789e2d67848eb9edbac051d0498c 100644 --- a/core/rpc/Cargo.toml +++ b/core/rpc/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4.8" primitives = { package = "substrate-primitives", path = "../primitives" } rpc = { package = "jsonrpc-core", version = "13.0.0" } runtime_version = { package = "sr-version", path = "../sr-version" } -serde_json = "1.0.40" +serde_json = "1.0.41" session = { package = "substrate-session", path = "../session" } sr-primitives = { path = "../sr-primitives" } rpc-primitives = { package = "substrate-rpc-primitives", path = "primitives" } @@ -23,6 +23,7 @@ substrate-executor = { path = "../executor" } substrate-keystore = { path = "../keystore" } transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" } hash-db = { version = "0.15.2", default-features = false } +parking_lot = { version = "0.9.0" } [dev-dependencies] assert_matches = "1.3.0" diff --git a/core/rpc/api/Cargo.toml b/core/rpc/api/Cargo.toml index b2614dbcede89c9101690cc0d41c4d061ce0b805..bccafc2a85dd7619984d881f27525526204b4657 100644 --- a/core/rpc/api/Cargo.toml +++ b/core/rpc/api/Cargo.toml @@ -17,6 +17,6 @@ parking_lot = "0.9.0" primitives = { package = "substrate-primitives", path = "../../primitives" } runtime_version = { package = "sr-version", path = "../../sr-version" } serde = { version = "1.0.101", features = ["derive"] } -serde_json = "1.0.40" +serde_json = "1.0.41" txpool = { package = "substrate-transaction-graph", path = "../../transaction-pool/graph" } rpc-primitives = { package = "substrate-rpc-primitives", path = "../../rpc/primitives" } diff --git a/core/rpc/api/src/chain/error.rs b/core/rpc/api/src/chain/error.rs index eccb7f4f1b0f0892eca1d1a91ce4280f1df3a028..454c0887abf820c284ce2eaa74da852cecb082b0 100644 --- a/core/rpc/api/src/chain/error.rs +++ b/core/rpc/api/src/chain/error.rs @@ -23,7 +23,7 @@ use jsonrpc_core as rpc; /// Chain RPC Result type. pub type Result = std::result::Result; -/// State RPC future Result type. +/// Chain RPC future Result type. pub type FutureResult = Box + Send>; /// Chain RPC errors. diff --git a/core/rpc/api/src/subscriptions.rs b/core/rpc/api/src/subscriptions.rs index bff184cadeac02258c103eab591a634a1fa84a40..d5ca74fa60bc7e441b6f2645725b6babbba64528 100644 --- a/core/rpc/api/src/subscriptions.rs +++ b/core/rpc/api/src/subscriptions.rs @@ -69,18 +69,26 @@ impl Subscriptions { } } + /// Borrows the internal task executor. + /// + /// This can be used to spawn additional tasks on the underyling event loop. + pub fn executor(&self) -> &TaskExecutor { + &self.executor + } + /// Creates new subscription for given subscriber. /// /// Second parameter is a function that converts Subscriber sink into a future. /// This future will be driven to completion by the underlying event loop /// or will be cancelled in case #cancel is invoked. - pub fn add(&self, subscriber: Subscriber, into_future: G) where + pub fn add(&self, subscriber: Subscriber, into_future: G) -> SubscriptionId where G: FnOnce(Sink) -> R, R: future::IntoFuture, F: future::Future + Send + 'static, { let id = self.next_id.next_id(); - if let Ok(sink) = subscriber.assign_id(id.into()) { + let subscription_id: SubscriptionId = id.into(); + if let Ok(sink) = subscriber.assign_id(subscription_id.clone()) { let (tx, rx) = oneshot::channel(); let future = into_future(sink) .into_future() @@ -92,6 +100,8 @@ impl Subscriptions { error!("Failed to spawn RPC subscription task"); } } + + subscription_id } /// Cancel subscription. diff --git a/core/rpc/api/src/system/helpers.rs b/core/rpc/api/src/system/helpers.rs index 00e2ba9f408b7658ccb45a10d62642bbfed1209c..10319d57b693b0fb0e7847cb6b024c122b60b930 100644 --- a/core/rpc/api/src/system/helpers.rs +++ b/core/rpc/api/src/system/helpers.rs @@ -50,6 +50,14 @@ pub struct Health { pub should_have_peers: bool, } +impl fmt::Display for Health { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{} peers ({})", self.peers, if self.is_syncing { + "syncing" + } else { "idle" }) + } +} + /// Network Peer information #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -66,12 +74,17 @@ pub struct PeerInfo { pub best_number: Number, } -impl fmt::Display for Health { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{} peers ({})", self.peers, if self.is_syncing { - "syncing" - } else { "idle" }) - } +/// The role the node is running as +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum NodeRole { + /// The node is a full node + Full, + /// The node is a light client + LightClient, + /// The node is an authority + Authority, + /// An unknown role with a bit number + UnknownRole(u8) } #[cfg(test)] diff --git a/core/rpc/api/src/system/mod.rs b/core/rpc/api/src/system/mod.rs index b5eacc5d618233d30166bf0463fed9829705bd31..f59fd84c7c027bc891cd664e5be386c44dbe2885 100644 --- a/core/rpc/api/src/system/mod.rs +++ b/core/rpc/api/src/system/mod.rs @@ -24,7 +24,7 @@ use jsonrpc_derive::rpc; use self::error::Result; -pub use self::helpers::{Properties, SystemInfo, Health, PeerInfo}; +pub use self::helpers::{Properties, SystemInfo, Health, PeerInfo, NodeRole}; pub use self::gen_client::Client as SystemClient; /// Substrate system RPC API @@ -64,4 +64,8 @@ pub trait SystemApi { // TODO: make this stable and move structs https://github.com/paritytech/substrate/issues/1890 #[rpc(name = "system_networkState", returns = "jsonrpc_core::Value")] fn system_network_state(&self) -> Receiver; + + /// Returns the roles the node is running as. + #[rpc(name = "system_nodeRoles", returns = "Vec")] + fn system_node_roles(&self) -> Receiver>; } diff --git a/core/rpc/src/author/mod.rs b/core/rpc/src/author/mod.rs index 9a978f22f717a1f2ae20457c009d46630a072d66..82122dcf3d21f90041b890b90fc43ed2dd984a75 100644 --- a/core/rpc/src/author/mod.rs +++ b/core/rpc/src/author/mod.rs @@ -26,10 +26,9 @@ use log::warn; use client::{self, Client}; use rpc::futures::{ Sink, Future, - stream::Stream as _, future::result, }; -use futures03::{StreamExt as _, compat::Compat}; +use futures03::{StreamExt as _, compat::Compat, future::ready}; use api::Subscriptions; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use codec::{Encode, Decode}; @@ -162,42 +161,44 @@ impl AuthorApi, BlockHash

> for Author whe let best_block_hash = self.client.info().chain.best_hash; let dxt = <

::Block as traits::Block>::Extrinsic::decode(&mut &xt[..]) .map_err(error::Error::from)?; - Ok(self.pool - .submit_and_watch(&generic::BlockId::hash(best_block_hash), dxt) - .boxed() - .compat() - .map_err(|e| e.into_pool_error() - .map(error::Error::from) - .unwrap_or_else(|e| error::Error::Verification(Box::new(e)).into()) - )) + Ok( + self.pool + .submit_and_watch(&generic::BlockId::hash(best_block_hash), dxt) + .map_err(|e| e.into_pool_error() + .map(error::Error::from) + .unwrap_or_else(|e| error::Error::Verification(Box::new(e)).into()) + ) + ) }; - let future_watcher = match submit() { - Ok(future_watcher) => future_watcher, - Err(err) => { - // reject the subscriber (ignore errors - we don't care if subscriber is no longer there). - let _ = subscriber.reject(err.into()); - return; - }, - }; - - // make 'future' watcher be a future with output = stream of watcher events - let future_watcher = future_watcher - .map_err(|err| { warn!("Failed to submit extrinsic: {}", err); }) - .map(|watcher| Compat::new(watcher.into_stream().map(|v| Ok::<_, ()>(Ok(v))))); - - // convert a 'future' watcher into the stream with single element = stream of watcher events - let watcher_stream = future_watcher.into_stream(); - - // and now flatten the 'watcher_stream' so that we'll have the stream with watcher events - let watcher_stream = watcher_stream.flatten(); + let subscriptions = self.subscriptions.clone(); + let future = ready(submit()) + .and_then(|res| res) + // convert the watcher into a `Stream` + .map(|res| res.map(|watcher| watcher.into_stream().map(|v| Ok::<_, ()>(Ok(v))))) + // now handle the import result, + // start a new subscrition + .map(move |result| match result { + Ok(watcher) => { + subscriptions.add(subscriber, move |sink| { + sink + .sink_map_err(|_| unimplemented!()) + .send_all(Compat::new(watcher)) + .map(|_| ()) + }); + }, + Err(err) => { + warn!("Failed to submit extrinsic: {}", err); + // reject the subscriber (ignore errors - we don't care if subscriber is no longer there). + let _ = subscriber.reject(err.into()); + }, + }); - self.subscriptions.add(subscriber, move |sink| { - sink - .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) - .send_all(watcher_stream) - .map(|_| ()) - }); + let res = self.subscriptions.executor() + .execute(Box::new(Compat::new(future.map(|_| Ok(()))))); + if res.is_err() { + warn!("Error spawning subscription RPC task."); + } } fn unwatch_extrinsic(&self, _metadata: Option, id: SubscriptionId) -> Result { diff --git a/core/rpc/src/author/tests.rs b/core/rpc/src/author/tests.rs index 57d3929d928f70e60f1a86060d10e7282e3f8141..5ae044ff49ede78ab20adca9d9c3da1b108fd489 100644 --- a/core/rpc/src/author/tests.rs +++ b/core/rpc/src/author/tests.rs @@ -19,17 +19,18 @@ use super::*; use std::sync::Arc; use assert_matches::assert_matches; use codec::Encode; -use transaction_pool::{ - txpool::Pool, - FullChainApi, -}; use primitives::{ - H256, blake2_256, hexdisplay::HexDisplay, traits::BareCryptoStore, - testing::{ED25519, SR25519, KeyStore}, ed25519, crypto::Pair + H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore}, traits::BareCryptoStorePtr, ed25519, + crypto::Pair, }; +use rpc::futures::Stream as _; use test_client::{ - self, AccountKeyring, runtime::{Extrinsic, Transfer, SessionKeys}, DefaultTestClientBuilderExt, - TestClientBuilderExt, + self, AccountKeyring, runtime::{Extrinsic, Transfer, SessionKeys, RuntimeApi, Block}, DefaultTestClientBuilderExt, + TestClientBuilderExt, Backend, Client, Executor +}; +use transaction_pool::{ + txpool::Pool, + FullChainApi, }; use tokio::runtime; @@ -43,17 +44,41 @@ fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic { tx.into_signed_tx() } +struct TestSetup { + pub runtime: runtime::Runtime, + pub client: Arc>, + pub keystore: BareCryptoStorePtr, + pub pool: Arc, Block>>>, +} + +impl Default for TestSetup { + fn default() -> Self { + let keystore = KeyStore::new(); + let client = Arc::new(test_client::TestClientBuilder::new().set_keystore(keystore.clone()).build()); + let pool = Arc::new(Pool::new(Default::default(), FullChainApi::new(client.clone()))); + TestSetup { + runtime: runtime::Runtime::new().expect("Failed to create runtime in test setup"), + client, + keystore, + pool, + } + } +} + +impl TestSetup { + fn author(&self) -> Author, Block>, RuntimeApi> { + Author { + client: self.client.clone(), + pool: self.pool.clone(), + subscriptions: Subscriptions::new(Arc::new(self.runtime.executor())), + keystore: self.keystore.clone(), + } + } +} + #[test] fn submit_transaction_should_not_cause_error() { - let runtime = runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let keystore = KeyStore::new(); - let p = Author { - client: client.clone(), - pool: Arc::new(Pool::new(Default::default(), FullChainApi::new(client))), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; + let p = TestSetup::default().author(); let xt = uxt(AccountKeyring::Alice, 1).encode(); let h: H256 = blake2_256(&xt).into(); @@ -68,15 +93,7 @@ fn submit_transaction_should_not_cause_error() { #[test] fn submit_rich_transaction_should_not_cause_error() { - let runtime = runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let keystore = KeyStore::new(); - let p = Author { - client: client.clone(), - pool: Arc::new(Pool::new(Default::default(), FullChainApi::new(client.clone()))), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; + let p = TestSetup::default().author(); let xt = uxt(AccountKeyring::Alice, 0).encode(); let h: H256 = blake2_256(&xt).into(); @@ -92,23 +109,16 @@ fn submit_rich_transaction_should_not_cause_error() { #[test] fn should_watch_extrinsic() { //given - let mut runtime = runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let pool = Arc::new(Pool::new(Default::default(), FullChainApi::new(client.clone()))); - let keystore = KeyStore::new(); - let p = Author { - client, - pool: pool.clone(), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; - let (subscriber, id_rx, data) = ::jsonrpc_pubsub::typed::Subscriber::new_test("test"); + let mut setup = TestSetup::default(); + let p = setup.author(); + + let (subscriber, id_rx, data) = jsonrpc_pubsub::typed::Subscriber::new_test("test"); // when p.watch_extrinsic(Default::default(), subscriber, uxt(AccountKeyring::Alice, 0).encode().into()); // then - assert_eq!(runtime.block_on(id_rx), Ok(Ok(1.into()))); + assert_eq!(setup.runtime.block_on(id_rx), Ok(Ok(1.into()))); // check notifications let replacement = { let tx = Transfer { @@ -120,30 +130,38 @@ fn should_watch_extrinsic() { tx.into_signed_tx() }; AuthorApi::submit_extrinsic(&p, replacement.encode().into()).wait().unwrap(); - let (res, data) = runtime.block_on(data.into_future()).unwrap(); + let (res, data) = setup.runtime.block_on(data.into_future()).unwrap(); assert_eq!( res, Some(r#"{"jsonrpc":"2.0","method":"test","params":{"result":"ready","subscription":1}}"#.into()) ); let h = blake2_256(&replacement.encode()); assert_eq!( - runtime.block_on(data.into_future()).unwrap().0, + setup.runtime.block_on(data.into_future()).unwrap().0, Some(format!(r#"{{"jsonrpc":"2.0","method":"test","params":{{"result":{{"usurped":"0x{}"}},"subscription":1}}}}"#, HexDisplay::from(&h))) ); } +#[test] +fn should_return_watch_validation_error() { + //given + let mut setup = TestSetup::default(); + let p = setup.author(); + + let (subscriber, id_rx, _data) = jsonrpc_pubsub::typed::Subscriber::new_test("test"); + + // when + p.watch_extrinsic(Default::default(), subscriber, uxt(AccountKeyring::Alice, 179).encode().into()); + + // then + let res = setup.runtime.block_on(id_rx).unwrap(); + assert!(res.is_err(), "Expected the transaction to be rejected as invalid."); +} + #[test] fn should_return_pending_extrinsics() { - let runtime = runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let pool = Arc::new(Pool::new(Default::default(), FullChainApi::new(client.clone()))); - let keystore = KeyStore::new(); - let p = Author { - client, - pool: pool.clone(), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; + let p = TestSetup::default().author(); + let ex = uxt(AccountKeyring::Alice, 0); AuthorApi::submit_extrinsic(&p, ex.encode().into()).wait().unwrap(); assert_matches!( @@ -154,23 +172,16 @@ fn should_return_pending_extrinsics() { #[test] fn should_remove_extrinsics() { - let runtime = runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let pool = Arc::new(Pool::new(Default::default(), FullChainApi::new(client.clone()))); - let keystore = KeyStore::new(); - let p = Author { - client, - pool: pool.clone(), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; + let setup = TestSetup::default(); + let p = setup.author(); + let ex1 = uxt(AccountKeyring::Alice, 0); p.submit_extrinsic(ex1.encode().into()).wait().unwrap(); let ex2 = uxt(AccountKeyring::Alice, 1); p.submit_extrinsic(ex2.encode().into()).wait().unwrap(); let ex3 = uxt(AccountKeyring::Bob, 0); let hash3 = p.submit_extrinsic(ex3.encode().into()).wait().unwrap(); - assert_eq!(pool.status().ready, 3); + assert_eq!(setup.pool.status().ready, 3); // now remove all 3 let removed = p.remove_extrinsic(vec![ @@ -184,15 +195,8 @@ fn should_remove_extrinsics() { #[test] fn should_insert_key() { - let runtime = runtime::Runtime::new().unwrap(); - let client = Arc::new(test_client::new()); - let keystore = KeyStore::new(); - let p = Author { - client: client.clone(), - pool: Arc::new(Pool::new(Default::default(), FullChainApi::new(client))), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; + let setup = TestSetup::default(); + let p = setup.author(); let suri = "//Alice"; let key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair"); @@ -202,7 +206,7 @@ fn should_insert_key() { key_pair.public().0.to_vec().into(), ).expect("Insert key"); - let store_key_pair = keystore.read() + let store_key_pair = setup.keystore.read() .ed25519_key_pair(ED25519, &key_pair.public()).expect("Key exists in store"); assert_eq!(key_pair.public(), store_key_pair.public()); @@ -210,27 +214,20 @@ fn should_insert_key() { #[test] fn should_rotate_keys() { - let runtime = runtime::Runtime::new().unwrap(); - let keystore = KeyStore::new(); - let client = Arc::new(test_client::TestClientBuilder::new().set_keystore(keystore.clone()).build()); - let p = Author { - client: client.clone(), - pool: Arc::new(Pool::new(Default::default(), FullChainApi::new(client))), - subscriptions: Subscriptions::new(Arc::new(runtime.executor())), - keystore: keystore.clone(), - }; + let setup = TestSetup::default(); + let p = setup.author(); let new_public_keys = p.rotate_keys().expect("Rotates the keys"); let session_keys = SessionKeys::decode(&mut &new_public_keys[..]) .expect("SessionKeys decode successfully"); - let ed25519_key_pair = keystore.read().ed25519_key_pair( + let ed25519_key_pair = setup.keystore.read().ed25519_key_pair( ED25519, &session_keys.ed25519.clone().into(), ).expect("ed25519 key exists in store"); - let sr25519_key_pair = keystore.read().sr25519_key_pair( + let sr25519_key_pair = setup.keystore.read().sr25519_key_pair( SR25519, &session_keys.sr25519.clone().into(), ).expect("sr25519 key exists in store"); diff --git a/core/rpc/src/lib.rs b/core/rpc/src/lib.rs index 9ce9f82fdad2c3413247017378529d0313d3073e..1341acb63d3537e49d48ace6b363172dc09da2f6 100644 --- a/core/rpc/src/lib.rs +++ b/core/rpc/src/lib.rs @@ -20,7 +20,6 @@ #![warn(missing_docs)] -mod helpers; mod metadata; pub use api::Subscriptions; diff --git a/core/rpc/src/state/mod.rs b/core/rpc/src/state/mod.rs index 390f95ab41dffbebeb26208872d312e02ec53c1c..b922601b0a5b213999e49bc2cc76c64e7714b93d 100644 --- a/core/rpc/src/state/mod.rs +++ b/core/rpc/src/state/mod.rs @@ -23,27 +23,24 @@ mod state_light; mod tests; use std::sync::Arc; -use futures03::{future, StreamExt as _, TryStreamExt as _}; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; -use log::warn; use rpc::{ Result as RpcResult, - futures::{stream, Future, Sink, Stream}, + futures::Future, }; use api::Subscriptions; use client::{ - BlockchainEvents, Client, CallExecutor, + Client, CallExecutor, runtime_api::Metadata, light::{blockchain::RemoteBlockchain, fetcher::Fetcher}, }; use primitives::{ Blake2Hasher, Bytes, H256, - storage::{well_known_keys, StorageKey, StorageData, StorageChangeSet}, + storage::{StorageKey, StorageData, StorageChangeSet}, }; use runtime_version::RuntimeVersion; use sr_primitives::{ - generic::BlockId, traits::{Block as BlockT, ProvideRuntimeApi}, }; @@ -59,12 +56,6 @@ pub trait StateBackend: Send + Sync + 'static E: client::CallExecutor + Send + Sync + 'static, RA: Send + Sync + 'static, { - /// Get client reference. - fn client(&self) -> &Arc>; - - /// Get subscriptions reference. - fn subscriptions(&self) -> &Subscriptions; - /// Call runtime method at given block. fn call( &self, @@ -161,123 +152,29 @@ pub trait StateBackend: Send + Sync + 'static &self, _meta: crate::metadata::Metadata, subscriber: Subscriber, - ) { - let stream = match self.client().storage_changes_notification_stream( - Some(&[StorageKey(well_known_keys::CODE.to_vec())]), - None, - ) { - Ok(stream) => stream, - Err(err) => { - let _ = subscriber.reject(Error::from(client_err(err)).into()); - return; - } - }; - - self.subscriptions().add(subscriber, |sink| { - let version = self.runtime_version(None.into()) - .map_err(Into::into) - .wait(); - - let client = self.client().clone(); - let mut previous_version = version.clone(); - - let stream = stream - .filter_map(move |_| { - let info = client.info(); - let version = client - .runtime_version_at(&BlockId::hash(info.chain.best_hash)) - .map_err(client_err) - .map_err(Into::into); - if previous_version != version { - previous_version = version.clone(); - future::ready(Some(Ok::<_, ()>(version))) - } else { - future::ready(None) - } - }) - .compat(); - - sink - .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) - .send_all( - stream::iter_result(vec![Ok(version)]) - .chain(stream) - ) - // we ignore the resulting Stream (if the first stream is over we are unsubscribed) - .map(|_| ()) - }); - } + ); /// Unsubscribe from runtime version subscription fn unsubscribe_runtime_version( &self, _meta: Option, id: SubscriptionId, - ) -> RpcResult { - Ok(self.subscriptions().cancel(id)) - } + ) -> RpcResult; /// New storage subscription fn subscribe_storage( &self, _meta: crate::metadata::Metadata, subscriber: Subscriber>, - keys: Option> - ) { - let keys = Into::>>::into(keys); - let stream = match self.client().storage_changes_notification_stream( - keys.as_ref().map(|x| &**x), - None - ) { - Ok(stream) => stream, - Err(err) => { - let _ = subscriber.reject(client_err(err).into()); - return; - }, - }; - - // initial values - let initial = stream::iter_result(keys - .map(|keys| { - let block = self.client().info().chain.best_hash; - let changes = keys - .into_iter() - .map(|key| self.storage(Some(block.clone()).into(), key.clone()) - .map(|val| (key.clone(), val)) - .wait() - .unwrap_or_else(|_| (key, None)) - ) - .collect(); - vec![Ok(Ok(StorageChangeSet { block, changes }))] - }).unwrap_or_default()); - - self.subscriptions().add(subscriber, |sink| { - let stream = stream - .map(|(block, changes)| Ok::<_, ()>(Ok(StorageChangeSet { - block, - changes: changes.iter() - .filter_map(|(o_sk, k, v)| if o_sk.is_none() { - Some((k.clone(),v.cloned())) - } else { None }).collect(), - }))) - .compat(); - - sink - .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) - .send_all(initial.chain(stream)) - // we ignore the resulting Stream (if the first stream is over we are unsubscribed) - .map(|_| ()) - }) - } + keys: Option>, + ); /// Unsubscribe from storage subscription fn unsubscribe_storage( &self, _meta: Option, id: SubscriptionId, - ) -> RpcResult { - Ok(self.subscriptions().cancel(id)) - } + ) -> RpcResult; } /// Create new state API that works on full node. diff --git a/core/rpc/src/state/state_full.rs b/core/rpc/src/state/state_full.rs index 8b3dcd3d697cf00cdebaf3d5a2ce211ff5c2654f..ff4c5e5599a8a23af4cc9940f3ecc844d92620fd 100644 --- a/core/rpc/src/state/state_full.rs +++ b/core/rpc/src/state/state_full.rs @@ -19,16 +19,21 @@ use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; use std::ops::Range; -use rpc::futures::future::result; +use futures03::{future, StreamExt as _, TryStreamExt as _}; +use log::warn; +use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; +use rpc::{ + Result as RpcResult, + futures::{stream, Future, Sink, Stream, future::result}, +}; use api::Subscriptions; use client::{ - Client, CallExecutor, runtime_api::Metadata, + Client, CallExecutor, BlockchainEvents, runtime_api::Metadata, backend::Backend, error::Result as ClientResult, }; use primitives::{ - H256, Blake2Hasher, Bytes, offchain::NeverOffchainExt, - storage::{StorageKey, StorageData, StorageChangeSet}, + H256, Blake2Hasher, Bytes, storage::{well_known_keys, StorageKey, StorageData, StorageChangeSet}, }; use runtime_version::RuntimeVersion; use state_machine::ExecutionStrategy; @@ -53,6 +58,7 @@ struct QueryStorageRange { pub filtered_range: Option>, } +/// State API backend for full nodes. pub struct FullState { client: Arc>, subscriptions: Subscriptions, @@ -64,14 +70,14 @@ impl FullState B: Backend + Send + Sync + 'static, E: CallExecutor + Send + Sync + 'static + Clone, { - /// + /// Create new state API backend for full nodes. pub fn new(client: Arc>, subscriptions: Subscriptions) -> Self { Self { client, subscriptions } } /// Returns given block hash or best block hash if None is passed. fn block_or_best(&self, hash: Option) -> ClientResult { - crate::helpers::unwrap_or_else(|| Ok(self.client.info().chain.best_hash), hash) + Ok(hash.unwrap_or_else(|| self.client.info().chain.best_hash)) } /// Splits the `query_storage` block range into 'filtered' and 'unfiltered' subranges. @@ -225,14 +231,6 @@ impl StateBackend for FullState: ProvideRuntimeApi, as ProvideRuntimeApi>::Api: Metadata, { - fn client(&self) -> &Arc> { - &self.client - } - - fn subscriptions(&self) -> &Subscriptions { - &self.subscriptions - } - fn call( &self, block: Option, @@ -241,13 +239,16 @@ impl StateBackend for FullState FutureResult { Box::new(result( self.block_or_best(block) - .and_then(|block| self.client.executor() + .and_then(|block| + self + .client + .executor() .call( &BlockId::Hash(block), &method, &*call_data, ExecutionStrategy::NativeElseWasm, - NeverOffchainExt::new(), + None, ) .map(Into::into)) .map_err(client_err))) @@ -352,6 +353,125 @@ impl StateBackend for FullState, + ) { + let stream = match self.client.storage_changes_notification_stream( + Some(&[StorageKey(well_known_keys::CODE.to_vec())]), + None, + ) { + Ok(stream) => stream, + Err(err) => { + let _ = subscriber.reject(Error::from(client_err(err)).into()); + return; + } + }; + + self.subscriptions.add(subscriber, |sink| { + let version = self.runtime_version(None.into()) + .map_err(Into::into) + .wait(); + + let client = self.client.clone(); + let mut previous_version = version.clone(); + + let stream = stream + .filter_map(move |_| { + let info = client.info(); + let version = client + .runtime_version_at(&BlockId::hash(info.chain.best_hash)) + .map_err(client_err) + .map_err(Into::into); + if previous_version != version { + previous_version = version.clone(); + future::ready(Some(Ok::<_, ()>(version))) + } else { + future::ready(None) + } + }) + .compat(); + + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all( + stream::iter_result(vec![Ok(version)]) + .chain(stream) + ) + // we ignore the resulting Stream (if the first stream is over we are unsubscribed) + .map(|_| ()) + }); + } + + fn unsubscribe_runtime_version( + &self, + _meta: Option, + id: SubscriptionId, + ) -> RpcResult { + Ok(self.subscriptions.cancel(id)) + } + + fn subscribe_storage( + &self, + _meta: crate::metadata::Metadata, + subscriber: Subscriber>, + keys: Option>, + ) { + let keys = Into::>>::into(keys); + let stream = match self.client.storage_changes_notification_stream( + keys.as_ref().map(|x| &**x), + None + ) { + Ok(stream) => stream, + Err(err) => { + let _ = subscriber.reject(client_err(err).into()); + return; + }, + }; + + // initial values + let initial = stream::iter_result(keys + .map(|keys| { + let block = self.client.info().chain.best_hash; + let changes = keys + .into_iter() + .map(|key| self.storage(Some(block.clone()).into(), key.clone()) + .map(|val| (key.clone(), val)) + .wait() + .unwrap_or_else(|_| (key, None)) + ) + .collect(); + vec![Ok(Ok(StorageChangeSet { block, changes }))] + }).unwrap_or_default()); + + self.subscriptions.add(subscriber, |sink| { + let stream = stream + .map(|(block, changes)| Ok::<_, ()>(Ok(StorageChangeSet { + block, + changes: changes.iter() + .filter_map(|(o_sk, k, v)| if o_sk.is_none() { + Some((k.clone(),v.cloned())) + } else { None }).collect(), + }))) + .compat(); + + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(initial.chain(stream)) + // we ignore the resulting Stream (if the first stream is over we are unsubscribed) + .map(|_| ()) + }); + } + + fn unsubscribe_storage( + &self, + _meta: Option, + id: SubscriptionId, + ) -> RpcResult { + Ok(self.subscriptions.cancel(id)) + } } /// Splits passed range into two subranges where: diff --git a/core/rpc/src/state/state_light.rs b/core/rpc/src/state/state_light.rs index 456992db664f75907c3a947c3044a4682e746d1d..3d0c7979e39953a10c102dc729522ade010eb1c1 100644 --- a/core/rpc/src/state/state_light.rs +++ b/core/rpc/src/state/state_light.rs @@ -16,19 +16,31 @@ //! State API backend for light nodes. -use std::sync::Arc; +use std::{ + sync::Arc, + collections::{HashSet, HashMap, hash_map::Entry}, +}; use codec::Decode; -use futures03::{future::{ready, Either}, FutureExt, TryFutureExt}; +use futures03::{ + future::{ready, Either}, + channel::oneshot::{channel, Sender}, + FutureExt, TryFutureExt, + StreamExt as _, TryStreamExt as _, +}; use hash_db::Hasher; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; +use log::warn; +use parking_lot::Mutex; use rpc::{ Result as RpcResult, + futures::Sink, futures::future::{result, Future}, + futures::stream::Stream, }; use api::Subscriptions; use client::{ - Client, CallExecutor, backend::Backend, + BlockchainEvents, Client, CallExecutor, backend::Backend, error::Error as ClientError, light::{ blockchain::{future_header, RemoteBlockchain}, @@ -42,18 +54,89 @@ use primitives::{ use runtime_version::RuntimeVersion; use sr_primitives::{ generic::BlockId, - traits::{Block as BlockT, Header as HeaderT}, + traits::Block as BlockT, }; use super::{StateBackend, error::{FutureResult, Error}, client_err}; +/// Storage data map of storage keys => (optional) storage value. +type StorageMap = HashMap>; + +/// State API backend for light nodes. pub struct LightState, B, E, RA> { client: Arc>, subscriptions: Subscriptions, + version_subscriptions: SimpleSubscriptions, + storage_subscriptions: Arc>>, remote_blockchain: Arc>, fetcher: Arc, } +/// Shared requests container. +trait SharedRequests: Clone + Send + Sync { + /// Tries to listen for already issued request, or issues request. + /// + /// Returns true if requests has been issued. + fn listen_request( + &self, + block: Hash, + sender: Sender>, + ) -> bool; + + /// Returns (and forgets) all listeners for given request. + fn on_response_received(&self, block: Hash) -> Vec>>; +} + +/// Storage subscriptions data. +struct StorageSubscriptions { + /// Active storage requests. + active_requests: HashMap>>>, + /// Map of subscription => keys that this subscription watch for. + keys_by_subscription: HashMap>, + /// Map of key => set of subscriptions that watch this key. + subscriptions_by_key: HashMap>, +} + +impl SharedRequests for Arc>> { + fn listen_request( + &self, + block: Block::Hash, + sender: Sender>, + ) -> bool { + let mut subscriptions = self.lock(); + let active_requests_at = subscriptions.active_requests.entry(block).or_default(); + active_requests_at.push(sender); + active_requests_at.len() == 1 + } + + fn on_response_received(&self, block: Block::Hash) -> Vec>> { + self.lock().active_requests.remove(&block).unwrap_or_default() + } +} + +/// Simple, maybe shared, subscription data that shares per block requests. +type SimpleSubscriptions = Arc>>>>>; + +impl SharedRequests for SimpleSubscriptions where + Hash: Send + Eq + std::hash::Hash, + V: Send, +{ + fn listen_request( + &self, + block: Hash, + sender: Sender>, + ) -> bool { + let mut subscriptions = self.lock(); + let active_requests_at = subscriptions.entry(block).or_default(); + active_requests_at.push(sender); + active_requests_at.len() == 1 + } + + fn on_response_received(&self, block: Hash) -> Vec>> { + self.lock().remove(&block).unwrap_or_default() + } +} + impl + 'static, B, E, RA> LightState where Block: BlockT, @@ -61,39 +144,31 @@ impl + 'static, B, E, RA> LightState + Send + Sync + 'static + Clone, RA: Send + Sync + 'static, { - /// + /// Create new state API backend for light nodes. pub fn new( client: Arc>, subscriptions: Subscriptions, remote_blockchain: Arc>, fetcher: Arc, ) -> Self { - Self { client, subscriptions, remote_blockchain, fetcher, } + Self { + client, + subscriptions, + version_subscriptions: Arc::new(Mutex::new(HashMap::new())), + storage_subscriptions: Arc::new(Mutex::new(StorageSubscriptions { + active_requests: HashMap::new(), + keys_by_subscription: HashMap::new(), + subscriptions_by_key: HashMap::new(), + })), + remote_blockchain, + fetcher, + } } /// Returns given block hash or best block hash if None is passed. fn block_or_best(&self, hash: Option) -> Block::Hash { hash.unwrap_or_else(|| self.client.info().chain.best_hash) } - - /// Resolve header by hash. - fn resolve_header( - &self, - block: Option, - ) -> impl std::future::Future> { - let block = self.block_or_best(block); - let maybe_header = future_header( - &*self.remote_blockchain, - &*self.fetcher, - BlockId::Hash(block), - ); - - maybe_header.then(move |result| - ready(result.and_then(|maybe_header| - maybe_header.ok_or(ClientError::UnknownBlock(format!("{}", block))) - ).map_err(client_err)), - ) - } } impl StateBackend for LightState @@ -104,34 +179,19 @@ impl StateBackend for LightState + 'static { - fn client(&self) -> &Arc> { - &self.client - } - - fn subscriptions(&self) -> &Subscriptions { - &self.subscriptions - } - fn call( &self, block: Option, method: String, call_data: Bytes, ) -> FutureResult { - let fetcher = self.fetcher.clone(); - let call_result = self.resolve_header(block) - .then(move |result| match result { - Ok(header) => Either::Left(fetcher.remote_call(RemoteCallRequest { - block: header.hash(), - header, - method, - call_data: call_data.0, - retry_count: Default::default(), - }).then(|result| ready(result.map(Bytes).map_err(client_err)))), - Err(error) => Either::Right(ready(Err(error))), - }); - - Box::new(call_result.boxed().compat()) + Box::new(call( + &*self.remote_blockchain, + self.fetcher.clone(), + self.block_or_best(block), + method, + call_data, + ).boxed().compat()) } fn storage_keys( @@ -147,26 +207,15 @@ impl StateBackend for LightState, key: StorageKey, ) -> FutureResult> { - let fetcher = self.fetcher.clone(); - let storage = self.resolve_header(block) - .then(move |result| match result { - Ok(header) => Either::Left(fetcher.remote_read(RemoteReadRequest { - block: header.hash(), - header, - keys: vec![key.0.clone()], - retry_count: Default::default(), - }).then(move |result| ready(result - .map(|mut data| data - .remove(&key.0) - .expect("successful result has entry for all keys; qed") - .map(StorageData) - ) - .map_err(client_err) - ))), - Err(error) => Either::Right(ready(Err(error))), - }); - - Box::new(storage.boxed().compat()) + Box::new(storage( + &*self.remote_blockchain, + self.fetcher.clone(), + self.block_or_best(block), + vec![key.0.clone()], + ).boxed().compat().map(move |mut values| values + .remove(&key) + .expect("successful request has entries for all requested keys; qed") + )) } fn storage_hash( @@ -197,11 +246,12 @@ impl StateBackend for LightState FutureResult> { + let block = self.block_or_best(block); let fetcher = self.fetcher.clone(); - let child_storage = self.resolve_header(block) + let child_storage = resolve_header(&*self.remote_blockchain, &*self.fetcher, block) .then(move |result| match result { Ok(header) => Either::Left(fetcher.remote_read_child(RemoteReadChildRequest { - block: header.hash(), + block, header, storage_key: child_storage_key.0, keys: vec![key.0.clone()], @@ -247,12 +297,11 @@ impl StateBackend for LightState) -> FutureResult { - let version = self.call(block, "Core_version".into(), Bytes(Vec::new())) - .and_then(|version| Decode::decode(&mut &version.0[..]) - .map_err(|_| client_err(ClientError::VersionInvalid)) - ); - - Box::new(version) + Box::new(runtime_version( + &*self.remote_blockchain, + self.fetcher.clone(), + self.block_or_best(block), + ).boxed().compat()) } fn query_storage( @@ -267,31 +316,469 @@ impl StateBackend for LightState>, - _keys: Option> + subscriber: Subscriber>, + keys: Option> ) { + let keys = match keys { + Some(keys) => keys, + None => { + warn!("Cannot subscribe to all keys on light client. Subscription rejected."); + return; + } + }; + + let keys = keys.iter().cloned().collect::>(); + let keys_to_check = keys.iter().map(|k| k.0.clone()).collect::>(); + let subscription_id = self.subscriptions.add(subscriber, move |sink| { + let fetcher = self.fetcher.clone(); + let remote_blockchain = self.remote_blockchain.clone(); + let storage_subscriptions = self.storage_subscriptions.clone(); + let initial_block = self.block_or_best(None); + let initial_keys = keys_to_check.iter().cloned().collect::>(); + + let changes_stream = subscription_stream::( + storage_subscriptions.clone(), + self.client + .import_notification_stream() + .map(|notification| Ok::<_, ()>(notification.hash)) + .compat(), + display_error(storage( + &*remote_blockchain, + fetcher.clone(), + initial_block, + initial_keys, + ).map(move |r| r.map(|r| (initial_block, r)))), + move |block| { + // there'll be single request per block for all active subscriptions + // with all subscribed keys + let keys = storage_subscriptions + .lock() + .subscriptions_by_key + .keys() + .map(|k| k.0.clone()) + .collect(); + + storage( + &*remote_blockchain, + fetcher.clone(), + block, + keys, + ) + }, + move |block, old_value, new_value| { + // let's only select keys which are valid for this subscription + let new_value = new_value + .iter() + .filter(|(k, _)| keys_to_check.contains(&k.0)) + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + let value_differs = old_value + .as_ref() + .map(|old_value| **old_value != new_value) + .unwrap_or(true); + match value_differs { + true => Some(StorageChangeSet { + block, + changes: new_value + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(), + }), + false => None, + } + } + ); + + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(changes_stream.map(|changes| Ok(changes))) + // we ignore the resulting Stream (if the first stream is over we are unsubscribed) + .map(|_| ()) + }); + + // remember keys associated with this subscription + let mut storage_subscriptions = self.storage_subscriptions.lock(); + storage_subscriptions.keys_by_subscription.insert(subscription_id.clone(), keys.clone()); + for key in keys { + storage_subscriptions + .subscriptions_by_key + .entry(key) + .or_default() + .insert(subscription_id.clone()); + } } fn unsubscribe_storage( &self, _meta: Option, - _id: SubscriptionId, + id: SubscriptionId, ) -> RpcResult { - Ok(false) + if !self.subscriptions.cancel(id.clone()) { + return Ok(false); + } + + // forget subscription keys + let mut storage_subscriptions = self.storage_subscriptions.lock(); + let keys = storage_subscriptions.keys_by_subscription.remove(&id); + for key in keys.into_iter().flat_map(|keys| keys.into_iter()) { + match storage_subscriptions.subscriptions_by_key.entry(key) { + Entry::Vacant(_) => unreachable!("every key from keys_by_subscription has\ + corresponding entry in subscriptions_by_key; qed"), + Entry::Occupied(mut entry) => { + entry.get_mut().remove(&id); + if entry.get().is_empty() { + entry.remove(); + } + } + } + } + + Ok(true) } fn subscribe_runtime_version( &self, _meta: crate::metadata::Metadata, - _subscriber: Subscriber, + subscriber: Subscriber, ) { + self.subscriptions.add(subscriber, move |sink| { + let fetcher = self.fetcher.clone(); + let remote_blockchain = self.remote_blockchain.clone(); + let version_subscriptions = self.version_subscriptions.clone(); + let initial_block = self.block_or_best(None); + + let versions_stream = subscription_stream::( + version_subscriptions, + self.client + .import_notification_stream() + .map(|notification| Ok::<_, ()>(notification.hash)) + .compat(), + display_error(runtime_version( + &*remote_blockchain, + fetcher.clone(), + initial_block, + ).map(move |r| r.map(|r| (initial_block, r)))), + move |block| runtime_version( + &*remote_blockchain, + fetcher.clone(), + block, + ), + |_, old_version, new_version| { + let version_differs = old_version + .as_ref() + .map(|old_version| *old_version != new_version) + .unwrap_or(true); + match version_differs { + true => Some(new_version.clone()), + false => None, + } + } + ); + + sink + .sink_map_err(|e| warn!("Error sending notifications: {:?}", e)) + .send_all(versions_stream.map(|version| Ok(version))) + // we ignore the resulting Stream (if the first stream is over we are unsubscribed) + .map(|_| ()) + }); } fn unsubscribe_runtime_version( &self, _meta: Option, - _id: SubscriptionId, + id: SubscriptionId, ) -> RpcResult { - Ok(false) + Ok(self.subscriptions.cancel(id)) + } +} + +/// Resolve header by hash. +fn resolve_header>( + remote_blockchain: &dyn RemoteBlockchain, + fetcher: &F, + block: Block::Hash, +) -> impl std::future::Future> { + let maybe_header = future_header( + remote_blockchain, + fetcher, + BlockId::Hash(block), + ); + + maybe_header.then(move |result| + ready(result.and_then(|maybe_header| + maybe_header.ok_or(ClientError::UnknownBlock(format!("{}", block))) + ).map_err(client_err)), + ) +} + +/// Call runtime method at given block +fn call>( + remote_blockchain: &dyn RemoteBlockchain, + fetcher: Arc, + block: Block::Hash, + method: String, + call_data: Bytes, +) -> impl std::future::Future> { + resolve_header(remote_blockchain, &*fetcher, block) + .then(move |result| match result { + Ok(header) => Either::Left(fetcher.remote_call(RemoteCallRequest { + block, + header, + method, + call_data: call_data.0, + retry_count: Default::default(), + }).then(|result| ready(result.map(Bytes).map_err(client_err)))), + Err(error) => Either::Right(ready(Err(error))), + }) +} + +/// Get runtime version at given block. +fn runtime_version>( + remote_blockchain: &dyn RemoteBlockchain, + fetcher: Arc, + block: Block::Hash, +) -> impl std::future::Future> { + call( + remote_blockchain, + fetcher, + block, + "Core_version".into(), + Bytes(Vec::new()), + ) + .then(|version| ready(version.and_then(|version| + Decode::decode(&mut &version.0[..]).map_err(|_| client_err(ClientError::VersionInvalid)) + ))) +} + +/// Get storage value at given key at given block. +fn storage>( + remote_blockchain: &dyn RemoteBlockchain, + fetcher: Arc, + block: Block::Hash, + keys: Vec>, +) -> impl std::future::Future>, Error>> { + resolve_header(remote_blockchain, &*fetcher, block) + .then(move |result| match result { + Ok(header) => Either::Left(fetcher.remote_read(RemoteReadRequest { + block, + header, + keys, + retry_count: Default::default(), + }).then(|result| ready(result + .map(|result| result + .into_iter() + .map(|(key, value)| (StorageKey(key), value.map(StorageData))) + .collect() + ).map_err(client_err) + ))), + Err(error) => Either::Right(ready(Err(error))), + }) +} + +/// Returns subscription stream that issues request on every imported block and +/// if value has changed from previous block, emits (stream) item. +fn subscription_stream< + Block, + Requests, + FutureBlocksStream, + V, N, + InitialRequestFuture, + IssueRequest, IssueRequestFuture, + CompareValues, +>( + shared_requests: Requests, + future_blocks_stream: FutureBlocksStream, + initial_request: InitialRequestFuture, + issue_request: IssueRequest, + compare_values: CompareValues, +) -> impl Stream where + Block: BlockT, + Requests: 'static + SharedRequests, + FutureBlocksStream: Stream, + V: Send + 'static + Clone, + InitialRequestFuture: std::future::Future> + Send + 'static, + IssueRequest: 'static + Fn(Block::Hash) -> IssueRequestFuture, + IssueRequestFuture: std::future::Future> + Send + 'static, + CompareValues: Fn(Block::Hash, Option<&V>, &V) -> Option, +{ + // we need to send initial value first, then we'll only be sending if value has changed + let previous_value = Arc::new(Mutex::new(None)); + + // prepare 'stream' of initial values + let initial_value_stream = ignore_error(initial_request) + .boxed() + .compat() + .into_stream(); + + // prepare stream of future values + // + // we do not want to stop stream if single request fails + // (the warning should have been already issued by the request issuer) + let future_values_stream = future_blocks_stream + .and_then(move |block| ignore_error(maybe_share_remote_request::( + shared_requests.clone(), + block, + &issue_request, + ).map(move |r| r.map(|v| (block, v)))).boxed().compat()); + + // now let's return changed values for selected blocks + initial_value_stream + .chain(future_values_stream) + .filter_map(move |block_and_new_value| block_and_new_value.and_then(|(block, new_value)| { + let mut previous_value = previous_value.lock(); + compare_values(block, previous_value.as_ref(), &new_value) + .map(|notification_value| { + *previous_value = Some(new_value); + notification_value + }) + })) + .map_err(|_| ()) +} + +/// Request some data from remote node, probably reusing response from already +/// (in-progress) existing request. +fn maybe_share_remote_request( + shared_requests: Requests, + block: Block::Hash, + issue_request: &IssueRequest, +) -> impl std::future::Future> where + V: Clone, + Requests: SharedRequests, + IssueRequest: Fn(Block::Hash) -> IssueRequestFuture, + IssueRequestFuture: std::future::Future>, +{ + let (sender, receiver) = channel(); + let need_issue_request = shared_requests.listen_request(block, sender); + + // if that isn't the first request - just listen for existing request' response + if !need_issue_request { + return Either::Right(receiver.then(|r| ready(r.unwrap_or(Err(()))))); + } + + // that is the first request - issue remote request + notify all listeners on + // completion + Either::Left( + display_error(issue_request(block)) + .then(move |remote_result| { + let listeners = shared_requests.on_response_received(block); + // skip first element, because this future is the first element + for receiver in listeners.into_iter().skip(1) { + if let Err(_) = receiver.send(remote_result.clone()) { + // we don't care if receiver has been dropped already + } + } + + ready(remote_result) + }) + ) +} + +/// Convert successful future result into Ok(result) and error into Err(()), +/// displaying warning. +fn display_error(future: F) -> impl std::future::Future> where + F: std::future::Future> +{ + future.then(|result| ready(match result { + Ok(result) => Ok(result), + Err(err) => { + warn!("Remote request for subscription data has failed with: {:?}", err); + Err(()) + }, + })) +} + +/// Convert successful future result into Ok(Some(result)) and error into Ok(None), +/// displaying warning. +fn ignore_error(future: F) -> impl std::future::Future, ()>> where + F: std::future::Future> +{ + future.then(|result| ready(match result { + Ok(result) => Ok(Some(result)), + Err(()) => Ok(None), + })) +} + +#[cfg(test)] +mod tests { + use rpc::futures::stream::futures_ordered; + use test_client::runtime::Block; + use super::*; + + #[test] + fn subscription_stream_works() { + let stream = subscription_stream::( + SimpleSubscriptions::default(), + futures_ordered(vec![result(Ok(H256::from([2; 32]))), result(Ok(H256::from([3; 32])))]), + ready(Ok((H256::from([1; 32]), 100))), + |block| match block[0] { + 2 => ready(Ok(100)), + 3 => ready(Ok(200)), + _ => unreachable!("should not issue additional requests"), + }, + |_, old_value, new_value| match old_value == Some(new_value) { + true => None, + false => Some(new_value.clone()), + } + ); + + assert_eq!( + stream.collect().wait(), + Ok(vec![100, 200]) + ); + } + + #[test] + fn subscription_stream_ignores_failed_requests() { + let stream = subscription_stream::( + SimpleSubscriptions::default(), + futures_ordered(vec![result(Ok(H256::from([2; 32]))), result(Ok(H256::from([3; 32])))]), + ready(Ok((H256::from([1; 32]), 100))), + |block| match block[0] { + 2 => ready(Err(client_err(ClientError::NotAvailableOnLightClient))), + 3 => ready(Ok(200)), + _ => unreachable!("should not issue additional requests"), + }, + |_, old_value, new_value| match old_value == Some(new_value) { + true => None, + false => Some(new_value.clone()), + } + ); + + assert_eq!( + stream.collect().wait(), + Ok(vec![100, 200]) + ); + } + + #[test] + fn maybe_share_remote_request_shares_request() { + type UnreachableFuture = futures03::future::Ready>; + + let shared_requests = SimpleSubscriptions::default(); + + // let's 'issue' requests for B1 + shared_requests.lock().insert( + H256::from([1; 32]), + vec![channel().0], + ); + + // make sure that no additional requests are issued when we're asking for B1 + let _ = maybe_share_remote_request::( + shared_requests.clone(), + H256::from([1; 32]), + &|_| unreachable!("no duplicate requests issued"), + ); + + // make sure that additional requests is issued when we're asking for B2 + let request_issued = Arc::new(Mutex::new(false)); + let _ = maybe_share_remote_request::( + shared_requests.clone(), + H256::from([2; 32]), + &|_| { + *request_issued.lock() = true; + ready(Ok(Default::default())) + }, + ); + assert!(*request_issued.lock()); } } diff --git a/core/rpc/src/state/tests.rs b/core/rpc/src/state/tests.rs index ba1aac4cc3eb4810a388587dd9217c5853196e41..5dfa234337afa18f8d511226ab53dd2f9d0ebd94 100644 --- a/core/rpc/src/state/tests.rs +++ b/core/rpc/src/state/tests.rs @@ -282,7 +282,7 @@ fn should_return_runtime_version() { \"specVersion\":1,\"implVersion\":1,\"apis\":[[\"0xdf6acb689907609b\",2],\ [\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",1],[\"0x40fe3ad401f8959a\",3],\ [\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",1],\ - [\"0xf78b278be53f454c\",1],[\"0xab3c0572291feb8b\",1]]}"; + [\"0xf78b278be53f454c\",1],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]]}"; let runtime_version = api.runtime_version(None.into()).wait().unwrap(); let serialized = serde_json::to_string(&runtime_version).unwrap(); @@ -313,3 +313,11 @@ fn should_notify_on_runtime_version_initially() { // no more notifications on this channel assert_eq!(core.block_on(next.into_future()).unwrap().0, None); } + +#[test] +fn should_deserialize_storage_key() { + let k = "\"0x7f864e18e3dd8b58386310d2fe0919eef27c6e558564b7f67f22d99d20f587b\""; + let k: StorageKey = serde_json::from_str(k).unwrap(); + + assert_eq!(k.0.len(), 32); +} diff --git a/core/rpc/src/system/mod.rs b/core/rpc/src/system/mod.rs index 8eeff6758b0b1f036bc1c3f4d00c4bbbfdd9ed3e..8907151d9ac6984bb8396c3f7af263b1b9994ee3 100644 --- a/core/rpc/src/system/mod.rs +++ b/core/rpc/src/system/mod.rs @@ -25,7 +25,7 @@ use sr_primitives::traits::{self, Header as HeaderT}; use self::error::Result; pub use api::system::*; -pub use self::helpers::{Properties, SystemInfo, Health, PeerInfo}; +pub use self::helpers::{Properties, SystemInfo, Health, PeerInfo, NodeRole}; pub use self::gen_client::Client as SystemClient; /// System API implementation @@ -42,6 +42,8 @@ pub enum Request { Peers(oneshot::Sender::Number>>>), /// Must return the state of the network. NetworkState(oneshot::Sender), + /// Must return the node role. + NodeRoles(oneshot::Sender>) } impl System { @@ -94,4 +96,10 @@ impl SystemApi::Number> for Sy let _ = self.send_back.unbounded_send(Request::NetworkState(tx)); Receiver(Compat::new(rx)) } + + fn system_node_roles(&self) -> Receiver> { + let (tx, rx) = oneshot::channel(); + let _ = self.send_back.unbounded_send(Request::NodeRoles(tx)); + Receiver(Compat::new(rx)) + } } diff --git a/core/rpc/src/system/tests.rs b/core/rpc/src/system/tests.rs index 5b271af9ac23a747fd3a84ae6b45fbd8ea444cab..1c532be372660fbee4c8210a56c15842413e553e 100644 --- a/core/rpc/src/system/tests.rs +++ b/core/rpc/src/system/tests.rs @@ -79,6 +79,9 @@ fn api>>(sync: T) -> System { average_upload_per_sec: 0, peerset: serde_json::Value::Null, }).unwrap()); + }, + Request::NodeRoles(sender) => { + let _ = sender.send(vec![NodeRole::Authority]); } }; @@ -221,3 +224,11 @@ fn system_network_state() { } ); } + +#[test] +fn system_node_roles() { + assert_eq!( + wait_receiver(api(None).system_node_roles()), + vec![NodeRole::Authority] + ); +} \ No newline at end of file diff --git a/core/serializer/Cargo.toml b/core/serializer/Cargo.toml index b6dfa3f470c7fb51efa88f5e0920fa308b4b35ce..4e0e706e2fab232ff516ade2bfa22a2ed9ad598c 100644 --- a/core/serializer/Cargo.toml +++ b/core/serializer/Cargo.toml @@ -6,4 +6,4 @@ edition = "2018" [dependencies] serde = "1.0.101" -serde_json = "1.0.40" +serde_json = "1.0.41" diff --git a/core/service/Cargo.toml b/core/service/Cargo.toml index 5d8abee11696b9b61a19c60d2af6e7ed146245c5..3d40550c8c5b5e6bee712b346871459434586c2d 100644 --- a/core/service/Cargo.toml +++ b/core/service/Cargo.toml @@ -4,6 +4,15 @@ version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +[features] +default = ["rocksdb"] +# The RocksDB feature activates the RocksDB database backend. If it is not activated, and you pass +# a path to a database, an error will be produced at runtime. +rocksdb = ["client_db/kvdb-rocksdb"] +wasmtime = [ + "substrate-executor/wasmtime", +] + [dependencies] derive_more = "0.15.0" futures = "0.1.29" @@ -16,7 +25,7 @@ tokio-executor = "0.1.8" tokio-timer = "0.2.11" exit-future = "0.1.4" serde = "1.0.101" -serde_json = "1.0.40" +serde_json = "1.0.41" sysinfo = "0.9.5" target_info = "0.1.0" keystore = { package = "substrate-keystore", path = "../../core/keystore" } @@ -29,17 +38,15 @@ consensus_common = { package = "substrate-consensus-common", path = "../../core/ network = { package = "substrate-network", path = "../../core/network" } chain-spec = { package = "substrate-chain-spec", path = "../chain-spec" } client = { package = "substrate-client", path = "../../core/client" } -client_db = { package = "substrate-client-db", path = "../../core/client/db", features = ["kvdb-rocksdb"] } +client_db = { package = "substrate-client-db", path = "../../core/client/db" } codec = { package = "parity-scale-codec", version = "1.0.0" } substrate-executor = { path = "../../core/executor" } -substrate-authority-discovery = { path = "../../core/authority-discovery"} transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } rpc-servers = { package = "substrate-rpc-servers", path = "../../core/rpc-servers" } rpc = { package = "substrate-rpc", path = "../../core/rpc" } tel = { package = "substrate-telemetry", path = "../../core/telemetry" } offchain = { package = "substrate-offchain", path = "../../core/offchain" } parity-multiaddr = { package = "parity-multiaddr", version = "0.5.0" } -authority-discovery-primitives = { package = "substrate-authority-discovery-primitives", path = "../authority-discovery/primitives", default-features = false } [dev-dependencies] substrate-test-runtime-client = { path = "../test-runtime/client" } @@ -49,3 +56,4 @@ node-runtime = { path = "../../node/runtime" } babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives" } grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa" } grandpa-primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" } +tokio = "0.1" diff --git a/core/service/src/builder.rs b/core/service/src/builder.rs index f0860de1c1cfa0e2139e27010afa76b1141855a5..03db6e385b025daa296b77eda0629ddcf43e1eac 100644 --- a/core/service/src/builder.rs +++ b/core/service/src/builder.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{NewService, NetworkStatus, NetworkState, error::{self, Error}, DEFAULT_PROTOCOL_ID}; +use crate::{Service, NetworkStatus, NetworkState, error::{self, Error}, DEFAULT_PROTOCOL_ID}; use crate::{SpawnTaskHandle, start_rpc_servers, build_network_future, TransactionPoolAdapter}; -use crate::TaskExecutor; -use crate::config::Configuration; +use crate::status_sinks; +use crate::config::{Configuration, DatabaseConfig}; use client::{ BlockchainEvents, Client, runtime_api, backend::RemoteBackend, light::blockchain::RemoteBlockchain, @@ -32,13 +32,13 @@ use futures03::{ FutureExt as _, TryFutureExt as _, StreamExt as _, TryStreamExt as _, }; -use keystore::{Store as Keystore, KeyStorePtr}; +use keystore::{Store as Keystore}; use log::{info, warn}; use network::{FinalityProofProvider, OnDemand, NetworkService, NetworkStateInfo, DhtEvent}; use network::{config::BoxFinalityProofRequestBuilder, specialization::NetworkSpecialization}; use parking_lot::{Mutex, RwLock}; use primitives::{Blake2Hasher, H256, Hasher}; -use rpc::{self, system::SystemInfo}; +use rpc; use sr_primitives::generic::BlockId; use sr_primitives::traits::{ Block as BlockT, Extrinsic, ProvideRuntimeApi, NumberFor, One, Zero, Header, SaturatedConversion @@ -68,7 +68,7 @@ use transaction_pool::txpool::{self, ChainApi, Pool as TransactionPool}; /// generics is done when you call `build`. /// pub struct ServiceBuilder + TNetP, TExPool, TRpc, Backend> { config: Configuration, client: Arc, @@ -82,7 +82,7 @@ pub struct ServiceBuilder, rpc_extensions: TRpc, - rpc_builder: TRpcB, + remote_backend: Option>>, dht_event_tx: Option>, marker: PhantomData<(TBl, TRtApi)>, } @@ -133,7 +133,7 @@ type TLightCallExecutor = client::light::call_executor::GenesisC >, >; -impl ServiceBuilder<(), (), TCfg, TGen, TCSExt, (), (), (), (), (), (), (), (), (), (), ()> +impl ServiceBuilder<(), (), TCfg, TGen, TCSExt, (), (), (), (), (), (), (), (), (), ()> where TGen: RuntimeGenesis, TCSExt: Extension { /// Start the service builder with a configuration. pub fn new_full, TRtApi, TExecDisp: NativeExecutionDispatch>( @@ -149,25 +149,21 @@ where TGen: RuntimeGenesis, TCSExt: Extension { (), (), BoxFinalityProofRequestBuilder, + Arc>, (), (), (), - (), - FullRpcBuilder, TFullBackend, >, Error> { - let keystore = Keystore::open(config.keystore_path.clone(), config.keystore_password.clone())?; - - let db_settings = client_db::DatabaseSettings { - cache_size: None, - state_cache_size: config.state_cache_size, - state_cache_child_ratio: - config.state_cache_child_ratio.map(|v| (v, 100)), - path: config.database_path.clone(), - pruning: config.pruning.clone(), - }; + let keystore = Keystore::open( + config.keystore_path.clone().ok_or("No basepath configured")?, + config.keystore_password.clone() + )?; - let executor = NativeExecutor::::new(config.default_heap_pages); + let executor = NativeExecutor::::new( + config.wasm_method, + config.default_heap_pages, + ); let fork_blocks = config.chain_spec .extensions() @@ -175,19 +171,35 @@ where TGen: RuntimeGenesis, TCSExt: Extension { .cloned() .unwrap_or_default(); - let (client, backend) = client_db::new_client( - db_settings, - executor, - &config.chain_spec, - fork_blocks, - config.execution_strategies.clone(), - Some(keystore.clone()), - )?; + let (client, backend) = { + let db_config = client_db::DatabaseSettings { + state_cache_size: config.state_cache_size, + state_cache_child_ratio: + config.state_cache_child_ratio.map(|v| (v, 100)), + pruning: config.pruning.clone(), + source: match &config.database { + DatabaseConfig::Path { path, cache_size } => + client_db::DatabaseSettingsSrc::Path { + path: path.clone(), + cache_size: cache_size.clone().map(|u| u as usize), + }, + DatabaseConfig::Custom(db) => + client_db::DatabaseSettingsSrc::Custom(db.clone()), + }, + }; + + client_db::new_client( + db_config, + executor, + &config.chain_spec, + fork_blocks, + config.execution_strategies.clone(), + Some(keystore.clone()), + )? + }; let client = Arc::new(client); - let rpc_builder = FullRpcBuilder { client: client.clone() }; - Ok(ServiceBuilder { config, client, @@ -201,7 +213,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension { network_protocol: (), transaction_pool: Arc::new(()), rpc_extensions: Default::default(), - rpc_builder, + remote_backend: None, dht_event_tx: None, marker: PhantomData, }) @@ -221,27 +233,40 @@ where TGen: RuntimeGenesis, TCSExt: Extension { (), (), BoxFinalityProofRequestBuilder, + Arc>, (), (), (), - (), - LightRpcBuilder, TLightBackend, >, Error> { - let keystore = Keystore::open(config.keystore_path.clone(), config.keystore_password.clone())?; - - let db_settings = client_db::DatabaseSettings { - cache_size: config.database_cache_size.map(|u| u as usize), - state_cache_size: config.state_cache_size, - state_cache_child_ratio: - config.state_cache_child_ratio.map(|v| (v, 100)), - path: config.database_path.clone(), - pruning: config.pruning.clone(), - }; - - let executor = NativeExecutor::::new(config.default_heap_pages); + let keystore = Keystore::open( + config.keystore_path.clone().ok_or("No basepath configured")?, + config.keystore_password.clone() + )?; - let db_storage = client_db::light::LightStorage::new(db_settings)?; + let executor = NativeExecutor::::new( + config.wasm_method, + config.default_heap_pages, + ); + + let db_storage = { + let db_settings = client_db::DatabaseSettings { + state_cache_size: config.state_cache_size, + state_cache_child_ratio: + config.state_cache_child_ratio.map(|v| (v, 100)), + pruning: config.pruning.clone(), + source: match &config.database { + DatabaseConfig::Path { path, cache_size } => + client_db::DatabaseSettingsSrc::Path { + path: path.clone(), + cache_size: cache_size.clone().map(|u| u as usize), + }, + DatabaseConfig::Custom(db) => + client_db::DatabaseSettingsSrc::Custom(db.clone()), + }, + }; + client_db::light::LightStorage::new(db_settings)? + }; let light_blockchain = client::light::new_light_blockchain(db_storage); let fetch_checker = Arc::new(client::light::new_fetch_checker(light_blockchain.clone(), executor.clone())); let fetcher = Arc::new(network::OnDemand::new(fetch_checker)); @@ -252,11 +277,6 @@ where TGen: RuntimeGenesis, TCSExt: Extension { &config.chain_spec, executor, )?); - let rpc_builder = LightRpcBuilder { - client: client.clone(), - remote_blockchain, - fetcher: fetcher.clone(), - }; Ok(ServiceBuilder { config, @@ -271,16 +291,16 @@ where TGen: RuntimeGenesis, TCSExt: Extension { network_protocol: (), transaction_pool: Arc::new(()), rpc_extensions: Default::default(), - rpc_builder, + remote_backend: Some(remote_blockchain), dht_event_tx: None, marker: PhantomData, }) } } -impl +impl ServiceBuilder { + TNetP, TExPool, TRpc, Backend> { /// Returns a reference to the client that was stored in this builder. pub fn client(&self) -> &Arc { @@ -304,7 +324,7 @@ impl, &Arc ) -> Result, Error> ) -> Result, Error> { + TNetP, TExPool, TRpc, Backend>, Error> { let select_chain = select_chain_builder(&self.config, &self.backend)?; Ok(ServiceBuilder { @@ -320,7 +340,7 @@ impl, &Arc) -> Result ) -> Result, Error> { + TNetP, TExPool, TRpc, Backend>, Error> { self.with_opt_select_chain(|cfg, b| builder(cfg, b).map(Option::Some)) } @@ -341,7 +361,7 @@ impl, Arc, Option, Arc) -> Result ) -> Result, Error> + TNetP, TExPool, TRpc, Backend>, Error> where TSc: Clone { let import_queue = builder( &self.config, @@ -363,7 +383,7 @@ impl) -> Result ) -> Result, Error> { + UNetP, TExPool, TRpc, Backend>, Error> { let network_protocol = network_protocol_builder(&self.config)?; Ok(ServiceBuilder { @@ -390,7 +410,7 @@ impl, Error> { let finality_proof_provider = builder(self.client.clone(), self.backend.clone())?; @@ -433,7 +452,7 @@ impl, Error> { self.with_opt_finality_proof_provider(|client, backend| build(client, backend).map(Option::Some)) @@ -476,7 +494,7 @@ impl, ) -> Result<(UImpQu, Option), Error> ) -> Result, Error> + TNetP, TExPool, TRpc, Backend>, Error> where TSc: Clone, TFchr: Clone { let (import_queue, fprb) = builder( &self.config, @@ -500,7 +518,7 @@ impl, ) -> Result<(UImpQu, UFprb), Error> ) -> Result, Error> + TNetP, TExPool, TRpc, Backend>, Error> where TSc: Clone, TFchr: Clone { self.with_import_queue_and_opt_fprb(|cfg, cl, b, f, sc, tx| builder(cfg, cl, b, f, sc, tx) @@ -531,7 +549,7 @@ impl) -> Result ) -> Result, Error> { + TNetP, UExPool, TRpc, Backend>, Error> { let transaction_pool = transaction_pool_builder(self.config.transaction_pool.clone(), self.client.clone())?; Ok(ServiceBuilder { @@ -547,7 +565,7 @@ impl( self, - rpc_ext_builder: impl FnOnce(Arc, Arc) -> URpc + rpc_ext_builder: impl FnOnce(Arc, Arc, Arc) -> URpc ) -> Result, Error> { - let rpc_extensions = rpc_ext_builder(self.client.clone(), self.transaction_pool.clone()); + TNetP, TExPool, URpc, Backend>, Error> { + let rpc_extensions = rpc_ext_builder(self.client.clone(), self.transaction_pool.clone(), self.backend.clone()); Ok(ServiceBuilder { config: self.config, @@ -574,114 +592,36 @@ impl, - ) -> Result, Error> { - Ok(ServiceBuilder { - config: self.config, - client: self.client, - backend: self.backend, - keystore: self.keystore, - fetcher: self.fetcher, - select_chain: self.select_chain, - import_queue: self.import_queue, - finality_proof_request_builder: self.finality_proof_request_builder, - finality_proof_provider: self.finality_proof_provider, - network_protocol: self.network_protocol, - transaction_pool: self.transaction_pool, - rpc_extensions: self.rpc_extensions, - rpc_builder: self.rpc_builder, - dht_event_tx: Some(dht_event_tx), - marker: self.marker, - }) - } -} - -/// RPC handlers builder. -pub trait RpcBuilder { - /// Build chain RPC handler. - fn build_chain(&self, subscriptions: rpc::Subscriptions) -> rpc::chain::Chain; - /// Build state RPC handler. - fn build_state(&self, subscriptions: rpc::Subscriptions) -> rpc::state::State; -} - -/// RPC handlers builder for full nodes. -pub struct FullRpcBuilder { - client: Arc>, -} - -impl RpcBuilder, TFullCallExecutor, TRtApi> - for - FullRpcBuilder - where - TBl: BlockT, - TRtApi: 'static + Send + Sync, - TExecDisp: 'static + NativeExecutionDispatch, - TFullClient: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: runtime_api::Metadata, -{ - fn build_chain( - &self, - subscriptions: rpc::Subscriptions, - ) -> rpc::chain::Chain, TFullCallExecutor, TBl, TRtApi> { - rpc::chain::new_full(self.client.clone(), subscriptions) - } - - fn build_state( - &self, - subscriptions: rpc::Subscriptions, - ) -> rpc::state::State, TFullCallExecutor, TBl, TRtApi> { - rpc::state::new_full(self.client.clone(), subscriptions) - } -} - -/// RPC handlers builder for light nodes. -pub struct LightRpcBuilder, TRtApi, TExecDisp> { - client: Arc>, - remote_blockchain: Arc>, - fetcher: Arc>, -} - -impl RpcBuilder, TLightCallExecutor, TRtApi> - for - LightRpcBuilder - where - TBl: BlockT, - TRtApi: 'static + Send + Sync, - TExecDisp: 'static + NativeExecutionDispatch, -{ - fn build_chain( - &self, - subscriptions: rpc::Subscriptions, - ) -> rpc::chain::Chain, TLightCallExecutor, TBl, TRtApi> { - rpc::chain::new_light( - self.client.clone(), - subscriptions, - self.remote_blockchain.clone(), - self.fetcher.clone(), - ) - } - - fn build_state( - &self, - subscriptions: rpc::Subscriptions, - ) -> rpc::state::State, TLightCallExecutor, TBl, TRtApi> { - rpc::state::new_light( - self.client.clone(), - subscriptions, - self.remote_blockchain.clone(), - self.fetcher.clone(), - ) + /// Adds a dht event sender to builder to be used by the network to send dht events to the authority discovery + /// module. + pub fn with_dht_event_tx( + self, + dht_event_tx: mpsc::Sender, + ) -> Result, Error> { + Ok(ServiceBuilder { + config: self.config, + client: self.client, + backend: self.backend, + keystore: self.keystore, + fetcher: self.fetcher, + select_chain: self.select_chain, + import_queue: self.import_queue, + finality_proof_request_builder: self.finality_proof_request_builder, + finality_proof_provider: self.finality_proof_provider, + network_protocol: self.network_protocol, + transaction_pool: self.transaction_pool, + rpc_extensions: self.rpc_extensions, + remote_backend: self.remote_backend, + dht_event_tx: Some(dht_event_tx), + marker: self.marker, + }) } } @@ -729,10 +669,10 @@ pub trait ServiceBuilderRevert { impl< TBl, TRtApi, TCfg, TGen, TCSExt, TBackend, TExec, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, - TExPool, TRpc, TRpcB, Backend + TExPool, TRpc, Backend > ServiceBuilderImport for ServiceBuilder< TBl, TRtApi, TCfg, TGen, TCSExt, Client, - TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, TRpcB, Backend + TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, Backend > where TBl: BlockT::Out>, TBackend: 'static + client::backend::Backend + Send, @@ -752,9 +692,9 @@ impl< } } -impl +impl ServiceBuilderExport for ServiceBuilder, - TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, TRpcB, TBackend> + TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, TBackend> where TBl: BlockT::Out>, TBackend: 'static + client::backend::Backend + Send, @@ -775,9 +715,9 @@ where } } -impl +impl ServiceBuilderRevert for ServiceBuilder, - TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, TRpcB, TBackend> + TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, TBackend> where TBl: BlockT::Out>, TBackend: 'static + client::backend::Backend + Send, @@ -794,7 +734,7 @@ where } } -impl +impl ServiceBuilder< TBl, TRtApi, @@ -810,7 +750,6 @@ ServiceBuilder< TNetP, TransactionPool, TRpc, - TRpcB, TBackend, > where Client: ProvideRuntimeApi, @@ -831,10 +770,9 @@ ServiceBuilder< TNetP: NetworkSpecialization, TExPoolApi: 'static + ChainApi::Hash>, TRpc: rpc::RpcExtension + Clone, - TRpcB: RpcBuilder, { /// Builds the service. - pub fn build(self) -> Result Result, TSc, @@ -851,7 +789,7 @@ ServiceBuilder< marker: _, mut config, client, - fetcher, + fetcher: on_demand, backend, keystore, select_chain, @@ -861,8 +799,8 @@ ServiceBuilder< network_protocol, transaction_pool, rpc_extensions, + remote_backend, dht_event_tx, - rpc_builder, } = self; session::generate_initial_session_keys( @@ -870,74 +808,344 @@ ServiceBuilder< config.dev_key_seed.clone().map(|s| vec![s]).unwrap_or_default() )?; - new_impl!( - TBl, - config, - move |_| -> Result<_, Error> { - Ok(( - client, - fetcher, - backend, - keystore, - select_chain, - import_queue, - finality_proof_request_builder, - finality_proof_provider, - network_protocol, - transaction_pool, - rpc_extensions, - dht_event_tx, - )) + let (signal, exit) = exit_future::signal(); + + // 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>>(); + + let import_queue = Box::new(import_queue); + let chain_info = client.info().chain; + + let version = config.full_version(); + info!("Highest known block at #{}", chain_info.best_number); + telemetry!( + SUBSTRATE_INFO; + "node.start"; + "height" => chain_info.best_number.saturated_into::(), + "best" => ?chain_info.best_hash + ); + + let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { + 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() }), + }); + + let protocol_id = { + let protocol_id_full = match config.chain_spec.protocol_id() { + Some(pid) => pid, + None => { + warn!("Using default protocol ID {:?} because none is configured in the \ + chain specs", DEFAULT_PROTOCOL_ID + ); + DEFAULT_PROTOCOL_ID + } + }.as_bytes(); + network::config::ProtocolId::from(protocol_id_full) + }; + + let block_announce_validator = + Box::new(consensus_common::block_validation::DefaultBlockAnnounceValidator::new(client.clone())); + + let network_params = network::config::Params { + roles: config.roles, + network_config: config.network.clone(), + chain: client.clone(), + finality_proof_provider, + finality_proof_request_builder, + on_demand: on_demand.clone(), + transaction_pool: transaction_pool_adapter.clone() as _, + import_queue, + protocol_id, + specialization: network_protocol, + block_announce_validator, + }; + + let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); + let network_mut = network::NetworkWorker::new(network_params)?; + let network = network_mut.service().clone(); + 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) { + (true, Some(db)) => { + Some(Arc::new(offchain::OffchainWorkers::new(client.clone(), db))) + }, + (true, None) => { + log::warn!("Offchain workers disabled, due to lack of offchain storage support in backend."); + None }, - |h, c, tx, r| maintain_transaction_pool(h, c, tx, r), - |n, o, p, ns, v| offchain_workers(n, o, p, ns, v), - |c, ssb, si, te, tp, ext, ks| start_rpc(&rpc_builder, c, ssb, si, te, tp, ext, ks), + _ => None, + }; + + { + // block notifications + let txpool = Arc::downgrade(&transaction_pool); + let wclient = Arc::downgrade(&client); + let offchain = offchain_workers.as_ref().map(Arc::downgrade); + let to_spawn_tx_ = to_spawn_tx.clone(); + let network_state_info: Arc = network.clone(); + let is_validator = config.roles.is_authority(); + + let events = client.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() + .for_each(move |notification| { + let number = *notification.header.number(); + let txpool = txpool.upgrade(); + + if let (Some(txpool), Some(client)) = (txpool.as_ref(), wclient.upgrade()) { + let future = maintain_transaction_pool( + &BlockId::hash(notification.hash), + &client, + &*txpool, + ¬ification.retracted, + ).map_err(|e| warn!("Pool error processing new block: {:?}", e))?; + let _ = to_spawn_tx_.unbounded_send(future); + } + + let offchain = offchain.as_ref().and_then(|o| o.upgrade()); + if let (Some(txpool), Some(offchain)) = (txpool, offchain) { + let future = offchain.on_block_imported(&number, &txpool, network_state_info.clone(), is_validator) + .map(|()| Ok(())); + let _ = to_spawn_tx_.unbounded_send(Box::new(Compat::new(future))); + } + + Ok(()) + }) + .select(exit.clone()) + .then(|_| Ok(())); + let _ = to_spawn_tx.unbounded_send(Box::new(events)); + } + + { + // extrinsic notifications + let network = Arc::downgrade(&network); + let transaction_pool_ = transaction_pool.clone(); + let events = transaction_pool.import_notification_stream() + .map(|v| Ok::<_, ()>(v)).compat() + .for_each(move |_| { + if let Some(network) = network.upgrade() { + network.trigger_repropagate(); + } + let status = transaction_pool_.status(); + telemetry!(SUBSTRATE_INFO; "txpool.import"; + "ready" => status.ready, + "future" => status.future + ); + Ok(()) + }) + .select(exit.clone()) + .then(|_| Ok(())); + + let _ = to_spawn_tx.unbounded_send(Box::new(events)); + } + + // Periodically notify the telemetry. + let transaction_pool_ = transaction_pool.clone(); + let client_ = client.clone(); + let mut sys = System::new(); + let self_pid = get_current_pid().ok(); + let (state_tx, state_rx) = mpsc::unbounded::<(NetworkStatus<_>, NetworkState)>(); + network_status_sinks.lock().push(std::time::Duration::from_millis(5000), state_tx); + let tel_task = state_rx.for_each(move |(net_status, _)| { + let info = client_.info(); + let best_number = info.chain.best_number.saturated_into::(); + let best_hash = info.chain.best_hash; + let num_peers = net_status.num_connected_peers; + let txpool_status = transaction_pool_.status(); + let finalized_number: u64 = info.chain.finalized_number.saturated_into::(); + let bandwidth_download = net_status.average_download_per_sec; + let bandwidth_upload = net_status.average_upload_per_sec; + + let used_state_cache_size = match info.used_state_cache_size { + Some(size) => size, + None => 0, + }; + + // get cpu usage and memory usage of this process + let (cpu_usage, memory) = if let Some(self_pid) = self_pid { + if sys.refresh_process(self_pid) { + let proc = sys.get_process(self_pid) + .expect("Above refresh_process succeeds, this should be Some(), qed"); + (proc.cpu_usage(), proc.memory()) + } else { (0.0, 0) } + } else { (0.0, 0) }; + + telemetry!( + SUBSTRATE_INFO; + "system.interval"; + "peers" => num_peers, + "height" => best_number, + "best" => ?best_hash, + "txcount" => txpool_status.ready, + "cpu" => cpu_usage, + "memory" => memory, + "finalized_height" => finalized_number, + "finalized_hash" => ?info.chain.finalized_hash, + "bandwidth_download" => bandwidth_download, + "bandwidth_upload" => bandwidth_upload, + "used_state_cache_size" => used_state_cache_size, + ); + + Ok(()) + }).select(exit.clone()).then(|_| Ok(())); + let _ = to_spawn_tx.unbounded_send(Box::new(tel_task)); + + // Periodically send the network state to the telemetry. + let (netstat_tx, netstat_rx) = mpsc::unbounded::<(NetworkStatus<_>, NetworkState)>(); + network_status_sinks.lock().push(std::time::Duration::from_secs(30), netstat_tx); + let tel_task_2 = netstat_rx.for_each(move |(_, network_state)| { + telemetry!( + SUBSTRATE_INFO; + "system.network_state"; + "state" => network_state, + ); + Ok(()) + }).select(exit.clone()).then(|_| Ok(())); + let _ = to_spawn_tx.unbounded_send(Box::new(tel_task_2)); + + // RPC + let (system_rpc_tx, system_rpc_rx) = futures03::channel::mpsc::unbounded(); + let gen_handler = || { + use rpc::{chain, state, author, system}; + + let system_info = rpc::system::SystemInfo { + chain_name: config.chain_spec.name().into(), + impl_name: config.impl_name.into(), + impl_version: config.impl_version.into(), + properties: config.chain_spec.properties().clone(), + }; + + let subscriptions = rpc::Subscriptions::new(Arc::new(SpawnTaskHandle { + sender: to_spawn_tx.clone(), + on_exit: exit.clone() + })); + + let (chain, state) = if let (Some(remote_backend), Some(on_demand)) = + (remote_backend.as_ref(), on_demand.as_ref()) { + // Light clients + let chain = rpc::chain::new_light( + client.clone(), + subscriptions.clone(), + remote_backend.clone(), + on_demand.clone() + ); + let state = rpc::state::new_light( + client.clone(), + subscriptions.clone(), + remote_backend.clone(), + on_demand.clone() + ); + (chain, state) + + } else { + // Full nodes + let chain = rpc::chain::new_full(client.clone(), subscriptions.clone()); + let state = rpc::state::new_full(client.clone(), subscriptions.clone()); + (chain, state) + }; + + let author = rpc::author::Author::new( + client.clone(), + transaction_pool.clone(), + subscriptions, + keystore.clone(), + ); + let system = system::System::new(system_info, system_rpc_tx.clone()); + + rpc_servers::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::new(build_network_future( + config.roles, + network_mut, + client.clone(), + network_status_sinks.clone(), + system_rpc_rx, + has_bootnodes, + dht_event_tx, ) + .map_err(|_| ()) + .select(exit.clone()) + .then(|_| Ok(())))); + + let telemetry_connection_sinks: Arc>>> = Default::default(); + + // Telemetry + let telemetry = config.telemetry_endpoints.clone().map(|endpoints| { + let is_authority = config.roles.is_authority(); + let network_id = network.local_peer_id().to_base58(); + 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 telemetry_connection_sinks_ = telemetry_connection_sinks.clone(); + let telemetry = tel::init_telemetry(tel::TelemetryConfig { + endpoints, + wasm_external_transport: config.telemetry_external_transport.take(), + }); + let future = telemetry.clone() + .map(|ev| Ok::<_, ()>(ev)) + .compat() + .for_each(move |event| { + // Safe-guard in case we add more events in the future. + let tel::TelemetryEvent::Connected = event; + + telemetry!(SUBSTRATE_INFO; "system.connected"; + "name" => name.clone(), + "implementation" => impl_name.clone(), + "version" => version.clone(), + "config" => "", + "chain" => chain_name.clone(), + "authority" => is_authority, + "network_id" => network_id.clone() + ); + + telemetry_connection_sinks_.lock().retain(|sink| { + sink.unbounded_send(()).is_ok() + }); + Ok(()) + }); + let _ = to_spawn_tx.unbounded_send(Box::new(future + .select(exit.clone()) + .then(|_| Ok(())))); + telemetry + }); + + Ok(Service { + client, + network, + network_status_sinks, + select_chain, + transaction_pool, + exit, + signal: Some(signal), + essential_failed: Arc::new(AtomicBool::new(false)), + to_spawn_tx, + to_spawn_rx, + to_poll: Vec::new(), + rpc_handlers, + _rpc: rpc, + _telemetry: telemetry, + _offchain_workers: offchain_workers, + _telemetry_on_connect_sinks: telemetry_connection_sinks.clone(), + keystore, + marker: PhantomData::, + }) } } -pub(crate) fn start_rpc( - rpc_builder: &RpcB, - client: Arc>, - system_send_back: futures03::channel::mpsc::UnboundedSender>, - rpc_system_info: SystemInfo, - task_executor: TaskExecutor, - transaction_pool: Arc>, - rpc_extensions: impl rpc::RpcExtension, - keystore: KeyStorePtr, -) -> rpc_servers::RpcHandler -where - Block: BlockT::Out>, - Backend: client::backend::Backend + 'static, - Client: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: - runtime_api::Metadata + session::SessionKeys, - Api: Send + Sync + 'static, - Executor: client::CallExecutor + Send + Sync + Clone + 'static, - PoolApi: txpool::ChainApi + 'static, - RpcB: RpcBuilder, -{ - use rpc::{chain, state, author, system}; - let subscriptions = rpc::Subscriptions::new(task_executor); - let chain = rpc_builder.build_chain(subscriptions.clone()); - let state = rpc_builder.build_state(subscriptions.clone()); - let author = rpc::author::Author::new( - client, - transaction_pool, - subscriptions, - keystore, - ); - let system = system::System::new(rpc_system_info, system_send_back); - - rpc_servers::rpc_handler(( - state::StateApi::to_delegate(state), - chain::ChainApi::to_delegate(chain), - author::AuthorApi::to_delegate(author), - system::SystemApi::to_delegate(system), - rpc_extensions, - )) -} - pub(crate) fn maintain_transaction_pool( id: &BlockId, client: &Arc>, @@ -990,32 +1198,6 @@ pub(crate) fn maintain_transaction_pool( }) } -pub(crate) fn offchain_workers( - number: &NumberFor, - offchain: &offchain::OffchainWorkers< - Client, - >::OffchainStorage, - Block - >, - pool: &Arc>, - network_state: &Arc, - is_validator: bool, -) -> error::Result + Send>> -where - Block: BlockT::Out>, - Backend: client::backend::Backend + 'static, - Api: 'static, - >::OffchainStorage: 'static, - Client: ProvideRuntimeApi + Send + Sync, - as ProvideRuntimeApi>::Api: offchain::OffchainWorkerApi, - Executor: client::CallExecutor + 'static, - PoolApi: txpool::ChainApi + 'static, -{ - let future = offchain.on_block_imported(number, pool, network_state.clone(), is_validator) - .map(|()| Ok(())); - Ok(Box::new(Compat::new(future))) -} - #[cfg(test)] mod tests { use super::*; diff --git a/core/service/src/chain_ops.rs b/core/service/src/chain_ops.rs index 0858534482f4b25f4915e07f05f00f2478daf25f..e6d7df33c2a1d3b4994761722ee558397f5783d3 100644 --- a/core/service/src/chain_ops.rs +++ b/core/service/src/chain_ops.rs @@ -46,7 +46,7 @@ macro_rules! export_blocks { let last_: u64 = last.saturated_into::(); let block_: u64 = block.saturated_into::(); let len: u64 = last_ - block_ + 1; - $output.write(&len.encode())?; + $output.write_all(&len.encode())?; } loop { @@ -59,7 +59,7 @@ macro_rules! export_blocks { serde_json::to_writer(&mut $output, &block) .map_err(|e| format!("Error writing JSON: {}", e))?; } else { - $output.write(&block.encode())?; + $output.write_all(&block.encode())?; } }, None => break, diff --git a/core/service/src/config.rs b/core/service/src/config.rs index c4690e53f7995ea01b256f9d97e12e3dd5f4a952..e9f002c21f4423e8982c4a9286ac35432d380834 100644 --- a/core/service/src/config.rs +++ b/core/service/src/config.rs @@ -17,10 +17,11 @@ //! Service configuration. pub use client::ExecutionStrategies; -pub use client_db::PruningMode; +pub use client_db::{kvdb::KeyValueDB, PruningMode}; pub use network::config::{ExtTransport, NetworkConfiguration, Roles}; +pub use substrate_executor::WasmExecutionMethod; -use std::{path::PathBuf, net::SocketAddr}; +use std::{path::PathBuf, net::SocketAddr, sync::Arc}; use transaction_pool; use chain_spec::{ChainSpec, RuntimeGenesis, Extension, NoExtension}; use primitives::crypto::Protected; @@ -42,12 +43,12 @@ pub struct Configuration { pub transaction_pool: transaction_pool::txpool::Options, /// Network configuration. pub network: NetworkConfiguration, + /// Path to the base configuration directory. + pub config_dir: Option, /// Path to key files. - pub keystore_path: PathBuf, - /// Path to the database. - pub database_path: PathBuf, - /// Cache Size for internal database in MiB - pub database_cache_size: Option, + pub keystore_path: Option, + /// Configuration for the database. + pub database: DatabaseConfig, /// Size of internal state cache in Bytes pub state_cache_size: usize, /// Size in percent of cache size dedicated to child tries @@ -60,6 +61,8 @@ pub struct Configuration { pub custom: C, /// Node name. pub name: String, + /// Wasm execution method. + pub wasm_method: WasmExecutionMethod, /// Execution strategies. pub execution_strategies: ExecutionStrategies, /// RPC over HTTP binding address. `None` if disabled. @@ -79,6 +82,10 @@ pub struct Configuration { pub default_heap_pages: Option, /// Should offchain workers be executed. pub offchain_worker: bool, + /// Sentry mode is enabled, the node's role is AUTHORITY but it should not + /// actively participate in consensus (i.e. no keystores should be passed to + /// consensus modules). + pub sentry_mode: bool, /// Enable authoring even when offline. pub force_authoring: bool, /// Disable GRANDPA when running in validator mode @@ -93,29 +100,48 @@ pub struct Configuration { pub dev_key_seed: Option, } +/// Configuration of the database of the client. +#[derive(Clone)] +pub enum DatabaseConfig { + /// Database file at a specific path. Recommended for most uses. + Path { + /// Path to the database. + path: PathBuf, + /// Cache Size for internal database in MiB + cache_size: Option, + }, + + /// A custom implementation of an already-open database. + Custom(Arc), +} + impl Configuration where C: Default, G: RuntimeGenesis, E: Extension, { - /// Create default config for given chain spec. - pub fn default_with_spec(chain_spec: ChainSpec) -> Self { + /// 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_name: "parity-substrate", impl_version: "0.0.0", impl_commit: "", chain_spec, + config_dir: config_dir.clone(), name: Default::default(), roles: Roles::FULL, transaction_pool: Default::default(), network: Default::default(), - keystore_path: Default::default(), - database_path: Default::default(), - database_cache_size: Default::default(), + keystore_path: config_dir.map(|c| c.join("keystore")), + database: DatabaseConfig::Path { + path: Default::default(), + cache_size: Default::default(), + }, 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(), rpc_http: None, rpc_ws: None, @@ -125,6 +151,7 @@ impl Configuration where telemetry_external_transport: None, default_heap_pages: None, offchain_worker: Default::default(), + sentry_mode: false, force_authoring: false, disable_grandpa: false, keystore_password: None, @@ -137,6 +164,9 @@ impl Configuration where configuration } +} + +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) @@ -146,6 +176,17 @@ impl Configuration where pub fn client_id(&self) -> String { format!("{}/v{}", self.impl_name, self.full_version()) } + + /// Generate a PathBuf to sub in the chain configuration directory + /// if given + 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(sub); + path + }) + } } /// Returns platform info diff --git a/core/service/src/lib.rs b/core/service/src/lib.rs index fccf04d8a13452e2b22e71ededd5cbcdff628c87..d09fcb093b2024aad38539eb4a3015c417ecdfe5 100644 --- a/core/service/src/lib.rs +++ b/core/service/src/lib.rs @@ -24,6 +24,9 @@ pub mod config; pub mod chain_ops; pub mod error; +mod builder; +mod status_sinks; + use std::io; use std::marker::PhantomData; use std::net::SocketAddr; @@ -69,14 +72,13 @@ pub use futures::future::Executor; const DEFAULT_PROTOCOL_ID: &str = "sup"; /// Substrate service. -pub struct NewService { +pub struct Service { client: Arc, select_chain: Option, network: Arc, /// Sinks to propagate network status updates. - network_status_sinks: Arc>>>, + /// For each element, every time the `Interval` fires we push an element on the sender. + network_status_sinks: Arc>>, transaction_pool: Arc, /// A future that resolves when the service has exited, this is useful to /// make sure any internally spawned futures stop when the service does. @@ -128,325 +130,6 @@ impl Executor + Send>> for SpawnTaskHandle } } -macro_rules! new_impl { - ( - $block:ty, - $config:ident, - $build_components:expr, - $maintain_transaction_pool:expr, - $offchain_workers:expr, - $start_rpc:expr, - ) => {{ - let (signal, exit) = exit_future::signal(); - - // 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>>(); - - // Create all the components. - let ( - client, - on_demand, - backend, - keystore, - select_chain, - import_queue, - finality_proof_request_builder, - finality_proof_provider, - network_protocol, - transaction_pool, - rpc_extensions, - dht_event_tx, - ) = $build_components(&$config)?; - let import_queue = Box::new(import_queue); - let chain_info = client.info().chain; - - let version = $config.full_version(); - info!("Highest known block at #{}", chain_info.best_number); - telemetry!( - SUBSTRATE_INFO; - "node.start"; - "height" => chain_info.best_number.saturated_into::(), - "best" => ?chain_info.best_hash - ); - - let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { - 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() }), - }); - - let protocol_id = { - let protocol_id_full = match $config.chain_spec.protocol_id() { - Some(pid) => pid, - None => { - warn!("Using default protocol ID {:?} because none is configured in the \ - chain specs", DEFAULT_PROTOCOL_ID - ); - DEFAULT_PROTOCOL_ID - } - }.as_bytes(); - network::config::ProtocolId::from(protocol_id_full) - }; - - let block_announce_validator = - Box::new(consensus_common::block_validation::DefaultBlockAnnounceValidator::new(client.clone())); - - let network_params = network::config::Params { - roles: $config.roles, - network_config: $config.network.clone(), - chain: client.clone(), - finality_proof_provider, - finality_proof_request_builder, - on_demand, - transaction_pool: transaction_pool_adapter.clone() as _, - import_queue, - protocol_id, - specialization: network_protocol, - block_announce_validator, - }; - - let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); - let network_mut = network::NetworkWorker::new(network_params)?; - let network = network_mut.service().clone(); - let network_status_sinks = Arc::new(Mutex::new(Vec::new())); - - let offchain_storage = backend.offchain_storage(); - let offchain_workers = match ($config.offchain_worker, offchain_storage) { - (true, Some(db)) => { - Some(Arc::new(offchain::OffchainWorkers::new(client.clone(), db))) - }, - (true, None) => { - log::warn!("Offchain workers disabled, due to lack of offchain storage support in backend."); - None - }, - _ => None, - }; - - { - // block notifications - let txpool = Arc::downgrade(&transaction_pool); - let wclient = Arc::downgrade(&client); - let offchain = offchain_workers.as_ref().map(Arc::downgrade); - let to_spawn_tx_ = to_spawn_tx.clone(); - let network_state_info: Arc = network.clone(); - let is_validator = $config.roles.is_authority(); - - let events = client.import_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .for_each(move |notification| { - let number = *notification.header.number(); - let txpool = txpool.upgrade(); - - if let (Some(txpool), Some(client)) = (txpool.as_ref(), wclient.upgrade()) { - let future = $maintain_transaction_pool( - &BlockId::hash(notification.hash), - &client, - &*txpool, - ¬ification.retracted, - ).map_err(|e| warn!("Pool error processing new block: {:?}", e))?; - let _ = to_spawn_tx_.unbounded_send(future); - } - - let offchain = offchain.as_ref().and_then(|o| o.upgrade()); - if let (Some(txpool), Some(offchain)) = (txpool, offchain) { - let future = $offchain_workers( - &number, - &offchain, - &txpool, - &network_state_info, - is_validator, - ).map_err(|e| warn!("Offchain workers error processing new block: {:?}", e))?; - let _ = to_spawn_tx_.unbounded_send(future); - } - - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - let _ = to_spawn_tx.unbounded_send(Box::new(events)); - } - - { - // extrinsic notifications - let network = Arc::downgrade(&network); - let transaction_pool_ = transaction_pool.clone(); - let events = transaction_pool.import_notification_stream() - .map(|v| Ok::<_, ()>(v)).compat() - .for_each(move |_| { - if let Some(network) = network.upgrade() { - network.trigger_repropagate(); - } - let status = transaction_pool_.status(); - telemetry!(SUBSTRATE_INFO; "txpool.import"; - "ready" => status.ready, - "future" => status.future - ); - Ok(()) - }) - .select(exit.clone()) - .then(|_| Ok(())); - - let _ = to_spawn_tx.unbounded_send(Box::new(events)); - } - - // Periodically notify the telemetry. - let transaction_pool_ = transaction_pool.clone(); - let client_ = client.clone(); - let mut sys = System::new(); - let self_pid = get_current_pid().ok(); - let (netstat_tx, netstat_rx) = mpsc::unbounded::<(NetworkStatus<_>, NetworkState)>(); - network_status_sinks.lock().push(netstat_tx); - let tel_task = netstat_rx.for_each(move |(net_status, network_state)| { - let info = client_.info(); - let best_number = info.chain.best_number.saturated_into::(); - let best_hash = info.chain.best_hash; - let num_peers = net_status.num_connected_peers; - let txpool_status = transaction_pool_.status(); - let finalized_number: u64 = info.chain.finalized_number.saturated_into::(); - let bandwidth_download = net_status.average_download_per_sec; - let bandwidth_upload = net_status.average_upload_per_sec; - - let used_state_cache_size = match info.used_state_cache_size { - Some(size) => size, - None => 0, - }; - - // get cpu usage and memory usage of this process - let (cpu_usage, memory) = if let Some(self_pid) = self_pid { - if sys.refresh_process(self_pid) { - let proc = sys.get_process(self_pid) - .expect("Above refresh_process succeeds, this should be Some(), qed"); - (proc.cpu_usage(), proc.memory()) - } else { (0.0, 0) } - } else { (0.0, 0) }; - - telemetry!( - SUBSTRATE_INFO; - "system.interval"; - "network_state" => network_state, - "peers" => num_peers, - "height" => best_number, - "best" => ?best_hash, - "txcount" => txpool_status.ready, - "cpu" => cpu_usage, - "memory" => memory, - "finalized_height" => finalized_number, - "finalized_hash" => ?info.chain.finalized_hash, - "bandwidth_download" => bandwidth_download, - "bandwidth_upload" => bandwidth_upload, - "used_state_cache_size" => used_state_cache_size, - ); - - Ok(()) - }).select(exit.clone()).then(|_| Ok(())); - let _ = to_spawn_tx.unbounded_send(Box::new(tel_task)); - - // RPC - let (system_rpc_tx, system_rpc_rx) = futures03::channel::mpsc::unbounded(); - let gen_handler = || { - let system_info = rpc::system::SystemInfo { - chain_name: $config.chain_spec.name().into(), - impl_name: $config.impl_name.into(), - impl_version: $config.impl_version.into(), - properties: $config.chain_spec.properties().clone(), - }; - $start_rpc( - client.clone(), - //light_components.clone(), - system_rpc_tx.clone(), - system_info.clone(), - Arc::new(SpawnTaskHandle { sender: to_spawn_tx.clone(), on_exit: exit.clone() }), - transaction_pool.clone(), - rpc_extensions.clone(), - keystore.clone(), - ) - }; - let rpc_handlers = gen_handler(); - let rpc = start_rpc_servers(&$config, gen_handler)?; - - - let _ = to_spawn_tx.unbounded_send(Box::new(build_network_future( - network_mut, - client.clone(), - network_status_sinks.clone(), - system_rpc_rx, - has_bootnodes, - dht_event_tx, - ) - .map_err(|_| ()) - .select(exit.clone()) - .then(|_| Ok(())))); - - let telemetry_connection_sinks: Arc>>> = Default::default(); - - // Telemetry - let telemetry = $config.telemetry_endpoints.clone().map(|endpoints| { - let is_authority = $config.roles.is_authority(); - let network_id = network.local_peer_id().to_base58(); - 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 telemetry_connection_sinks_ = telemetry_connection_sinks.clone(); - let telemetry = tel::init_telemetry(tel::TelemetryConfig { - endpoints, - wasm_external_transport: $config.telemetry_external_transport.take(), - }); - let future = telemetry.clone() - .map(|ev| Ok::<_, ()>(ev)) - .compat() - .for_each(move |event| { - // Safe-guard in case we add more events in the future. - let tel::TelemetryEvent::Connected = event; - - telemetry!(SUBSTRATE_INFO; "system.connected"; - "name" => name.clone(), - "implementation" => impl_name.clone(), - "version" => version.clone(), - "config" => "", - "chain" => chain_name.clone(), - "authority" => is_authority, - "network_id" => network_id.clone() - ); - - telemetry_connection_sinks_.lock().retain(|sink| { - sink.unbounded_send(()).is_ok() - }); - Ok(()) - }); - let _ = to_spawn_tx.unbounded_send(Box::new(future - .select(exit.clone()) - .then(|_| Ok(())))); - telemetry - }); - - Ok(NewService { - client, - network, - network_status_sinks, - select_chain, - transaction_pool, - exit, - signal: Some(signal), - essential_failed: Arc::new(AtomicBool::new(false)), - to_spawn_tx, - to_spawn_rx, - to_poll: Vec::new(), - rpc_handlers, - _rpc: rpc, - _telemetry: telemetry, - _offchain_workers: offchain_workers, - _telemetry_on_connect_sinks: telemetry_connection_sinks.clone(), - keystore, - marker: PhantomData::<$block>, - }) - }} -} - -mod builder; - /// Abstraction over a Substrate service. pub trait AbstractService: 'static + Future + Executor + Send>> + Send { @@ -506,7 +189,7 @@ pub trait AbstractService: 'static + Future + fn network(&self) -> Arc>; /// Returns a receiver that periodically receives a status of the network. - fn network_status(&self) -> mpsc::UnboundedReceiver<(NetworkStatus, NetworkState)>; + fn network_status(&self, interval: Duration) -> mpsc::UnboundedReceiver<(NetworkStatus, NetworkState)>; /// Get shared transaction pool instance. fn transaction_pool(&self) -> Arc>; @@ -516,7 +199,7 @@ pub trait AbstractService: 'static + Future + } impl AbstractService for - NewService, TSc, NetworkStatus, + Service, TSc, NetworkStatus, NetworkService, TransactionPool, TOc> where TBl: BlockT, @@ -589,9 +272,9 @@ where self.network.clone() } - fn network_status(&self) -> mpsc::UnboundedReceiver<(NetworkStatus, NetworkState)> { + fn network_status(&self, interval: Duration) -> mpsc::UnboundedReceiver<(NetworkStatus, NetworkState)> { let (sink, stream) = mpsc::unbounded(); - self.network_status_sinks.lock().push(sink); + self.network_status_sinks.lock().push(interval, sink); stream } @@ -605,7 +288,7 @@ where } impl Future for - NewService + Service { type Item = (); type Error = Error; @@ -638,7 +321,7 @@ impl Future for } impl Executor + Send>> for - NewService + Service { fn execute( &self, @@ -662,21 +345,18 @@ fn build_network_future< S: network::specialization::NetworkSpecialization, H: network::ExHashT > ( + roles: Roles, mut network: network::NetworkWorker, client: Arc, - status_sinks: Arc, NetworkState)>>>>, + status_sinks: Arc, NetworkState)>>>, rpc_rx: futures03::channel::mpsc::UnboundedReceiver>, should_have_peers: bool, dht_event_tx: Option>, ) -> impl Future { - // Compatibility shim while we're transitionning to stable Futures. + // Compatibility shim while we're transitioning to stable Futures. // See https://github.com/paritytech/substrate/issues/3099 let mut rpc_rx = futures03::compat::Compat::new(rpc_rx.map(|v| Ok::<_, ()>(v))); - // Interval at which we send status updates on the status stream. - const STATUS_INTERVAL: Duration = Duration::from_millis(5000); - let mut status_interval = tokio_timer::Interval::new_interval(STATUS_INTERVAL); - let mut imported_blocks_stream = client.import_notification_stream().fuse() .map(|v| Ok::<_, ()>(v)).compat(); let mut finality_notification_stream = client.finality_notification_stream().fuse() @@ -725,11 +405,26 @@ fn build_network_future< let _ = sender.send(network_state); } } + rpc::system::Request::NodeRoles(sender) => { + use rpc::system::NodeRole; + + let node_roles = (0 .. 8) + .filter(|&bit_number| (roles.bits() >> bit_number) & 1 == 1) + .map(|bit_number| match Roles::from_bits(1 << bit_number) { + Some(Roles::AUTHORITY) => NodeRole::Authority, + Some(Roles::LIGHT) => NodeRole::LightClient, + Some(Roles::FULL) => NodeRole::Full, + _ => NodeRole::UnknownRole(bit_number), + }) + .collect(); + + let _ = sender.send(node_roles); + } }; } // Interval report for the external API. - while let Ok(Async::Ready(_)) = status_interval.poll() { + status_sinks.lock().poll(|| { let status = NetworkStatus { sync_state: network.sync_state(), best_seen_block: network.best_seen_block(), @@ -740,9 +435,8 @@ fn build_network_future< average_upload_per_sec: network.average_upload_per_sec(), }; let state = network.network_state(); - - status_sinks.lock().retain(|sink| sink.unbounded_send((status.clone(), state.clone())).is_ok()); - } + (status, state) + }); // Main network polling. while let Ok(Async::Ready(Some(Event::Dht(event)))) = network.poll().map_err(|err| { @@ -765,7 +459,7 @@ fn build_network_future< let polling_dur = before_polling.elapsed(); log!( target: "service", - if polling_dur >= Duration::from_millis(50) { Level::Debug } else { Level::Trace }, + if polling_dur >= Duration::from_secs(1) { Level::Warn } else { Level::Trace }, "Polling the network future took {:?}", polling_dur ); @@ -794,7 +488,7 @@ pub struct NetworkStatus { } impl Drop for - NewService + Service { fn drop(&mut self) { debug!(target: "service", "Substrate service shutdown"); @@ -847,15 +541,16 @@ fn start_rpc_servers rpc_servers::RpcHandler components::RpcHandler>( +fn start_rpc_servers rpc_servers::RpcHandler>( _: &Configuration, _: H -) -> Result, error::Error> { +) -> Result, error::Error> { Ok(Box::new(())) } /// An RPC session. Used to perform in-memory RPC queries (ie. RPC queries that don't go through /// the HTTP or WebSockets server). +#[derive(Clone)] pub struct RpcSession { metadata: rpc::Metadata, } diff --git a/core/service/src/status_sinks.rs b/core/service/src/status_sinks.rs new file mode 100644 index 0000000000000000000000000000000000000000..079ecf6353b1fe125028e91dc13aabb0e7729f6f --- /dev/null +++ b/core/service/src/status_sinks.rs @@ -0,0 +1,149 @@ +// 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 . + +use futures::prelude::*; +use futures::sync::mpsc; +use futures::stream::futures_unordered::FuturesUnordered; +use std::time::{Duration, Instant}; +use tokio_timer::Delay; + +/// Holds a list of `UnboundedSender`s, each associated with a certain time period. Every time the +/// period elapses, we push an element on the sender. +/// +/// Senders are removed only when they are closed. +pub struct StatusSinks { + entries: FuturesUnordered>, +} + +struct YieldAfter { + delay: tokio_timer::Delay, + interval: Duration, + sender: Option>, +} + +impl StatusSinks { + /// Builds a new empty collection. + pub fn new() -> StatusSinks { + StatusSinks { + entries: FuturesUnordered::new(), + } + } + + /// Adds a sender to the collection. + /// + /// The `interval` is the time period between two pushes on the sender. + pub fn push(&mut self, interval: Duration, sender: mpsc::UnboundedSender) { + self.entries.push(YieldAfter { + delay: Delay::new(Instant::now() + interval), + interval, + sender: Some(sender), + }) + } + + /// Processes all the senders. If any sender is ready, calls the `status_grab` function and + /// pushes what it returns to the sender. + /// + /// This function doesn't return anything, but it should be treated as if it implicitly + /// returns `Ok(Async::NotReady)`. In particular, it should be called again when the task + /// is waken up. + /// + /// # Panic + /// + /// Panics if not called within the context of a task. + pub fn poll(&mut self, mut status_grab: impl FnMut() -> T) { + loop { + match self.entries.poll() { + Ok(Async::Ready(Some((sender, interval)))) => { + let status = status_grab(); + if sender.unbounded_send(status).is_ok() { + self.entries.push(YieldAfter { + // Note that since there's a small delay between the moment a task is + // waken up and the moment it is polled, the period is actually not + // `interval` but `interval + `. We ignore this problem in + // practice. + delay: Delay::new(Instant::now() + interval), + interval, + sender: Some(sender), + }); + } + } + Err(()) | + Ok(Async::Ready(None)) | + Ok(Async::NotReady) => break, + } + } + } +} + +impl Future for YieldAfter { + type Item = (mpsc::UnboundedSender, Duration); + type Error = (); + + fn poll(&mut self) -> Poll { + match self.delay.poll() { + Ok(Async::NotReady) => Ok(Async::NotReady), + Ok(Async::Ready(())) => { + let sender = self.sender.take() + .expect("sender is always Some unless the future is finished; qed"); + Ok(Async::Ready((sender, self.interval))) + }, + Err(_) => Err(()), + } + } +} + +#[cfg(test)] +mod tests { + use super::StatusSinks; + use futures::prelude::*; + use futures::sync::mpsc; + use std::time::Duration; + + #[test] + fn basic_usage() { + let mut status_sinks = StatusSinks::new(); + + let (tx1, rx1) = mpsc::unbounded(); + status_sinks.push(Duration::from_millis(200), tx1); + + let (tx2, rx2) = mpsc::unbounded(); + status_sinks.push(Duration::from_millis(500), tx2); + + let mut runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut val_order = 5; + runtime.spawn(futures::future::poll_fn(move || { + status_sinks.poll(|| { val_order += 1; val_order }); + Ok(Async::NotReady) + })); + + let done = rx1 + .into_future() + .and_then(|(item, rest)| { + assert_eq!(item, Some(6)); + rest.into_future() + }) + .and_then(|(item, _)| { + assert_eq!(item, Some(7)); + rx2.into_future() + }) + .map(|(item, _)| { + assert_eq!(item, Some(8)); + }); + + runtime.block_on(done).unwrap(); + } +} diff --git a/core/service/test/Cargo.toml b/core/service/test/Cargo.toml index b4651da7a75e8f6c1e93c54e19465ca445e962aa..4415369a6a1d3f69d5c42a8332408d3fa390a05f 100644 --- a/core/service/test/Cargo.toml +++ b/core/service/test/Cargo.toml @@ -9,10 +9,10 @@ tempdir = "0.3.7" tokio = "0.1.22" futures = "0.1.29" log = "0.4.8" -env_logger = "0.6.2" +env_logger = "0.7.0" fdlimit = "0.1.1" futures03 = { package = "futures-preview", version = "=0.3.0-alpha.19", features = ["compat"] } -service = { package = "substrate-service", path = "../../../core/service" } +service = { package = "substrate-service", path = "../../../core/service", default-features = false } network = { package = "substrate-network", path = "../../../core/network" } consensus = { package = "substrate-consensus-common", path = "../../../core/consensus/common" } client = { package = "substrate-client", path = "../../../core/client" } diff --git a/core/service/test/src/lib.rs b/core/service/test/src/lib.rs index 2d064f965bb27a627e9776b97707b2b217112f88..c147f8051065ea2c4435f8132ca9eb3896f5e256 100644 --- a/core/service/test/src/lib.rs +++ b/core/service/test/src/lib.rs @@ -29,6 +29,7 @@ use service::{ AbstractService, ChainSpec, Configuration, + config::DatabaseConfig, Roles, Error, }; @@ -168,16 +169,20 @@ fn node_config ( roles: role, transaction_pool: Default::default(), network: network_config, - keystore_path: root.join("key"), + keystore_path: Some(root.join("key")), keystore_password: None, - database_path: root.join("db"), - database_cache_size: None, + config_dir: Some(root.clone()), + database: 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(), name: format!("Node {}", index), + wasm_method: service::config::WasmExecutionMethod::Interpreted, execution_strategies: Default::default(), rpc_http: None, rpc_ws: None, @@ -187,6 +192,7 @@ fn node_config ( telemetry_external_transport: None, default_heap_pages: None, offchain_worker: false, + sentry_mode: false, force_authoring: false, disable_grandpa: false, dev_key_seed: key_seed, diff --git a/core/sr-api-macros/Cargo.toml b/core/sr-api-macros/Cargo.toml index 6f02e3040986f2842401db5cb7aa6d1d0093928d..022536136b80e31529bfdff4de71ac7ae40ae64a 100644 --- a/core/sr-api-macros/Cargo.toml +++ b/core/sr-api-macros/Cargo.toml @@ -21,12 +21,17 @@ state_machine = { package = "substrate-state-machine", path = "../state-machine" sr-primitives = { path = "../sr-primitives" } sr-version = { path = "../sr-version" } primitives = { package = "substrate-primitives", path = "../primitives" } -criterion = "0.2.11" +criterion = "0.3.0" consensus_common = { package = "substrate-consensus-common", path = "../consensus/common" } codec = { package = "parity-scale-codec", version = "1.0.0" } -trybuild = "1.0.14" -rustversion = "0.1.4" +trybuild = "1.0.17" +rustversion = "1.0.0" [[bench]] name = "bench" harness = false + +# We actually don't need the `std` feature in this crate, but the tests require it. +[features] +default = [ "std" ] +std = [] diff --git a/core/sr-api-macros/src/impl_runtime_apis.rs b/core/sr-api-macros/src/impl_runtime_apis.rs index fb154aa1123580a1e35597b1b40f1961b6ebb522..42d653b1abd9782fb96928a8ec12990a7dc4f112 100644 --- a/core/sr-api-macros/src/impl_runtime_apis.rs +++ b/core/sr-api-macros/src/impl_runtime_apis.rs @@ -83,8 +83,7 @@ fn generate_impl_call( )* #[allow(deprecated)] - let output = <#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*); - #c::runtime_api::Encode::encode(&output) + <#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*) ) ) } @@ -175,11 +174,12 @@ fn generate_impl_calls( /// Generate the dispatch function that is used in native to call into the runtime. fn generate_dispatch_function(impls: &[ItemImpl]) -> Result { let data = Ident::new("data", Span::call_site()); + let c = generate_crate_access(HIDDEN_INCLUDES_ID); let impl_calls = generate_impl_calls(impls, &data)? .into_iter() .map(|(trait_, fn_name, impl_)| { let name = prefix_function_with_trait(&trait_, &fn_name); - quote!( #name => Some({ #impl_ }), ) + quote!( #name => Some(#c::runtime_api::Encode::encode(&{ #impl_ })), ) }); Ok(quote!( @@ -218,13 +218,7 @@ fn generate_wasm_interface(impls: &[ItemImpl]) -> Result { }; let output = { #impl_ }; - let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); - - // Leak the output vector to avoid it being freed. - // This is fine in a WASM context since the heap - // will be discarded after the call. - #c::runtime_api::mem::forget(output); - res + #c::runtime_api::to_substrate_wasm_fn_return_value(&output) } ) }); @@ -306,15 +300,17 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result Option>> { + fn extract_proof(&mut self) -> Option<#crate_::runtime_api::StorageProof> { self.recorder .take() - .map(|r| { - r.borrow_mut() + .map(|recorder| { + let trie_nodes = recorder + .borrow_mut() .drain() .into_iter() - .map(|n| n.data.to_vec()) - .collect() + .map(|record| record.data) + .collect(); + #crate_::runtime_api::StorageProof::new(trie_nodes) }) } } diff --git a/core/sr-api-macros/tests/decl_and_impl.rs b/core/sr-api-macros/tests/decl_and_impl.rs index 36091d1f85062e6c693e0780383d0d9680b1d258..a539d838221be1409b5bd8ae8ebb451382ef82b0 100644 --- a/core/sr-api-macros/tests/decl_and_impl.rs +++ b/core/sr-api-macros/tests/decl_and_impl.rs @@ -93,13 +93,6 @@ fn test_client_side_function_signature() { RuntimeApiImpl::::same_name_before_version_2; } -#[test] -fn test_runtime_side_function_signature() { - let _api_same_name: fn(input_data: *mut u8, input_len: usize) -> u64 = api::Api_same_name; - let _api_with_version_same_name: fn(input_data: *mut u8, input_len: usize) -> u64 = - api::ApiWithCustomVersion_same_name; -} - #[test] fn check_runtime_api_info() { assert_eq!(&Api::::ID, &runtime_decl_for_Api::ID); diff --git a/core/sr-api-macros/tests/runtime_calls.rs b/core/sr-api-macros/tests/runtime_calls.rs index f33a9e257a53a3a0905d824fb7c3e2450fafe22f..ce6300bc4128287e893b47ec4492ce331341619a 100644 --- a/core/sr-api-macros/tests/runtime_calls.rs +++ b/core/sr-api-macros/tests/runtime_calls.rs @@ -186,7 +186,7 @@ fn record_proof_works() { // Use the proof backend to execute `execute_block`. let mut overlay = Default::default(); - let executor = NativeExecutor::::new(None); + let executor = NativeExecutor::::new(WasmExecutionMethod::Interpreted, None); execution_proof_check_on_trie_backend( &backend, &mut overlay, diff --git a/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.rs b/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.rs index 774d017c190e926e1a4e4d55e6ab9aa6ab46214e..b85431f3ba049b0a55e7e9ddd6a6c0d80e89cb27 100644 --- a/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.rs +++ b/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.rs @@ -1,6 +1,6 @@ -use sr_primitives::traits::GetNodeBlockType; +use sr_primitives::traits::{GetNodeBlockType, Block as BlockT}; use test_client::runtime::Block; -use client::{decl_runtime_apis, impl_runtime_apis}; +use client::{decl_runtime_apis, impl_runtime_apis, runtime_api}; /// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType` /// trait are done by the `construct_runtime!` macro in a real runtime. @@ -19,6 +19,18 @@ impl_runtime_apis! { impl self::Api for Runtime { fn test(data: String) {} } + + impl runtime_api::Core for Runtime { + fn version() -> runtime_api::RuntimeVersion { + unimplemented!() + } + fn execute_block(_: Block) { + unimplemented!() + } + fn initialize_block(_: &::Header) { + unimplemented!() + } + } } fn main() {} diff --git a/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.stderr b/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.stderr index 15434a52ba8b78ae6876b17e34f72ff9c7e85779..025ca60c48095365e706871c1ec4a2fe3a50d10e 100644 --- a/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.stderr +++ b/core/sr-api-macros/tests/ui/impl_incorrect_method_signature.stderr @@ -10,6 +10,37 @@ error[E0053]: method `test` has an incompatible type for trait = note: expected type `fn(u64)` found type `fn(std::string::String)` +error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for trait + --> $DIR/impl_incorrect_method_signature.rs:18:1 + | +12 | / decl_runtime_apis! { +13 | | pub trait Api { +14 | | fn test(data: u64); +15 | | } +16 | | } + | |_- type in trait +17 | +18 | impl_runtime_apis! { + | ^^^^^^^^^^^^^^^^^^ expected u64, found struct `std::string::String` + | + = note: expected type `fn(&RuntimeApiImpl, &sr_primitives::generic::block::BlockId, substrate_test_runtime::Extrinsic>>, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::ExecutionContext, std::option::Option, std::vec::Vec) -> std::result::Result, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::error::Error>` + found type `fn(&RuntimeApiImpl, &sr_primitives::generic::block::BlockId, substrate_test_runtime::Extrinsic>>, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::ExecutionContext, std::option::Option, std::vec::Vec) -> std::result::Result, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::error::Error>` + +error[E0308]: mismatched types + --> $DIR/impl_incorrect_method_signature.rs:18:1 + | +18 | / impl_runtime_apis! { +19 | | impl self::Api for Runtime { +20 | | fn test(data: String) {} +21 | | } +... | +33 | | } +34 | | } + | |_^ expected u64, found struct `std::string::String` + | + = note: expected type `u64` + found type `std::string::String` + error[E0308]: mismatched types --> $DIR/impl_incorrect_method_signature.rs:20:11 | @@ -18,6 +49,3 @@ error[E0308]: mismatched types | = note: expected type `u64` found type `std::string::String` - -Some errors have detailed explanations: E0053, E0308. -For more information about an error, try `rustc --explain E0053`. diff --git a/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.rs b/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.rs index acca97a73df5bd2464fb736c733d84116b07c994..1664bec577b57a53715fc65b0f3234e12bd78503 100644 --- a/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.rs +++ b/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.rs @@ -16,6 +16,8 @@ decl_runtime_apis! { } mod second { + use super::*; + decl_runtime_apis! { pub trait Api { fn test2(data: u64); diff --git a/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.stderr b/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.stderr index 355db2864bb784a7011b5c0b8376a713371f88b2..4c37b6b716a0a97cd19bca6a5f469adc56e58ed4 100644 --- a/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.stderr +++ b/core/sr-api-macros/tests/ui/impl_two_traits_with_same_name.stderr @@ -1,25 +1,17 @@ error: Two traits with the same name detected! The trait name is used to generate its ID. Please rename one trait at the declaration! - --> $DIR/impl_two_traits_with_same_name.rs:31:15 + --> $DIR/impl_two_traits_with_same_name.rs:33:15 | -31 | impl second::Api for Runtime { +33 | impl second::Api for Runtime { | ^^^ -error: cannot find macro `decl_runtime_apis!` in this scope - --> $DIR/impl_two_traits_with_same_name.rs:19:2 +error[E0277]: the trait bound `RuntimeApiImpl: sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::Core, substrate_test_runtime::Extrinsic>>` is not satisfied + --> $DIR/impl_two_traits_with_same_name.rs:29:7 | -19 | decl_runtime_apis! { - | ^^^^^^^^^^^^^^^^^ +29 | impl self::Api for Runtime { + | ^^^^^^^^^^^^^^^^ the trait `sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::Core, substrate_test_runtime::Extrinsic>>` is not implemented for `RuntimeApiImpl` -error[E0433]: failed to resolve: could not find `runtime_decl_for_Api` in `second` - --> $DIR/impl_two_traits_with_same_name.rs:26:1 +error[E0277]: the trait bound `RuntimeApiImpl: sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::Core, substrate_test_runtime::Extrinsic>>` is not satisfied + --> $DIR/impl_two_traits_with_same_name.rs:33:7 | -26 | / impl_runtime_apis! { -27 | | impl self::Api for Runtime { -28 | | fn test(data: u64) {} -29 | | } -... | -33 | | } -34 | | } - | |_^ could not find `runtime_decl_for_Api` in `second` - -For more information about this error, try `rustc --explain E0433`. +33 | impl second::Api for Runtime { + | ^^^^^^^^^^^^^^^^^^ the trait `sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::Core, substrate_test_runtime::Extrinsic>>` is not implemented for `RuntimeApiImpl` diff --git a/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.rs b/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.rs index 0e7dc56951647264d532ee41c4a917883e9f5b22..20f114c6bb29e597f7c093c72be7c9ebd9501b7b 100644 --- a/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.rs +++ b/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.rs @@ -1,6 +1,6 @@ -use sr_primitives::traits::GetNodeBlockType; +use sr_primitives::traits::{GetNodeBlockType, Block as BlockT}; use test_client::runtime::Block; -use client::{decl_runtime_apis, impl_runtime_apis}; +use client::{decl_runtime_apis, impl_runtime_apis, runtime_api}; /// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType` /// trait are done by the `construct_runtime!` macro in a real runtime. @@ -21,6 +21,18 @@ impl_runtime_apis! { unimplemented!() } } + + impl runtime_api::Core for Runtime { + fn version() -> runtime_api::RuntimeVersion { + unimplemented!() + } + fn execute_block(_: Block) { + unimplemented!() + } + fn initialize_block(_: &::Header) { + unimplemented!() + } + } } fn main() {} diff --git a/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.stderr b/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.stderr index 9bfc04c8db046ecba4f42d2140143a7ade6b3886..f3abaddd6ea9d757ae1b132a9274c068bc6814c6 100644 --- a/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.stderr +++ b/core/sr-api-macros/tests/ui/type_reference_in_impl_runtime_apis_call.stderr @@ -10,6 +10,22 @@ error[E0053]: method `test` has an incompatible type for trait = note: expected type `fn(u64)` found type `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:18:1 + | +12 | / decl_runtime_apis! { +13 | | pub trait Api { +14 | | fn test(data: u64); +15 | | } +16 | | } + | |_- type in trait +17 | +18 | impl_runtime_apis! { + | ^^^^^^^^^^^^^^^^^^ expected u64, found &u64 + | + = note: expected type `fn(&RuntimeApiImpl, &sr_primitives::generic::block::BlockId, substrate_test_runtime::Extrinsic>>, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::ExecutionContext, std::option::Option, std::vec::Vec) -> std::result::Result, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::error::Error>` + found type `fn(&RuntimeApiImpl, &sr_primitives::generic::block::BlockId, substrate_test_runtime::Extrinsic>>, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::runtime_api::ExecutionContext, std::option::Option<&u64>, std::vec::Vec) -> std::result::Result, sr_api_hidden_includes_DECL_RUNTIME_APIS::sr_api_client::error::Error>` + error[E0308]: mismatched types --> $DIR/type_reference_in_impl_runtime_apis_call.rs:18:1 | @@ -17,13 +33,10 @@ error[E0308]: mismatched types 19 | | impl self::Api for Runtime { 20 | | fn test(data: &u64) { 21 | | unimplemented!() -22 | | } -23 | | } -24 | | } +... | +35 | | } +36 | | } | |_^ expected u64, found &u64 | = note: expected type `u64` found type `&u64` - -Some errors have detailed explanations: E0053, E0308. -For more information about an error, try `rustc --explain E0053`. diff --git a/core/sr-arithmetic/Cargo.toml b/core/sr-arithmetic/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..fd484671dd84896f97d4daf0db343633899b2beb --- /dev/null +++ b/core/sr-arithmetic/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "sr-arithmetic" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +integer-sqrt = "0.1.2" +num-traits = { version = "0.2.8", default-features = false } +rstd = { package = "sr-std", path = "../sr-std", default-features = false } +serde = { version = "1.0.101", optional = true, features = ["derive"] } +substrate-debug-derive = { path = "../primitives/debug-derive", default-features = false } + +[dev-dependencies] +primitive-types = "0.6.0" +rand = "0.7.2" +criterion = "0.3" + +[features] +default = ["std"] +std = [ + "codec/std", + "num-traits/std", + "rstd/std", + "serde", + "substrate-debug-derive/std", +] + +[[bench]] +name = "bench" +harness = false diff --git a/core/sr-arithmetic/benches/bench.rs b/core/sr-arithmetic/benches/bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..22c0ce6f566e965b1fc8885b6427753fec2ad74a --- /dev/null +++ b/core/sr-arithmetic/benches/bench.rs @@ -0,0 +1,80 @@ +// 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 . + +use criterion::{Criterion, Throughput, BenchmarkId, criterion_group, criterion_main}; +use sr_arithmetic::biguint::{BigUint, Single}; +use rand::Rng; + +fn random_big_uint(size: usize) -> BigUint { + let mut rng = rand::thread_rng(); + let digits: Vec<_> = (0..size).map(|_| rng.gen_range(0, Single::max_value())).collect(); + BigUint::from_limbs(&digits) +} + +fn bench_op(c: &mut Criterion, name: &str, op: F) { + let mut group = c.benchmark_group(name); + + for size in [2, 4, 6, 8, 10].iter() { + group.throughput(Throughput::Elements(*size)); + group.bench_with_input(BenchmarkId::from_parameter(size), size, |bencher, &size| { + let a = random_big_uint(size as usize); + let b = random_big_uint(size as usize); + + bencher.iter(|| op(&a, &b)); + }); + } +} + +fn bench_addition(c: &mut Criterion) { + bench_op(c, "addition", |a, b| { + let _ = a.clone().add(&b); + }); +} + +fn bench_subtraction(c: &mut Criterion) { + bench_op(c, "subtraction", |a, b| { + let _ = a.clone().sub(&b); + }); +} + +fn bench_multiplication(c: &mut Criterion) { + bench_op(c, "multiplication", |a, b| { + let _ = a.clone().mul(&b); + }); +} + +fn bench_division(c: &mut Criterion) { + let mut group = c.benchmark_group("division"); + + for size in [4, 6, 8, 10].iter() { + group.throughput(Throughput::Elements(*size)); + group.bench_with_input(BenchmarkId::from_parameter(size), size, |bencher, &size| { + let a = random_big_uint(size as usize); + let b = random_big_uint(rand::thread_rng().gen_range(2, size as usize)); + + bencher.iter(|| { + let _ = a.clone().div(&b, true); + }); + }); + } +} + +criterion_group!{ + name = benches; + config = Criterion::default(); + targets = bench_addition, bench_subtraction, bench_multiplication, bench_division +} +criterion_main!(benches); diff --git a/core/sr-arithmetic/fuzzer/.gitignore b/core/sr-arithmetic/fuzzer/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3ebcb104d4a50a19959dc7ff2bc06ee6bb48b31f --- /dev/null +++ b/core/sr-arithmetic/fuzzer/.gitignore @@ -0,0 +1,2 @@ +hfuzz_target +hfuzz_workspace diff --git a/core/sr-arithmetic/fuzzer/Cargo.lock b/core/sr-arithmetic/fuzzer/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..f6ce6f1d4797dca029231bbe726851badf3e9ddb --- /dev/null +++ b/core/sr-arithmetic/fuzzer/Cargo.lock @@ -0,0 +1,465 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arbitrary" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitvec" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byte-slice-cast" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fixed-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.12" +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)", + "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)", +] + +[[package]] +name = "honggfuzz" +version = "0.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "impl-codec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-scale-codec" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.2 (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.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "primitive-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "toml 0.5.3 (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)", +] + +[[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" +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)", +] + +[[package]] +name = "quote" +version = "1.0.2" +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)", +] + +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (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)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +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)", +] + +[[package]] +name = "rustc-hex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.101" +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)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sr-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.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-std 2.0.0", +] + +[[package]] +name = "sr-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.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-arithmetic 2.0.0", +] + +[[package]] +name = "sr-std" +version = "2.0.0" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "static_assertions" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[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.5" +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)", + "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)", +] + +[[package]] +name = "toml" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uint" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[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" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7cbcbf18128ec71d8d4a0d054461ec59fff5b75b7d10a4c9b7c7cb1a379c3e77" +"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.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6357b15872f8126e4ea7cf79d579473f132ccd2de239494ad1bf4aa892faea68" +"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)" = "24c27b4aa3049d6d10d8e33d52c9d03ca9aec18f8a449b246f8c4a5b0c10fb34" +"checksum impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa0086251524c50fd53b32e7b05eb6d79e2f97221eaf0c53c0ca9c3096f21d3" +"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.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b5a08dda18910f056e5c2060c034e77cab18e0bd7d895e44f03207af4c71d5" +"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 rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"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 serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +"checksum uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8f0f47ed099f0db671ce82c66548c5de012e3c0cba3963514d1db15c7588701" +"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" diff --git a/core/sr-arithmetic/fuzzer/Cargo.toml b/core/sr-arithmetic/fuzzer/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..482905c4350106cffa386455c487c030fab16829 --- /dev/null +++ b/core/sr-arithmetic/fuzzer/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "sr-arithmetic-fuzzer" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +sr-arithmetic = { path = ".." } +honggfuzz = "0.5" +primitive-types = "0.6" +num-bigint = "0.2" +num-traits = "0.2" + +[workspace] + +[[bin]] +name = "biguint" +path = "src/biguint.rs" + +[[bin]] +name = "rational128" +path = "src/rational128.rs" diff --git a/core/sr-arithmetic/fuzzer/src/biguint.rs b/core/sr-arithmetic/fuzzer/src/biguint.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd270a97cac7102b37d3bd400f7c02c080e6cdaf --- /dev/null +++ b/core/sr-arithmetic/fuzzer/src/biguint.rs @@ -0,0 +1,181 @@ +// 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 . + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run biguint`. `honggfuzz` CLI options can +//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug biguint hfuzz_workspace/biguint/*.fuzz`. +//! +//! # More infomation +//! More information about `honggfuzz` can be found +//! [here](https://docs.rs/honggfuzz/). + +use honggfuzz::fuzz; +use sr_arithmetic::biguint::{BigUint, Single}; +use std::convert::TryFrom; + +fn main() { + loop { + fuzz!(|data: (Vec, Vec, bool)| { + let (mut digits_u, mut digits_v, return_remainder) = data; + + let mut u = BigUint::from_limbs(&digits_u); + let mut v = BigUint::from_limbs(&digits_v); + + u.lstrip(); + v.lstrip(); + + let ue = u128::try_from(u.clone()); + let ve = u128::try_from(v.clone()); + + digits_u.reverse(); + digits_v.reverse(); + + let num_u = num_bigint::BigUint::new(digits_u.clone()); + let num_v = num_bigint::BigUint::new(digits_v.clone()); + + if check_digit_lengths(&u, &v, 4) { + assert_eq!(u.cmp(&v), ue.cmp(&ve)); + assert_eq!(u.eq(&v), ue.eq(&ve)); + } + + if check_digit_lengths(&u, &v, 3) { + let expected = ue.unwrap() + ve.unwrap(); + let t = u.clone().add(&v); + assert_eq!( + u128::try_from(t.clone()).unwrap(), expected, + "{:?} + {:?} ===> {:?} != {:?}", u, v, t, expected, + ); + } + + if check_digit_lengths(&u, &v, 4) { + let expected = ue.unwrap().checked_sub(ve.unwrap()); + let t = u.clone().sub(&v); + if expected.is_none() { + assert!(t.is_err()) + } else { + let t = t.unwrap(); + let expected = expected.unwrap(); + assert_eq!( + u128::try_from(t.clone()).unwrap(), expected, + "{:?} - {:?} ===> {:?} != {:?}", u, v, t, expected, + ); + } + } + + if check_digit_lengths(&u, &v, 2) { + let expected = ue.unwrap() * ve.unwrap(); + let t = u.clone().mul(&v); + assert_eq!( + u128::try_from(t.clone()).unwrap(), expected, + "{:?} * {:?} ===> {:?} != {:?}", u, v, t, expected, + ); + } + + if check_digit_lengths(&u, &v, 4) { + let (ue, ve) = (ue.unwrap(), ve.unwrap()); + if ve == 0 { + return; + } + let (q, r) = (ue / ve, ue % ve); + if let Some((qq, rr)) = u.clone().div(&v, true) { + assert_eq!( + u128::try_from(qq.clone()).unwrap(), q, + "{:?} / {:?} ===> {:?} != {:?}", u, v, qq, q, + ); + assert_eq!( + u128::try_from(rr.clone()).unwrap(), r, + "{:?} % {:?} ===> {:?} != {:?}", u, v, rr, r, + ); + } else if v.len() == 1 { + let qq = u.clone().div_unit(ve as Single); + assert_eq!( + u128::try_from(qq.clone()).unwrap(), q, + "[single] {:?} / {:?} ===> {:?} != {:?}", u, v, qq, q, + ); + } else if v.msb() != 0 && u.msb() != 0 && u.len() > v.len() { + panic!("div returned none for an unexpected reason"); + } + } + + // Test against num_bigint + + // Equality + + assert_eq!(u.cmp(&v), num_u.cmp(&num_v)); + + // Addition + + let w = u.clone().add(&v); + let num_w = num_u.clone() + &num_v; + + assert_biguints_eq(&w, &num_w); + + // Subtraction + + if let Ok(w) = u.clone().sub(&v) { + let num_w = num_u.clone() - &num_v; + + assert_biguints_eq(&w, &num_w); + } + + // Multiplication + + let w = u.clone().mul(&v); + let num_w = num_u.clone() * &num_v; + + assert_biguints_eq(&w, &num_w); + + // Division + + if v.len() == 1 && v.get(0) != 0 { + let w = u.clone().div_unit(v.get(0)); + let num_w = num_u.clone() / &num_v; + assert_biguints_eq(&w, &num_w); + } else if u.len() > v.len() && v.len() > 0 { + let num_remainder = num_u.clone() % num_v.clone(); + + let (w, remainder) = u.clone().div(&v, return_remainder).unwrap(); + let num_w = num_u.clone() / &num_v; + + assert_biguints_eq(&w, &num_w); + + if return_remainder { + assert_biguints_eq(&remainder, &num_remainder); + } + } + }); + } +} + +fn check_digit_lengths(u: &BigUint, v: &BigUint, max_limbs: usize) -> bool { + 1 <= u.len() && u.len() <= max_limbs && 1 <= v.len() && v.len() <= max_limbs +} + +fn assert_biguints_eq(a: &BigUint, b: &num_bigint::BigUint) { + let mut a = a.clone(); + a.lstrip(); + + // `num_bigint::BigUint` doesn't expose it's internals, so we need to convert into that to + // compare. + let limbs = (0 .. a.len()).map(|i| a.get(i)).collect(); + let num_a = num_bigint::BigUint::new(limbs); + + assert!(&num_a == b, "\narithmetic: {:?}\nnum-bigint: {:?}", a, b); +} diff --git a/core/sr-arithmetic/fuzzer/src/rational128.rs b/core/sr-arithmetic/fuzzer/src/rational128.rs new file mode 100644 index 0000000000000000000000000000000000000000..b2a00d754527f042cec53f4f6986d8571ad7530e --- /dev/null +++ b/core/sr-arithmetic/fuzzer/src/rational128.rs @@ -0,0 +1,77 @@ +// 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 . + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run rational128`. `honggfuzz` CLI options can +//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug rational128 hfuzz_workspace/rational128/*.fuzz`. +//! +//! # More infomation +//! More information about `honggfuzz` can be found +//! [here](https://docs.rs/honggfuzz/). + +use honggfuzz::fuzz; +use sr_arithmetic::{helpers_128bit::multiply_by_rational, traits::Zero}; + +fn main() { + loop { + fuzz!(|data: ([u8; 16], [u8; 16], [u8; 16])| { + let (a_bytes, b_bytes, c_bytes) = data; + let (a, b, c) = ( + u128::from_be_bytes(a_bytes), + u128::from_be_bytes(b_bytes), + u128::from_be_bytes(c_bytes), + ); + + println!("++ Equation: {} * {} / {}", a, b, c); + + // The point of this fuzzing is to make sure that `multiply_by_rational` is 100% + // accurate as long as the value fits in a u128. + if let Ok(result) = multiply_by_rational(a, b, c) { + let truth = mul_div(a, b, c); + + if result != truth && result != truth + 1 { + println!("++ Expected {}", truth); + println!("+++++++ Got {}", result); + panic!(); + } + } + }) + } +} + +fn mul_div(a: u128, b: u128, c: u128) -> u128 { + use primitive_types::U256; + if a.is_zero() { + return Zero::zero(); + } + let c = c.max(1); + + // e for extended + let ae: U256 = a.into(); + let be: U256 = b.into(); + let ce: U256 = c.into(); + + let r = ae * be / ce; + if r > u128::max_value().into() { + a + } else { + r.as_u128() + } +} diff --git a/core/sr-arithmetic/src/biguint.rs b/core/sr-arithmetic/src/biguint.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a701a5ebd460cd950e312a11fbc3ca2e6c15769 --- /dev/null +++ b/core/sr-arithmetic/src/biguint.rs @@ -0,0 +1,735 @@ +// 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 . + +//! Infinite precision unsigned integer for substrate runtime. + +use num_traits::Zero; +use rstd::{cmp::Ordering, ops, prelude::*, cell::RefCell, convert::TryFrom}; + +// A sensible value for this would be half of the dword size of the host machine. Since the +// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively +// should yield the most performance. +/// Representation of a single limb. +pub type Single = u32; +/// Representation of two limbs. +pub type Double = u64; +/// Difference in the number of bits of [`Single`] and [`Double`]. +const SHIFT: usize = 32; +/// short form of _Base_. Analogous to the value 10 in base-10 decimal numbers. +const B: Double = Single::max_value() as Double + 1; + +/// Splits a [`Double`] limb number into a tuple of two [`Single`] limb numbers. +pub fn split(a: Double) -> (Single, Single) { + let al = a as Single; + let ah = (a >> SHIFT) as Single; + (ah, al) +} + +/// Assumed as a given primitive. +/// +/// Multiplication of two singles, which at most yields 1 double. +pub fn mul_single(a: Single, b: Single) -> Double { + let a: Double = a.into(); + let b: Double = b.into(); + a * b +} + +/// Assumed as a given primitive. +/// +/// Addition of two singles, which at most takes a single limb of result and a carry, +/// returned as a tuple respectively. +pub fn add_single(a: Single, b: Single) -> (Single, Single) { + let a: Double = a.into(); + let b: Double = b.into(); + let q = a + b; + let (carry, r) = split(q); + (r, carry) +} + +/// Assumed as a given primitive. +/// +/// Division of double by a single limb. Always returns a double limb of quotient and a single +/// limb of remainder. +fn div_single(a: Double, b: Single) -> (Double, Single) { + let b: Double = b.into(); + let q = a / b; + let r = a % b; + // both conversions are trivially safe. + (q, r as Single) +} + +/// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`]. +#[derive(Clone, Default)] +pub struct BigUint { + /// digits (limbs) of this number (sorted as msb -> lsd). + pub(crate) digits: Vec, +} + +impl BigUint { + /// Create a new instance with `size` limbs. This prevents any number with zero limbs to be + /// created. + /// + /// The behavior of the type is undefined with zero limbs. + pub fn with_capacity(size: usize) -> Self { + Self { digits: vec![0; size.max(1)] } + } + + /// Raw constructor from custom limbs. If `limbs` is empty, `Zero::zero()` implementation is + /// used. + pub fn from_limbs(limbs: &[Single]) -> Self { + if !limbs.is_empty() { + Self { digits: limbs.to_vec() } + } else { + Zero::zero() + } + } + + /// Number of limbs. + pub fn len(&self) -> usize { self.digits.len() } + + /// A naive getter for limb at `index`. Note that the order is lsb -> msb. + /// + /// #### Panics + /// + /// This panics if index is out of range. + pub fn get(&self, index: usize) -> Single { + self.digits[self.len() - 1 - index] + } + + /// A naive getter for limb at `index`. Note that the order is lsb -> msb. + pub fn checked_get(&self, index: usize) -> Option { + let i = self.len().checked_sub(1)?; + let j = i.checked_sub(index)?; + self.digits.get(j).cloned() + } + + /// A naive setter for limb at `index`. Note that the order is lsb -> msb. + /// + /// #### Panics + /// + /// This panics if index is out of range. + pub fn set(&mut self, index: usize, value: Single) { + let len = self.digits.len(); + self.digits[len - 1 - index] = value; + } + + /// returns the least significant limb of the number. + /// + /// #### Panics + /// + /// While the constructor of the type prevents this, this can panic if `self` has no digits. + pub fn lsb(&self) -> Single { + self.digits[self.len() - 1] + } + + /// returns the most significant limb of the number. + /// + /// #### Panics + /// + /// While the constructor of the type prevents this, this can panic if `self` has no digits. + pub fn msb(&self) -> Single { + self.digits[0] + } + + /// Strips zeros from the left side (the most significant limbs) of `self`, if any. + pub fn lstrip(&mut self) { + // by definition, a big-int number should never have leading zero limbs. This function + // has the ability to cause this. There is nothing to do if the number already has 1 + // limb only. call it a day and return. + if self.len().is_zero() { return; } + let index = self.digits.iter().position(|&elem| elem != 0).unwrap_or(0); + + if index > 0 { + self.digits = self.digits[index..].to_vec() + } + } + + /// Zero-pad `self` from left to reach `size` limbs. Will not make any difference if `self` + /// is already bigger than `size` limbs. + pub fn lpad(&mut self, size: usize) { + let n = self.len(); + if n >= size { return; } + let pad = size - n; + let mut new_digits = (0..pad).map(|_| 0).collect::>(); + new_digits.extend(self.digits.iter()); + self.digits = new_digits; + } + + /// Adds `self` with `other`. self and other do not have to have any particular size. Given + /// that the `n = max{size(self), size(other)}`, it will produce a number with `n + 1` + /// limbs. + /// + /// This function does not strip the output and returns the original allocated `n + 1` + /// limbs. The caller may strip the output if desired. + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn add(self, other: &Self) -> Self { + let n = self.len().max(other.len()); + let mut k: Double = 0; + let mut w = Self::with_capacity(n + 1); + + for j in 0..n { + let u = Double::from(self.checked_get(j).unwrap_or(0)); + let v = Double::from(other.checked_get(j).unwrap_or(0)); + let s = u + v + k; + w.set(j, (s % B) as Single); + k = s / B; + } + // k is always 0 or 1. + w.set(n, k as Single); + w + } + + /// Subtracts `other` from `self`. self and other do not have to have any particular size. + /// Given that the `n = max{size(self), size(other)}`, it will produce a number of size `n`. + /// + /// If `other` is bigger than `self`, `Err(B - borrow)` is returned. + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn sub(self, other: &Self) -> Result { + let n = self.len().max(other.len()); + let mut k = 0; + let mut w = Self::with_capacity(n); + for j in 0..n { + let s = { + let u = Double::from(self.checked_get(j).unwrap_or(0)); + let v = Double::from(other.checked_get(j).unwrap_or(0)); + let mut needs_borrow = false; + let mut t = 0; + + if let Some(v) = u.checked_sub(v) { + if let Some(v2) = v.checked_sub(k) { + t = v2 % B; + k = 0; + } else { + needs_borrow = true; + } + } else { + needs_borrow = true; + } + if needs_borrow { + t = u + B - v - k; + k = 1; + } + t + }; + // PROOF: t either comes from `v2 % B`, or from `u + B - v - k`. The former is + // trivial. The latter will not overflow this branch will only happen if the sum of + // `u - v - k` part has been negative, hence `u + B - v - k < b`. + w.set(j, s as Single); + } + + if k.is_zero() { + Ok(w) + } else { + Err(w) + } + } + + /// Multiplies n-limb number `self` with m-limb number `other`. + /// + /// The resulting number will always have `n + m` limbs. + /// + /// This function does not strip the output and returns the original allocated `n + m` + /// limbs. The caller may strip the output if desired. + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn mul(self, other: &Self) -> Self { + let n = self.len(); + let m = other.len(); + let mut w = Self::with_capacity(m + n); + + for j in 0..n { + if self.get(j) == 0 { + // Note: `with_capacity` allocates with 0. Explicitly set j + m to zero if + // otherwise. + continue; + } + + let mut k = 0; + for i in 0..m { + // PROOF: (B−1) × (B−1) + (B−1) + (B−1) = B^2 −1 < B^2. addition is safe. + let t = + mul_single(self.get(j), other.get(i)) + + Double::from(w.get(i + j)) + + Double::from(k); + w.set(i + j, (t % B) as Single); + // PROOF: (B^2 - 1) / B < B. conversion is safe. + k = (t / B) as Single; + } + w.set(j + m, k); + } + w + } + + /// Divides `self` by a single limb `other`. This can be used in cases where the original + /// division cannot work due to the divisor (`other`) being just one limb. + /// + /// Invariant: `other` cannot be zero. + pub fn div_unit(self, mut other: Single) -> Self { + other = other.max(1); + let n = self.len(); + let mut out = Self::with_capacity(n); + let mut r: Single = 0; + // PROOF: (B-1) * B + (B-1) still fits in double + let with_r = |x: Double, r: Single| { Double::from(r) * B + x }; + for d in (0..n).rev() { + let (q, rr) = div_single(with_r(self.get(d).into(), r), other) ; + out.set(d, q as Single); + r = rr; + } + out + } + + /// Divides an `n + m` limb self by a `n` limb `other`. The result is a `m + 1` limb + /// quotient and a `n` limb remainder, if enabled by passing `true` in `rem` argument, both + /// in the form of an option's `Ok`. + /// + /// - requires `other` to be stripped and have no leading zeros. + /// - requires `self` to be stripped and have no leading zeros. + /// - requires `other` to have at least two limbs. + /// - requires `self` to have a greater length compared to `other`. + /// + /// All arguments are examined without being stripped for the above conditions. If any of + /// the above fails, `None` is returned.` + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn div(self, other: &Self, rem: bool) -> Option<(Self, Self)> { + if other.len() <= 1 + || other.msb() == 0 + || self.msb() == 0 + || self.len() <= other.len() + { + return None + } + let n = other.len(); + let m = self.len() - n; + + let mut q = Self::with_capacity(m + 1); + let mut r = Self::with_capacity(n); + + // PROOF: 0 <= normalizer_bits < SHIFT 0 <= normalizer < B. all conversions are + // safe. + let normalizer_bits = other.msb().leading_zeros() as Single; + let normalizer = (2 as Single).pow(normalizer_bits as u32) as Single; + + // step D1. + let mut self_norm = self.mul(&Self::from(normalizer)); + let mut other_norm = other.clone().mul(&Self::from(normalizer)); + + // defensive only; the mul implementation should always create this. + self_norm.lpad(n + m + 1); + other_norm.lstrip(); + + // step D2. + for j in (0..=m).rev() { + // step D3.0 Find an estimate of q[j], named qhat. + let (qhat, rhat) = { + // PROOF: this always fits into `Double`. In the context of Single = u8, and + // Double = u16, think of 255 * 256 + 255 which is just u16::max_value(). + let dividend = + Double::from(self_norm.get(j + n)) + * B + + Double::from(self_norm.get(j + n - 1)); + let divisor = other_norm.get(n - 1); + div_single(dividend, divisor) + }; + + // D3.1 test qhat + // replace qhat and rhat with RefCells. This helps share state with the closure + let qhat = RefCell::new(qhat); + let rhat = RefCell::new(Double::from(rhat)); + + let test = || { + // decrease qhat if it is bigger than the base (B) + let qhat_local = *qhat.borrow(); + let rhat_local = *rhat.borrow(); + let predicate_1 = qhat_local >= B; + let predicate_2 = { + let lhs = qhat_local * Double::from(other_norm.get(n - 2)); + let rhs = B * rhat_local + Double::from(self_norm.get(j + n - 2)); + lhs > rhs + }; + if predicate_1 || predicate_2 { + *qhat.borrow_mut() -= 1; + *rhat.borrow_mut() += Double::from(other_norm.get(n - 1)); + true + } else { + false + } + }; + + test(); + while (*rhat.borrow() as Double) < B { + if !test() { break; } + } + + let qhat = qhat.into_inner(); + // we don't need rhat anymore. just let it go out of scope when it does. + + // step D4 + let lhs = Self { digits: (j..=j+n).rev().map(|d| self_norm.get(d)).collect() }; + let rhs = other_norm.clone().mul(&Self::from(qhat)); + + let maybe_sub = lhs.sub(&rhs); + let mut negative = false; + let sub = match maybe_sub { + Ok(t) => t, + Err(t) => { negative = true; t } + }; + (j..=j+n).for_each(|d| { self_norm.set(d, sub.get(d - j)); }); + + // step D5 + // PROOF: the `test()` specifically decreases qhat until it is below `B`. conversion + // is safe. + q.set(j, qhat as Single); + + // step D6: add back if negative happened. + if negative { + q.set(j, q.get(j) - 1); + let u = Self { digits: (j..=j+n).rev().map(|d| self_norm.get(d)).collect() }; + let r = other_norm.clone().add(&u); + (j..=j+n).rev().for_each(|d| { self_norm.set(d, r.get(d - j)); }) + } + } + + // if requested, calculate remainder. + if rem { + // undo the normalization. + if normalizer_bits > 0 { + let s = SHIFT as u32; + let nb = normalizer_bits; + for d in 0..n-1 { + let v = self_norm.get(d) >> nb + | self_norm.get(d + 1).overflowing_shl(s - nb).0; + r.set(d, v); + } + r.set(n - 1, self_norm.get(n - 1) >> normalizer_bits); + } else { + r = self_norm; + } + } + + Some((q, r)) + } +} + +impl rstd::fmt::Debug for BigUint { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { + write!( + f, + "BigUint {{ {:?} ({:?})}}", + self.digits, + u128::try_from(self.clone()).unwrap_or(0), + ) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { + Ok(()) + } + +} + +impl PartialEq for BigUint { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for BigUint {} + +impl Ord for BigUint { + fn cmp(&self, other: &Self) -> Ordering { + let lhs_first = self.digits.iter().position(|&e| e != 0); + let rhs_first = other.digits.iter().position(|&e| e != 0); + + match (lhs_first, rhs_first) { + // edge cases that should not happen. This basically means that one or both were + // zero. + (None, None) => Ordering::Equal, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(lhs_idx), Some(rhs_idx)) => { + let lhs = &self.digits[lhs_idx..]; + let rhs = &other.digits[rhs_idx..]; + let len_cmp = lhs.len().cmp(&rhs.len()); + match len_cmp { + Ordering::Equal => lhs.cmp(rhs), + _ => len_cmp, + } + } + } + } +} + +impl PartialOrd for BigUint { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl ops::Add for BigUint { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + self.add(&rhs) + } +} + +impl ops::Sub for BigUint { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + self.sub(&rhs).unwrap_or_else(|e| e) + } +} + +impl ops::Mul for BigUint { + type Output = Self; + fn mul(self, rhs: Self) -> Self::Output { + self.mul(&rhs) + } +} + +impl Zero for BigUint { + fn zero() -> Self { + Self { digits: vec![Zero::zero()] } + } + + fn is_zero(&self) -> bool { + self.digits.iter().all(|d| d.is_zero()) + } +} + +macro_rules! impl_try_from_number_for { + ($([$type:ty, $len:expr]),+) => { + $( + impl TryFrom for $type { + type Error = &'static str; + fn try_from(mut value: BigUint) -> Result<$type, Self::Error> { + value.lstrip(); + let error_message = concat!("cannot fit a number into ", stringify!($type)); + if value.len() * SHIFT > $len { + Err(error_message) + } else { + let mut acc: $type = Zero::zero(); + for (i, d) in value.digits.iter().rev().cloned().enumerate() { + let d: $type = d.into(); + acc += d << (SHIFT * i); + } + Ok(acc) + } + } + } + )* + }; +} +// can only be implemented for sizes bigger than two limb. +impl_try_from_number_for!([u128, 128], [u64, 64]); + +macro_rules! impl_from_for_smaller_than_word { + ($($type:ty),+) => { + $(impl From<$type> for BigUint { + fn from(a: $type) -> Self { + Self { digits: vec! [a.into()] } + } + })* + } +} +impl_from_for_smaller_than_word!(u8, u16, Single); + +impl From for BigUint { + fn from(a: Double) -> Self { + let (ah, al) = split(a); + Self { digits: vec![ah, al] } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + fn with_limbs(n: usize) -> BigUint { + BigUint { digits: vec![1; n] } + } + + #[test] + fn split_works() { + let a = SHIFT / 2; + let b = SHIFT * 3 / 2; + let num: Double = 1 << a | 1 << b; + // example when `Single = u8` + // assert_eq!(num, 0b_0001_0000_0001_0000) + assert_eq!(split(num), (1 << a, 1 << a)); + } + + #[test] + fn strip_works() { + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![1, 0] }); + + let mut a = BigUint::from_limbs(&[0, 0, 1]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![1] }); + + let mut a = BigUint::from_limbs(&[0, 0]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![0] }); + + let mut a = BigUint::from_limbs(&[0, 0, 0]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![0] }); + } + + #[test] + fn lpad_works() { + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lpad(2); + assert_eq!(a.digits, vec![0, 1, 0]); + + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lpad(3); + assert_eq!(a.digits, vec![0, 1, 0]); + + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lpad(4); + assert_eq!(a.digits, vec![0, 0, 1, 0]); + } + + #[test] + fn equality_works() { + assert_eq!( + BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, + true, + ); + assert_eq!( + BigUint { digits: vec![3, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, + false, + ); + assert_eq!( + BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, + true, + ); + } + + #[test] + fn ordering_works() { + assert!(BigUint { digits: vec![0] } < BigUint { digits: vec![1] }); + assert!(BigUint { digits: vec![0] } == BigUint { digits: vec![0] }); + assert!(BigUint { digits: vec![] } == BigUint { digits: vec![0] }); + assert!(BigUint { digits: vec![] } == BigUint { digits: vec![] }); + assert!(BigUint { digits: vec![] } < BigUint { digits: vec![1] }); + + assert!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }); + assert!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }); + + assert!(BigUint { digits: vec![1, 2, 4] } > BigUint { digits: vec![1, 2, 3] }); + assert!(BigUint { digits: vec![0, 1, 2, 4] } > BigUint { digits: vec![1, 2, 3] }); + assert!(BigUint { digits: vec![1, 2, 1, 0] } > BigUint { digits: vec![1, 2, 3] }); + + assert!(BigUint { digits: vec![0, 1, 2, 1] } < BigUint { digits: vec![1, 2, 3] }); + } + + #[test] + fn can_try_build_numbers_from_types() { + use rstd::convert::TryFrom; + assert_eq!(u64::try_from(with_limbs(1)).unwrap(), 1); + assert_eq!(u64::try_from(with_limbs(2)).unwrap(), u32::max_value() as u64 + 2); + assert_eq!( + u64::try_from(with_limbs(3)).unwrap_err(), + "cannot fit a number into u64", + ); + assert_eq!( + u128::try_from(with_limbs(3)).unwrap(), + u32::max_value() as u128 + u64::max_value() as u128 + 3 + ); + } + + #[test] + fn zero_works() { + assert_eq!(BigUint::zero(), BigUint { digits: vec![0] }); + assert_eq!(BigUint { digits: vec![0, 1, 0] }.is_zero(), false); + assert_eq!(BigUint { digits: vec![0, 0, 0] }.is_zero(), true); + + let a = BigUint::zero(); + let b = BigUint::zero(); + let c = a * b; + assert_eq!(c.digits, vec![0, 0]); + } + + #[test] + fn sub_negative_works() { + assert_eq!( + BigUint::from(10 as Single).sub(&BigUint::from(5 as Single)).unwrap(), + BigUint::from(5 as Single) + ); + assert_eq!( + BigUint::from(10 as Single).sub(&BigUint::from(10 as Single)).unwrap(), + BigUint::from(0 as Single) + ); + assert_eq!( + BigUint::from(10 as Single).sub(&BigUint::from(13 as Single)).unwrap_err(), + BigUint::from((B - 3) as Single), + ); + } + + #[test] + fn mul_always_appends_one_digit() { + let a = BigUint::from(10 as Single); + let b = BigUint::from(4 as Single); + assert_eq!(a.len(), 1); + assert_eq!(b.len(), 1); + + let n = a.mul(&b); + + assert_eq!(n.len(), 2); + assert_eq!(n.digits, vec![0, 40]); + } + + #[test] + fn div_conditions_work() { + let a = BigUint { digits: vec![2] }; + let b = BigUint { digits: vec![1, 2] }; + let c = BigUint { digits: vec![1, 1, 2] }; + let d = BigUint { digits: vec![0, 2] }; + let e = BigUint { digits: vec![0, 1, 1, 2] }; + + assert!(a.clone().div(&b, true).is_none()); + assert!(c.clone().div(&a, true).is_none()); + assert!(c.clone().div(&d, true).is_none()); + assert!(e.clone().div(&a, true).is_none()); + + assert!(c.clone().div(&b, true).is_some()); + } + + #[test] + fn div_unit_works() { + let a = BigUint { digits: vec![100] }; + let b = BigUint { digits: vec![1, 100] }; + + assert_eq!(a.clone().div_unit(1), a); + assert_eq!(a.clone().div_unit(0), a); + assert_eq!(a.clone().div_unit(2), BigUint::from(50 as Single)); + assert_eq!(a.clone().div_unit(7), BigUint::from(14 as Single)); + + assert_eq!(b.clone().div_unit(1), b); + assert_eq!(b.clone().div_unit(0), b); + assert_eq!(b.clone().div_unit(2), BigUint::from(((B + 100) / 2) as Single)); + assert_eq!(b.clone().div_unit(7), BigUint::from(((B + 100) / 7) as Single)); + + } +} diff --git a/core/sr-arithmetic/src/fixed64.rs b/core/sr-arithmetic/src/fixed64.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd58e36940ae6c1887302fd0ded53ba56232e244 --- /dev/null +++ b/core/sr-arithmetic/src/fixed64.rs @@ -0,0 +1,244 @@ +// 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 . + +use rstd::{ + ops, prelude::*, + convert::{TryFrom, TryInto}, +}; +use codec::{Encode, Decode}; +use crate::{ + Perbill, + traits::{ + SaturatedConversion, CheckedSub, CheckedAdd, Bounded, UniqueSaturatedInto, Saturating + } +}; + +/// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036] +/// with fixed point accuracy of one billion. +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Fixed64(i64); + +/// The accuracy of the `Fixed64` type. +const DIV: i64 = 1_000_000_000; + +impl Fixed64 { + /// creates self from a natural number. + /// + /// Note that this might be lossy. + pub fn from_natural(int: i64) -> Self { + Self(int.saturating_mul(DIV)) + } + + /// Return the accuracy of the type. Given that this function returns the value `X`, it means + /// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`. + pub fn accuracy() -> i64 { + DIV + } + + /// Consume self and return the inner value. + /// + /// This should only be used for testing. + #[cfg(any(feature = "std", test))] + pub fn into_inner(self) -> i64 { self.0 } + + /// Raw constructor. Equal to `parts / 1_000_000_000`. + pub fn from_parts(parts: i64) -> Self { + Self(parts) + } + + /// creates self from a rational number. Equal to `n/d`. + /// + /// Note that this might be lossy. + pub fn from_rational(n: i64, d: u64) -> Self { + Self( + (i128::from(n).saturating_mul(i128::from(DIV)) / i128::from(d).max(1)) + .try_into() + .unwrap_or_else(|_| Bounded::max_value()) + ) + } + + /// Performs a saturated multiply and accumulate by unsigned number. + /// + /// Returns a saturated `int + (self * int)`. + pub fn saturated_multiply_accumulate(self, int: N) -> N + where + N: TryFrom + From + UniqueSaturatedInto + Bounded + Clone + Saturating + + ops::Rem + ops::Div + ops::Mul + + ops::Add, + { + let div = DIV as u64; + let positive = self.0 > 0; + // safe to convert as absolute value. + let parts = self.0.checked_abs().map(|v| v as u64).unwrap_or(i64::max_value() as u64 + 1); + + + // will always fit. + let natural_parts = parts / div; + // might saturate. + let natural_parts: N = natural_parts.saturated_into(); + // fractional parts can always fit into u32. + let perbill_parts = (parts % div) as u32; + + let n = int.clone().saturating_mul(natural_parts); + let p = Perbill::from_parts(perbill_parts) * int.clone(); + + // everything that needs to be either added or subtracted from the original weight. + let excess = n.saturating_add(p); + + if positive { + int.saturating_add(excess) + } else { + int.saturating_sub(excess) + } + } +} + +impl Saturating for Fixed64 { + fn saturating_add(self, rhs: Self) -> Self { + Self(self.0.saturating_add(rhs.0)) + } + fn saturating_mul(self, rhs: Self) -> Self { + Self(self.0.saturating_mul(rhs.0) / DIV) + } + fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } +} + +/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait +/// for safe addition. +impl ops::Add for Fixed64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait +/// for safe subtraction. +impl ops::Sub for Fixed64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl CheckedSub for Fixed64 { + fn checked_sub(&self, rhs: &Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } +} + +impl CheckedAdd for Fixed64 { + fn checked_add(&self, rhs: &Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } +} + +impl rstd::fmt::Debug for Fixed64 { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "Fixed64({},{})", self.0 / DIV, (self.0 % DIV) / 1000) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn max() -> Fixed64 { + Fixed64::from_parts(i64::max_value()) + } + + #[test] + fn fixed64_semantics() { + assert_eq!(Fixed64::from_rational(5, 2).0, 5 * 1_000_000_000 / 2); + assert_eq!(Fixed64::from_rational(5, 2), Fixed64::from_rational(10, 4)); + assert_eq!(Fixed64::from_rational(5, 0), Fixed64::from_rational(5, 1)); + + // biggest value that can be created. + assert_ne!(max(), Fixed64::from_natural(9_223_372_036)); + assert_eq!(max(), Fixed64::from_natural(9_223_372_037)); + } + + #[test] + fn fixed_64_growth_decrease_curve() { + let test_set = vec![0u32, 1, 10, 1000, 1_000_000_000]; + + // negative (1/2) + let mut fm = Fixed64::from_rational(-1, 2); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i) as i32, i as i32 - i as i32 / 2); + }); + + // unit (1) multiplier + fm = Fixed64::from_parts(0); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i); + }); + + // i.5 multiplier + fm = Fixed64::from_rational(1, 2); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i * 3 / 2); + }); + + // dual multiplier + fm = Fixed64::from_rational(1, 1); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i * 2); + }); + } + + macro_rules! saturating_mul_acc_test { + ($num_type:tt) => { + assert_eq!( + Fixed64::from_rational(100, 1).saturated_multiply_accumulate(10 as $num_type), + 1010, + ); + assert_eq!( + Fixed64::from_rational(100, 2).saturated_multiply_accumulate(10 as $num_type), + 510, + ); + assert_eq!( + Fixed64::from_rational(100, 3).saturated_multiply_accumulate(0 as $num_type), + 0, + ); + assert_eq!( + Fixed64::from_rational(5, 1).saturated_multiply_accumulate($num_type::max_value()), + $num_type::max_value() + ); + assert_eq!( + max().saturated_multiply_accumulate($num_type::max_value()), + $num_type::max_value() + ); + } + } + + #[test] + fn fixed64_multiply_accumulate_works() { + saturating_mul_acc_test!(u32); + saturating_mul_acc_test!(u64); + saturating_mul_acc_test!(u128); + } +} diff --git a/core/sr-arithmetic/src/helpers_128bit.rs b/core/sr-arithmetic/src/helpers_128bit.rs new file mode 100644 index 0000000000000000000000000000000000000000..10cc94ae777e66d2f802a464947f7bce4385301e --- /dev/null +++ b/core/sr-arithmetic/src/helpers_128bit.rs @@ -0,0 +1,112 @@ +// 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 . + +//! Some helper functions to work with 128bit numbers. Note that the functionality provided here is +//! only sensible to use with 128bit numbers because for smaller sizes, you can always rely on +//! assumptions of a bigger type (u128) being available, or simply create a per-thing and use the +//! multiplication implementation provided there. + +use crate::biguint; +use num_traits::Zero; +use rstd::{cmp::{min, max}, convert::TryInto, mem}; + +/// Helper gcd function used in Rational128 implementation. +pub fn gcd(a: u128, b: u128) -> u128 { + match ((a, b), (a & 1, b & 1)) { + ((x, y), _) if x == y => y, + ((0, x), _) | ((x, 0), _) => x, + ((x, y), (0, 1)) | ((y, x), (1, 0)) => gcd(x >> 1, y), + ((x, y), (0, 0)) => gcd(x >> 1, y >> 1) << 1, + ((x, y), (1, 1)) => { + let (x, y) = (min(x, y), max(x, y)); + gcd((y - x) >> 1, x) + }, + _ => unreachable!(), + } +} + +/// split a u128 into two u64 limbs +pub fn split(a: u128) -> (u64, u64) { + let al = a as u64; + let ah = (a >> 64) as u64; + (ah, al) +} + +/// Convert a u128 to a u32 based biguint. +pub fn to_big_uint(x: u128) -> biguint::BigUint { + let (xh, xl) = split(x); + let (xhh, xhl) = biguint::split(xh); + let (xlh, xll) = biguint::split(xl); + let mut n = biguint::BigUint::from_limbs(&[xhh, xhl, xlh, xll]); + n.lstrip(); + n +} + +/// Safely and accurately compute `a * b / c`. The approach is: +/// - Simply try `a * b / c`. +/// - Else, convert them both into big numbers and re-try. `Err` is returned if the result +/// cannot be safely casted back to u128. +/// +/// Invariant: c must be greater than or equal to 1. +pub fn multiply_by_rational(mut a: u128, mut b: u128, mut c: u128) -> Result { + if a.is_zero() || b.is_zero() { return Ok(Zero::zero()); } + c = c.max(1); + + // a and b are interchangeable by definition in this function. It always helps to assume the + // bigger of which is being multiplied by a `0 < b/c < 1`. Hence, a should be the bigger and + // b the smaller one. + if b > a { + mem::swap(&mut a, &mut b); + } + + // Attempt to perform the division first + if a % c == 0 { + a /= c; + c = 1; + } else if b % c == 0 { + b /= c; + c = 1; + } + + if let Some(x) = a.checked_mul(b) { + // This is the safest way to go. Try it. + Ok(x / c) + } else { + let a_num = to_big_uint(a); + let b_num = to_big_uint(b); + let c_num = to_big_uint(c); + + let mut ab = a_num * b_num; + ab.lstrip(); + let mut q = if c_num.len() == 1 { + // PROOF: if `c_num.len() == 1` then `c` fits in one limb. + ab.div_unit(c as biguint::Single) + } else { + // PROOF: both `ab` and `c` cannot have leading zero limbs; if length of `c` is 1, + // the previous branch would handle. Also, if ab for sure has a bigger size than + // c, because `a.checked_mul(b)` has failed, hence ab must be at least one limb + // bigger than c. In this case, returning zero is defensive-only and div should + // always return Some. + let (mut q, r) = ab.div(&c_num, true).unwrap_or((Zero::zero(), Zero::zero())); + let r: u128 = r.try_into() + .expect("reminder of div by c is always less than c; qed"); + if r > (c / 2) { q = q.add(&to_big_uint(1)); } + q + }; + q.lstrip(); + q.try_into().map_err(|_| "result cannot fit in u128") + } +} diff --git a/core/sr-arithmetic/src/lib.rs b/core/sr-arithmetic/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..7b285002e54001ced6f9fcdeccfbd246f4a47519 --- /dev/null +++ b/core/sr-arithmetic/src/lib.rs @@ -0,0 +1,44 @@ +// 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 . + +//! Minimal fixed point arithmetic primitives and types for runtime. + +#![cfg_attr(not(feature = "std"), no_std)] + +/// Copied from `sr-primitives` and documented there. +#[cfg(test)] +macro_rules! assert_eq_error_rate { + ($x:expr, $y:expr, $error:expr $(,)?) => { + assert!( + ($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)), + "{:?} != {:?} (with error rate {:?})", + $x, + $y, + $error, + ); + }; +} + +pub mod biguint; +pub mod helpers_128bit; +pub mod traits; +mod per_things; +mod fixed64; +mod rational128; + +pub use fixed64::Fixed64; +pub use per_things::{Percent, Permill, Perbill, Perquintill}; +pub use rational128::Rational128; diff --git a/core/sr-arithmetic/src/per_things.rs b/core/sr-arithmetic/src/per_things.rs new file mode 100644 index 0000000000000000000000000000000000000000..2dd1e62d0b4db7aa45cd39b5ed3088fb84167013 --- /dev/null +++ b/core/sr-arithmetic/src/per_things.rs @@ -0,0 +1,520 @@ +// 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 . + +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; + +use rstd::{ops, prelude::*, convert::TryInto}; +use codec::{Encode, Decode, CompactAs}; +use crate::traits::{SaturatedConversion, UniqueSaturatedInto, Saturating}; +use substrate_debug_derive::RuntimeDebug; + +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, Ord, PartialOrd))] + #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, RuntimeDebug, CompactAs)] + pub struct $name($type); + + impl $name { + /// Nothing. + pub fn zero() -> Self { Self(0) } + + /// `true` if this is nothing. + pub fn is_zero(&self) -> bool { self.0 == 0 } + + /// Everything. + pub 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 } + + /// 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 from a percent. 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)) + } + + /// Return the product of multiplication of this value by itself. + pub 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); + Self::from_rational_approximation(p, q) + } + + /// Converts a fraction into `Permill`. + #[cfg(feature = "std")] + pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) } + + /// 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 + { + // q cannot be zero. + let q = q.max((1 as $type).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()); + + // 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()) + .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()) + .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" + ); + + // `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. + let part = + p_reduce as $upper_type + * <$upper_type>::from($max) + / q_reduce as $upper_type; + + $name(part as $type) + } + } + + impl Saturating for $name { + fn saturating_add(self, rhs: Self) -> Self { + // defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow. + Self::from_parts(self.0.saturating_add(rhs.0)) + } + fn saturating_sub(self, rhs: Self) -> Self { + Self::from_parts(self.0.saturating_sub(rhs.0)) + } + fn saturating_mul(self, rhs: Self) -> Self { + let a = self.0 as $upper_type; + let b = rhs.0 as $upper_type; + let m = <$upper_type>::from($max); + let parts = a * b / m; + // This will always fit into $type. + Self::from_parts(parts as $type) + } + } + + impl ops::Div for $name { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + let p = self.0; + let q = rhs.0; + Self::from_rational_approximation(p, q) + } + } + + /// Overflow-prune multiplication. + /// + /// tailored to be used with a balance type. + impl ops::Mul for $name + where + N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem + + ops::Div + ops::Mul + ops::Add, + { + type Output = N; + fn mul(self, b: N) -> Self::Output { + let maximum: N = $max.into(); + let upper_max: $upper_type = $max.into(); + let part: N = self.0.into(); + + let rem_multiplied_divided = { + let rem = b.clone().rem(maximum.clone()); + + // `rem_sized` is inferior to $max, thus it fits into $type. This is assured by + // a test. + let rem_sized = rem.saturated_into::<$type>(); + + // `self` and `rem_sized` are inferior to $max, thus the product is less than + // $max^2 and fits into $upper_type. This is assured by a test. + let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type; + + // `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits + // in $type. remember that $type always fits $max. + let mut rem_multiplied_divided_sized = + (rem_multiplied_upper / upper_max) as $type; + // fix a tiny rounding error + if rem_multiplied_upper % upper_max > upper_max / 2 { + rem_multiplied_divided_sized += 1; + } + + // `rem_multiplied_divided_sized` is inferior to b, thus it can be converted + // back to N type + rem_multiplied_divided_sized.into() + }; + + (b / maximum) * part + rem_multiplied_divided + } + } + + #[cfg(test)] + mod $test_mod { + use codec::{Encode, Decode}; + use super::{$name, Saturating, RuntimeDebug}; + use crate::traits::Zero; + + + #[test] + fn macro_expanded_correctly() { + // needed for the `from_percent` to work. + assert!($max >= 100); + assert!($max % 100 == 0); + + // needed for `from_rational_approximation` + assert!(2 * $max < <$type>::max_value()); + assert!(<$upper_type>::from($max) < <$upper_type>::max_value()); + + // for something like percent they can be the same. + assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value()); + assert!(<$upper_type>::from($max).checked_mul($max.into()).is_some()); + } + + #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)] + struct WithCompact { + data: T, + } + + #[test] + fn has_compact() { + let data = WithCompact { data: $name(1) }; + let encoded = data.encode(); + assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap()); + } + + #[test] + fn compact_encoding() { + let tests = [ + // assume all per_things have the size u8 at least. + (0 as $type, 1usize), + (1 as $type, 1usize), + (63, 1), + (64, 2), + (65, 2), + (<$type>::max_value(), <$type>::max_value().encode().len() + 1) + ]; + for &(n, l) in &tests { + let compact: codec::Compact<$name> = $name(n).into(); + let encoded = compact.encode(); + assert_eq!(encoded.len(), l); + let decoded = >::decode(&mut & encoded[..]) + .unwrap(); + let per_thingy: $name = decoded.into(); + assert_eq!(per_thingy, $name(n)); + } + } + + #[test] + fn per_thing_api_works() { + // 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::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)); + } + + macro_rules! per_thing_mul_test { + ($num_type:tt) => { + // multiplication from all sort of from_percent + assert_eq!( + $name::from_percent(100) * $num_type::max_value(), + $num_type::max_value() + ); + assert_eq_error_rate!( + $name::from_percent(99) * $num_type::max_value(), + ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, + 1, + ); + assert_eq!( + $name::from_percent(50) * $num_type::max_value(), + $num_type::max_value() / 2, + ); + assert_eq_error_rate!( + $name::from_percent(1) * $num_type::max_value(), + $num_type::max_value() / 100, + 1, + ); + assert_eq!($name::from_percent(0) * $num_type::max_value(), 0); + + // // multiplication with bounds + assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value()); + assert_eq!($name::zero() * $num_type::max_value(), 0); + } + } + + #[test] + fn per_thing_mul_works() { + use primitive_types::U256; + + // accuracy test + assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10); + + $(per_thing_mul_test!($test_units);)* + } + + #[test] + fn per_thing_mul_rounds_to_nearest_number() { + assert_eq!($name::from_percent(33) * 10u64, 3); + assert_eq!($name::from_percent(34) * 10u64, 3); + assert_eq!($name::from_percent(35) * 10u64, 3); + assert_eq!($name::from_percent(36) * 10u64, 4); + assert_eq!($name::from_percent(36) * 10u64, 4); + } + + #[test] + fn per_thing_multiplication_with_large_number() { + use primitive_types::U256; + let max_minus_one = $max - 1; + assert_eq_error_rate!( + $name::from_parts(max_minus_one) * std::u128::MAX, + ((Into::::into(std::u128::MAX) * max_minus_one) / $max).as_u128(), + 1, + ); + } + + macro_rules! per_thing_from_rationale_approx_test { + ($num_type:tt) => { + // within accuracy boundary + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 0), + $name::one(), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 1), + $name::one(), + ); + assert_eq_error_rate!( + $name::from_rational_approximation(1 as $num_type, 3).0, + $name::from_parts($max / 3).0, + 2 + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 10), + $name::from_percent(10), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 4), + $name::from_percent(25), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 4), + $name::from_rational_approximation(2 as $num_type, 8), + ); + // no accurate anymore but won't overflow. + assert_eq!( + $name::from_rational_approximation( + $num_type::max_value() - 1, + $num_type::max_value() + ), + $name::one(), + ); + assert_eq_error_rate!( + $name::from_rational_approximation( + $num_type::max_value() / 3, + $num_type::max_value() + ).0, + $name::from_parts($max / 3).0, + 2 + ); + assert_eq!( + $name::from_rational_approximation(1, $num_type::max_value()), + $name::zero(), + ); + }; + } + + #[test] + fn per_thing_from_rationale_approx_works() { + // This is just to make sure something like Percent which _might_ get built from a + // u8 does not overflow in the context of this test. + let max_value = <$upper_type>::from($max); + // almost at the edge + assert_eq!( + $name::from_rational_approximation($max - 1, $max + 1), + $name::from_parts($max - 2), + ); + assert_eq!( + $name::from_rational_approximation(1, $max-1), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(1, $max), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(2, 2 * $max - 1), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(1, $max+1), + $name::zero(), + ); + assert_eq!( + $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), + $name::from_percent(50), + ); + $(per_thing_from_rationale_approx_test!($test_units);)* + } + + #[test] + fn per_things_mul_operates_in_output_type() { + // assert_eq!($name::from_percent(50) * 100u32, 50u32); + assert_eq!($name::from_percent(50) * 100u64, 50u64); + assert_eq!($name::from_percent(50) * 100u128, 50u128); + } + + #[test] + fn per_thing_saturating_op_works() { + assert_eq!( + $name::from_percent(50).saturating_add($name::from_percent(40)), + $name::from_percent(90) + ); + assert_eq!( + $name::from_percent(50).saturating_add($name::from_percent(50)), + $name::from_percent(100) + ); + assert_eq!( + $name::from_percent(60).saturating_add($name::from_percent(50)), + $name::from_percent(100) + ); + + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(50)), + $name::from_percent(10) + ); + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(60)), + $name::from_percent(0) + ); + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(70)), + $name::from_percent(0) + ); + + assert_eq!( + $name::from_percent(50).saturating_mul($name::from_percent(50)), + $name::from_percent(25) + ); + assert_eq!( + $name::from_percent(20).saturating_mul($name::from_percent(20)), + $name::from_percent(4) + ); + assert_eq!( + $name::from_percent(10).saturating_mul($name::from_percent(10)), + $name::from_percent(1) + ); + } + + #[test] + fn per_thing_square_works() { + assert_eq!($name::from_percent(100).square(), $name::from_percent(100)); + assert_eq!($name::from_percent(50).square(), $name::from_percent(25)); + assert_eq!($name::from_percent(10).square(), $name::from_percent(1)); + assert_eq!( + $name::from_percent(2).square(), + $name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type) + ); + } + + #[test] + fn per_things_div_works() { + // normal + assert_eq!($name::from_percent(10) / $name::from_percent(20), + $name::from_percent(50) + ); + assert_eq!($name::from_percent(10) / $name::from_percent(10), + $name::from_percent(100) + ); + assert_eq!($name::from_percent(10) / $name::from_percent(0), + $name::from_percent(100) + ); + + // will not overflow + assert_eq!($name::from_percent(10) / $name::from_percent(5), + $name::from_percent(100) + ); + assert_eq!($name::from_percent(100) / $name::from_percent(50), + $name::from_percent(100) + ); + } + } + }; +} + +implement_per_thing!( + Percent, + test_per_cent, + [u32, u64, u128], + 100u8, + u8, + u16, + "_Percent_", +); +implement_per_thing!( + Permill, + test_permill, + [u32, u64, u128], + 1_000_000u32, + u32, + u64, + "_Parts per Million_", +); +implement_per_thing!( + Perbill, + test_perbill, + [u32, u64, u128], + 1_000_000_000u32, + u32, + u64, + "_Parts per Billion_", +); +implement_per_thing!( + Perquintill, + test_perquintill, + [u64, u128], + 1_000_000_000_000_000_000u64, + u64, + u128, + "_Parts per Quintillion_", +); diff --git a/core/sr-arithmetic/src/rational128.rs b/core/sr-arithmetic/src/rational128.rs new file mode 100644 index 0000000000000000000000000000000000000000..3247321199d6142e05fd8a11befcaad3e3e16186 --- /dev/null +++ b/core/sr-arithmetic/src/rational128.rs @@ -0,0 +1,384 @@ +// 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 . + +use rstd::{cmp::Ordering, prelude::*}; +use crate::helpers_128bit; +use num_traits::Zero; +use substrate_debug_derive::RuntimeDebug; + +/// A wrapper for any rational number with a 128 bit numerator and denominator. +#[derive(Clone, Copy, Default, Eq, RuntimeDebug)] +pub struct Rational128(u128, u128); + +impl Rational128 { + /// Nothing. + pub fn zero() -> Self { + Self(0, 1) + } + + /// If it is zero or not + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Build from a raw `n/d`. + pub fn from(n: u128, d: u128) -> Self { + Self(n, d.max(1)) + } + + /// Build from a raw `n/d`. This could lead to / 0 if not properly handled. + pub fn from_unchecked(n: u128, d: u128) -> Self { + Self(n, d) + } + + /// Return the numerator. + pub fn n(&self) -> u128 { + self.0 + } + + /// Return the denominator. + pub fn d(&self) -> u128 { + self.1 + } + + /// Convert `self` to a similar rational number where denominator is the given `den`. + // + /// This only returns if the result is accurate. `Err` is returned if the result cannot be + /// accurately calculated. + pub fn to_den(self, den: u128) -> Result { + if den == self.1 { + Ok(self) + } else { + helpers_128bit::multiply_by_rational(self.0, den, self.1).map(|n| Self(n, den)) + } + } + + /// Get the least common divisor of `self` and `other`. + /// + /// This only returns if the result is accurate. `Err` is returned if the result cannot be + /// accurately calculated. + pub fn lcm(&self, other: &Self) -> Result { + // this should be tested better: two large numbers that are almost the same. + if self.1 == other.1 { return Ok(self.1) } + let g = helpers_128bit::gcd(self.1, other.1); + helpers_128bit::multiply_by_rational(self.1 , other.1, g) + } + + /// A saturating add that assumes `self` and `other` have the same denominator. + pub fn lazy_saturating_add(self, other: Self) -> Self { + if other.is_zero() { + self + } else { + Self(self.0.saturating_add(other.0) ,self.1) + } + } + + /// A saturating subtraction that assumes `self` and `other` have the same denominator. + pub fn lazy_saturating_sub(self, other: Self) -> Self { + if other.is_zero() { + self + } else { + Self(self.0.saturating_sub(other.0) ,self.1) + } + } + + /// Addition. Simply tries to unify the denominators and add the numerators. + /// + /// Overflow might happen during any of the steps. Error is returned in such cases. + pub fn checked_add(self, other: Self) -> Result { + let lcm = self.lcm(&other).map_err(|_| "failed to scale to denominator")?; + let self_scaled = self.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + let other_scaled = other.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + let n = self_scaled.0.checked_add(other_scaled.0) + .ok_or("overflow while adding numerators")?; + Ok(Self(n, self_scaled.1)) + } + + /// Subtraction. Simply tries to unify the denominators and subtract the numerators. + /// + /// Overflow might happen during any of the steps. None is returned in such cases. + pub fn checked_sub(self, other: Self) -> Result { + let lcm = self.lcm(&other).map_err(|_| "failed to scale to denominator")?; + let self_scaled = self.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + let other_scaled = other.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + + let n = self_scaled.0.checked_sub(other_scaled.0) + .ok_or("overflow while subtracting numerators")?; + Ok(Self(n, self_scaled.1)) + } +} + +impl PartialOrd for Rational128 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Rational128 { + fn cmp(&self, other: &Self) -> Ordering { + // handle some edge cases. + if self.1 == other.1 { + self.0.cmp(&other.0) + } else if self.1.is_zero() { + Ordering::Greater + } else if other.1.is_zero() { + Ordering::Less + } else { + // Don't even compute gcd. + let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1); + let other_n = helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1); + self_n.cmp(&other_n) + } + } +} + +impl PartialEq for Rational128 { + fn eq(&self, other: &Self) -> bool { + // handle some edge cases. + if self.1 == other.1 { + self.0.eq(&other.0) + } else { + let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1); + let other_n = helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1); + self_n.eq(&other_n) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::helpers_128bit::*; + + const MAX128: u128 = u128::max_value(); + const MAX64: u128 = u64::max_value() as u128; + const MAX64_2: u128 = 2 * u64::max_value() as u128; + + fn r(p: u128, q: u128) -> Rational128 { + Rational128(p, q) + } + + fn mul_div(a: u128, b: u128, c: u128) -> u128 { + use primitive_types::U256; + if a.is_zero() { return Zero::zero(); } + let c = c.max(1); + + // e for extended + let ae: U256 = a.into(); + let be: U256 = b.into(); + let ce: U256 = c.into(); + + let r = ae * be / ce; + if r > u128::max_value().into() { + a + } else { + r.as_u128() + } + } + + #[test] + fn truth_value_function_works() { + assert_eq!( + mul_div(2u128.pow(100), 8, 4), + 2u128.pow(101) + ); + assert_eq!( + mul_div(2u128.pow(100), 4, 8), + 2u128.pow(99) + ); + + // and it returns a if result cannot fit + assert_eq!(mul_div(MAX128 - 10, 2, 1), MAX128 - 10); + } + + #[test] + fn to_denom_works() { + // simple up and down + assert_eq!(r(1, 5).to_den(10), Ok(r(2, 10))); + assert_eq!(r(4, 10).to_den(5), Ok(r(2, 5))); + + // up and down with large numbers + assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Ok(r(10, 10))); + assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Ok(r(5, 10))); + + // large to perbill. This is very well needed for phragmen. + assert_eq!( + r(MAX128 / 2, MAX128).to_den(1000_000_000), + Ok(r(500_000_000, 1000_000_000)) + ); + + // large to large + assert_eq!(r(MAX128 / 2, MAX128).to_den(MAX128/2), Ok(r(MAX128/4, MAX128/2))); + } + + #[test] + fn gdc_works() { + assert_eq!(gcd(10, 5), 5); + assert_eq!(gcd(7, 22), 1); + } + + #[test] + fn lcm_works() { + // simple stuff + assert_eq!(r(3, 10).lcm(&r(4, 15)).unwrap(), 30); + assert_eq!(r(5, 30).lcm(&r(1, 7)).unwrap(), 210); + assert_eq!(r(5, 30).lcm(&r(1, 10)).unwrap(), 30); + + // large numbers + assert_eq!( + r(1_000_000_000, MAX128).lcm(&r(7_000_000_000, MAX128-1)), + Err("result cannot fit in u128"), + ); + assert_eq!( + r(1_000_000_000, MAX64).lcm(&r(7_000_000_000, MAX64-1)), + Ok(340282366920938463408034375210639556610), + ); + assert!(340282366920938463408034375210639556610 < MAX128); + assert!(340282366920938463408034375210639556610 == MAX64 * (MAX64 - 1)); + } + + #[test] + fn add_works() { + // works + assert_eq!(r(3, 10).checked_add(r(1, 10)).unwrap(), r(2, 5)); + assert_eq!(r(3, 10).checked_add(r(3, 7)).unwrap(), r(51, 70)); + + // errors + assert_eq!( + r(1, MAX128).checked_add(r(1, MAX128-1)), + Err("failed to scale to denominator"), + ); + assert_eq!( + r(7, MAX128).checked_add(r(MAX128, MAX128)), + Err("overflow while adding numerators"), + ); + assert_eq!( + r(MAX128, MAX128).checked_add(r(MAX128, MAX128)), + Err("overflow while adding numerators"), + ); + } + + #[test] + fn sub_works() { + // works + assert_eq!(r(3, 10).checked_sub(r(1, 10)).unwrap(), r(1, 5)); + assert_eq!(r(6, 10).checked_sub(r(3, 7)).unwrap(), r(12, 70)); + + // errors + assert_eq!( + r(2, MAX128).checked_sub(r(1, MAX128-1)), + Err("failed to scale to denominator"), + ); + assert_eq!( + r(7, MAX128).checked_sub(r(MAX128, MAX128)), + Err("overflow while subtracting numerators"), + ); + assert_eq!( + r(1, 10).checked_sub(r(2,10)), + Err("overflow while subtracting numerators"), + ); + } + + #[test] + fn ordering_and_eq_works() { + assert!(r(1, 2) > r(1, 3)); + assert!(r(1, 2) > r(2, 6)); + + assert!(r(1, 2) < r(6, 6)); + assert!(r(2, 1) > r(2, 6)); + + assert!(r(5, 10) == r(1, 2)); + assert!(r(1, 2) == r(1, 2)); + + assert!(r(1, 1490000000000200000) > r(1, 1490000000000200001)); + } + + #[test] + fn multiply_by_rational_works() { + assert_eq!(multiply_by_rational(7, 2, 3).unwrap(), 7 * 2 / 3); + assert_eq!(multiply_by_rational(7, 20, 30).unwrap(), 7 * 2 / 3); + assert_eq!(multiply_by_rational(20, 7, 30).unwrap(), 7 * 2 / 3); + + assert_eq!( + // MAX128 % 3 == 0 + multiply_by_rational(MAX128, 2, 3).unwrap(), + MAX128 / 3 * 2, + ); + assert_eq!( + // MAX128 % 7 == 3 + multiply_by_rational(MAX128, 5, 7).unwrap(), + (MAX128 / 7 * 5) + (3 * 5 / 7), + ); + assert_eq!( + // MAX128 % 7 == 3 + multiply_by_rational(MAX128, 11 , 13).unwrap(), + (MAX128 / 13 * 11) + (8 * 11 / 13), + ); + assert_eq!( + // MAX128 % 1000 == 455 + multiply_by_rational(MAX128, 555, 1000).unwrap(), + (MAX128 / 1000 * 555) + (455 * 555 / 1000), + ); + + assert_eq!( + multiply_by_rational(2 * MAX64 - 1, MAX64, MAX64).unwrap(), + 2 * MAX64 - 1, + ); + assert_eq!( + multiply_by_rational(2 * MAX64 - 1, MAX64 - 1, MAX64).unwrap(), + 2 * MAX64 - 3, + ); + + assert_eq!( + multiply_by_rational(MAX64 + 100, MAX64_2, MAX64_2 / 2).unwrap(), + (MAX64 + 100) * 2, + ); + assert_eq!( + multiply_by_rational(MAX64 + 100, MAX64_2 / 100, MAX64_2 / 200).unwrap(), + (MAX64 + 100) * 2, + ); + + assert_eq!( + multiply_by_rational(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)).unwrap(), + 73786976294838206461, + ); + assert_eq!( + multiply_by_rational(1_000_000_000, MAX128 / 8, MAX128 / 2).unwrap(), + 250000000, + ); + } + + #[test] + fn multiply_by_rational_a_b_are_interchangeable() { + assert_eq!( + multiply_by_rational(10, MAX128, MAX128 / 2), + Ok(20), + ); + assert_eq!( + multiply_by_rational(MAX128, 10, MAX128 / 2), + Ok(20), + ); + } + + #[test] + #[ignore] + fn multiply_by_rational_fuzzed_equation() { + assert_eq!( + multiply_by_rational(154742576605164960401588224, 9223376310179529214, 549756068598), + Ok(2596149632101417846585204209223679) + ); + } +} diff --git a/core/sr-arithmetic/src/traits.rs b/core/sr-arithmetic/src/traits.rs new file mode 100644 index 0000000000000000000000000000000000000000..d02425066ff9d7f8e34efe6e73b94262ba197b64 --- /dev/null +++ b/core/sr-arithmetic/src/traits.rs @@ -0,0 +1,143 @@ +// Copyright 2017-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 . + +//! Primitives for the runtime modules. + +use rstd::{self, convert::{TryFrom, TryInto}}; +use codec::HasCompact; +pub use integer_sqrt::IntegerSquareRoot; +pub use num_traits::{ + Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, + CheckedShl, CheckedShr +}; +use rstd::ops::{ + Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, + 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: + 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 +{} +impl + 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 {} + +/// Just like `From` except that if the source value is too big to fit into the destination type +/// then it'll saturate the destination. +pub trait UniqueSaturatedFrom: Sized { + /// Convert from a value of `T` into an equivalent instance of `Self`. + fn unique_saturated_from(t: T) -> Self; +} + +/// Just like `Into` except that if the source value is too big to fit into the destination type +/// then it'll saturate the destination. +pub trait UniqueSaturatedInto: Sized { + /// Consume self to return an equivalent value of `T`. + fn unique_saturated_into(self) -> T; +} + +impl + Bounded + Sized> UniqueSaturatedFrom for S { + fn unique_saturated_from(t: T) -> Self { + S::try_from(t).unwrap_or_else(|_| Bounded::max_value()) + } +} + +impl + Sized> UniqueSaturatedInto for S { + fn unique_saturated_into(self) -> T { + self.try_into().unwrap_or_else(|_| Bounded::max_value()) + } +} + +/// Simple trait to use checked mul and max value to give a saturated mul operation over +/// supported types. +pub trait Saturating { + /// Saturated addition - if the product can't fit in the type then just use max-value. + fn saturating_add(self, o: Self) -> Self; + + /// Saturated subtraction - if the product can't fit in the type then just use max-value. + fn saturating_sub(self, o: Self) -> Self; + + /// Saturated multiply - if the product can't fit in the type then just use max-value. + fn saturating_mul(self, o: Self) -> Self; +} + +impl Saturating for T { + fn saturating_add(self, o: Self) -> Self { + ::saturating_add(self, o) + } + fn saturating_sub(self, o: Self) -> Self { + ::saturating_sub(self, o) + } + fn saturating_mul(self, o: Self) -> Self { + self.checked_mul(&o).unwrap_or_else(Bounded::max_value) + } +} + +/// Convenience type to work around the highly unergonomic syntax needed +/// to invoke the functions of overloaded generic traits, in this case +/// `SaturatedFrom` and `SaturatedInto`. +pub trait SaturatedConversion { + /// Convert from a value of `T` into an equivalent instance of `Self`. + /// + /// This just uses `UniqueSaturatedFrom` internally but with this + /// variant you can provide the destination type using turbofish syntax + /// in case Rust happens not to assume the correct type. + fn saturated_from(t: T) -> Self where Self: UniqueSaturatedFrom { + >::unique_saturated_from(t) + } + + /// Consume self to return an equivalent value of `T`. + /// + /// This just uses `UniqueSaturatedInto` internally but with this + /// variant you can provide the destination type using turbofish syntax + /// in case Rust happens not to assume the correct type. + fn saturated_into(self) -> T where Self: UniqueSaturatedInto { + >::unique_saturated_into(self) + } +} +impl SaturatedConversion for T {} diff --git a/core/sr-io/Cargo.toml b/core/sr-io/Cargo.toml index 65b080b86df82faad299eccfbacdc16e16b0cfb7..4d140d289dafe3d1033243f59f7fcc09bd8e5fe2 100644 --- a/core/sr-io/Cargo.toml +++ b/core/sr-io/Cargo.toml @@ -9,15 +9,16 @@ edition = "2018" rustc_version = "0.2.3" [dependencies] -rstd = { package = "sr-std", path = "../sr-std", default-features = false } -primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } hash-db = { version = "0.15.2", default-features = false } +primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } +rstd = { package = "sr-std", path = "../sr-std", default-features = false } libsecp256k1 = { version = "0.3.0", optional = true } tiny-keccak = { version = "1.5.0", optional = true } -environmental = { version = "1.0.2", optional = true } substrate-state-machine = { path = "../state-machine", optional = true } trie = { package = "substrate-trie", path = "../trie", optional = true } +externalities = { package = "substrate-externalities", path = "../externalities", optional = true } +log = { version = "0.4.8", optional = true } [features] default = ["std"] @@ -27,10 +28,11 @@ std = [ "rstd/std", "hash-db/std", "trie", - "environmental", "substrate-state-machine", "libsecp256k1", "tiny-keccak", + "externalities", + "log", ] nightly = [] strict = [] diff --git a/core/sr-io/src/lib.rs b/core/sr-io/src/lib.rs index 4b00a842311f5829e1471031d9e2048419a7ea11..fe5e50b3eda3e8defce026001755e199dcea1b53 100644 --- a/core/sr-io/src/lib.rs +++ b/core/sr-io/src/lib.rs @@ -33,6 +33,7 @@ use primitives::{ offchain::{ Timestamp, HttpRequestId, HttpRequestStatus, HttpError, StorageKind, OpaqueNetworkState, }, + LogLevel, }; /// Error verifying ECDSA signature @@ -158,6 +159,20 @@ export_api! { fn print_utf8(utf8: &[u8]); /// Print any `u8` slice as hex. fn print_hex(data: &[u8]); + + /// Request to print a log message (stderr) on the host. + /// + /// Note that this will be only displayed if the host + /// is enabed to display log messages with given + /// level and target. + /// + /// Instead of using directly, prefer setting up `RuntimeLogger` + /// and using `log` macros. + fn log( + level: LogLevel, + target: &[u8], + message: &[u8] + ); } } @@ -205,15 +220,20 @@ export_api! { /// Verify and recover a SECP256k1 ECDSA signature. /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. - /// - returns `Err` if the signature is bad, otherwise the 64-byte pubkey (doesn't include the 0x04 prefix). + /// - returns `Err` if the signature is bad, otherwise the 64-byte raw pubkey (doesn't include the 0x04 prefix). fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64], EcdsaVerifyError>; + + /// Verify and recover a SECP256k1 ECDSA signature. + /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. + /// - returns `Err` if the signature is bad, otherwise the 33-byte compressed pubkey. + fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError>; } } export_api! { pub(crate) trait HashingApi { /// Conduct a 256-bit Keccak hash. - fn keccak_256(data: &[u8]) -> [u8; 32] ; + fn keccak_256(data: &[u8]) -> [u8; 32]; /// Conduct a 128-bit Blake2 hash. fn blake2_128(data: &[u8]) -> [u8; 16]; @@ -368,13 +388,10 @@ mod imp { } #[cfg(feature = "std")] -pub use self::imp::{ - StorageOverlay, ChildrenStorageOverlay, with_storage, - with_externalities -}; +pub use self::imp::{StorageOverlay, ChildrenStorageOverlay, with_storage}; #[cfg(not(feature = "std"))] pub use self::imp::ext::*; /// Type alias for Externalities implementation used in tests. #[cfg(feature = "std")] -pub type TestExternalities = self::imp::TestExternalities; +pub type TestExternalities = self::imp::TestExternalities; diff --git a/core/sr-io/with_std.rs b/core/sr-io/with_std.rs index 919f4a913acc90d079cdfd2dd775bbf560f03a4c..fdd32124c129c8bd172d410c1a2ce7ad4876ad68 100644 --- a/core/sr-io/with_std.rs +++ b/core/sr-io/with_std.rs @@ -16,19 +16,18 @@ use primitives::{ blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher, sr25519, Pair, H256, - traits::Externalities, child_storage_key::ChildStorageKey, hexdisplay::HexDisplay, offchain, - Hasher, + traits::KeystoreExt, storage::ChildStorageKey, hexdisplay::HexDisplay, Hasher, + offchain::{self, OffchainExt}, }; // Switch to this after PoC-3 // pub use primitives::BlakeHasher; pub use substrate_state_machine::{BasicExternalities, TestExternalities}; -use environmental::environmental; use trie::{TrieConfiguration, trie_types::Layout}; use std::{collections::HashMap, convert::TryFrom}; -environmental!(ext: trait Externalities); +use externalities::{with_externalities, set_and_run_with_externalities, ExternalitiesExt}; /// Additional bounds for `Hasher` trait for with_std. pub trait HasherBounds {} @@ -48,12 +47,12 @@ fn child_storage_key_or_panic(storage_key: &[u8]) -> ChildStorageKey { impl StorageApi for () { fn storage(key: &[u8]) -> Option> { - ext::with(|ext| ext.storage(key).map(|s| s.to_vec())) + with_externalities(|ext| ext.storage(key).map(|s| s.to_vec())) .expect("storage cannot be called outside of an Externalities-provided environment.") } fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { - ext::with(|ext| ext.storage(key).map(|value| { + with_externalities(|ext| ext.storage(key).map(|value| { let data = &value[value_offset.min(value.len())..]; let written = std::cmp::min(data.len(), value_out.len()); value_out[..written].copy_from_slice(&data[..written]); @@ -62,7 +61,7 @@ impl StorageApi for () { } fn child_storage(storage_key: &[u8], key: &[u8]) -> Option> { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.child_storage(storage_key, key).map(|s| s.to_vec()) }) @@ -70,7 +69,7 @@ impl StorageApi for () { } fn set_storage(key: &[u8], value: &[u8]) { - ext::with(|ext| + with_externalities(|ext| ext.set_storage(key.to_vec(), value.to_vec()) ); } @@ -81,7 +80,7 @@ impl StorageApi for () { value_out: &mut [u8], value_offset: usize, ) -> Option { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.child_storage(storage_key, key) .map(|value| { @@ -95,73 +94,71 @@ impl StorageApi for () { } fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.set_child_storage(storage_key, key.to_vec(), value.to_vec()) }); } fn clear_storage(key: &[u8]) { - ext::with(|ext| + with_externalities(|ext| ext.clear_storage(key) ); } fn clear_child_storage(storage_key: &[u8], key: &[u8]) { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.clear_child_storage(storage_key, key) }); } fn kill_child_storage(storage_key: &[u8]) { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.kill_child_storage(storage_key) }); } fn exists_storage(key: &[u8]) -> bool { - ext::with(|ext| + with_externalities(|ext| ext.exists_storage(key) ).unwrap_or(false) } fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.exists_child_storage(storage_key, key) }).unwrap_or(false) } fn clear_prefix(prefix: &[u8]) { - ext::with(|ext| - ext.clear_prefix(prefix) - ); + with_externalities(|ext| ext.clear_prefix(prefix)); } fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]) { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.clear_child_prefix(storage_key, prefix) }); } fn storage_root() -> [u8; 32] { - ext::with(|ext| + with_externalities(|ext| ext.storage_root() ).unwrap_or(H256::zero()).into() } fn child_storage_root(storage_key: &[u8]) -> Vec { - ext::with(|ext| { + with_externalities(|ext| { let storage_key = child_storage_key_or_panic(storage_key); ext.child_storage_root(storage_key) }).expect("child_storage_root cannot be called outside of an Externalities-provided environment.") } fn storage_changes_root(parent_hash: [u8; 32]) -> Option<[u8; 32]> { - ext::with(|ext| + with_externalities(|ext| ext.storage_changes_root(parent_hash.into()).map(|h| h.map(|h| h.into())) ).unwrap_or(Ok(None)).expect("Invalid parent hash passed to storage_changes_root") } @@ -177,7 +174,7 @@ impl StorageApi for () { impl OtherApi for () { fn chain_id() -> u64 { - ext::with(|ext| + with_externalities(|ext| ext.chain_id() ).unwrap_or(0) } @@ -195,12 +192,28 @@ impl OtherApi for () { fn print_hex(data: &[u8]) { println!("{}", HexDisplay::from(&data)); } + + fn log( + level: LogLevel, + target: &[u8], + message: &[u8], + ) { + let target = std::str::from_utf8(target).unwrap_or("invalid utf8"); + let msg = std::str::from_utf8(message).unwrap_or("invalid utf8"); + + log::log!( + target: target, + log::Level::from(level), + "{}", + msg, + ) + } } impl CryptoApi for () { fn ed25519_public_keys(id: KeyTypeId) -> Vec { - ext::with(|ext| { - ext.keystore() + with_externalities(|ext| { + ext.extension::() .expect("No `keystore` associated for the current context!") .read() .ed25519_public_keys(id) @@ -208,8 +221,8 @@ impl CryptoApi for () { } fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public { - ext::with(|ext| { - ext.keystore() + with_externalities(|ext| { + ext.extension::() .expect("No `keystore` associated for the current context!") .write() .ed25519_generate_new(id, seed) @@ -224,8 +237,8 @@ impl CryptoApi for () { ) -> Option { let pub_key = ed25519::Public::try_from(pubkey.as_ref()).ok()?; - ext::with(|ext| { - ext.keystore() + with_externalities(|ext| { + ext.extension::() .expect("No `keystore` associated for the current context!") .read() .ed25519_key_pair(id, &pub_key) @@ -238,8 +251,8 @@ impl CryptoApi for () { } fn sr25519_public_keys(id: KeyTypeId) -> Vec { - ext::with(|ext| { - ext.keystore() + with_externalities(|ext| { + ext.extension::() .expect("No `keystore` associated for the current context!") .read() .sr25519_public_keys(id) @@ -247,8 +260,8 @@ impl CryptoApi for () { } fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public { - ext::with(|ext| { - ext.keystore() + with_externalities(|ext| { + ext.extension::() .expect("No `keystore` associated for the current context!") .write() .sr25519_generate_new(id, seed) @@ -263,8 +276,8 @@ impl CryptoApi for () { ) -> Option { let pub_key = sr25519::Public::try_from(pubkey.as_ref()).ok()?; - ext::with(|ext| { - ext.keystore() + with_externalities(|ext| { + ext.extension::() .expect("No `keystore` associated for the current context!") .read() .sr25519_key_pair(id, &pub_key) @@ -287,6 +300,16 @@ impl CryptoApi for () { res.copy_from_slice(&pubkey.serialize()[1..65]); Ok(res) } + + fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError> { + let rs = secp256k1::Signature::parse_slice(&sig[0..64]) + .map_err(|_| EcdsaVerifyError::BadRS)?; + let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8) + .map_err(|_| EcdsaVerifyError::BadV)?; + let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v) + .map_err(|_| EcdsaVerifyError::BadSignature)?; + Ok(pubkey.serialize_compressed()) + } } impl HashingApi for () { @@ -316,9 +339,9 @@ impl HashingApi for () { } fn with_offchain(f: impl FnOnce(&mut dyn offchain::Externalities) -> R, msg: &'static str) -> R { - ext::with(|ext| ext - .offchain() - .map(|ext| f(ext)) + with_externalities(|ext| ext + .extension::() + .map(|ext| f(&mut **ext)) .expect(msg) ).expect("offchain-worker functions cannot be called outside of an Externalities-provided environment.") } @@ -443,13 +466,6 @@ impl OffchainApi for () { impl Api for () {} -/// Execute the given closure with global function available whose functionality routes into the -/// externalities `ext`. Forwards the value that the closure returns. -// NOTE: need a concrete hasher here due to limitations of the `environmental!` macro, otherwise a type param would have been fine I think. -pub fn with_externalities R>(ext: &mut dyn Externalities, f: F) -> R { - ext::using(ext, f) -} - /// A set of key value pairs for storage. pub type StorageOverlay = HashMap, Vec>; @@ -467,7 +483,7 @@ pub fn with_storage R>( rstd::mem::swap(&mut alt_storage, storage); let mut ext = BasicExternalities::new(alt_storage.0, alt_storage.1); - let r = ext::using(&mut ext, f); + let r = set_and_run_with_externalities(&mut ext, f); *storage = ext.into_storages(); @@ -482,7 +498,7 @@ mod std_tests { #[test] fn storage_works() { let mut t = BasicExternalities::default(); - assert!(with_externalities(&mut t, || { + assert!(set_and_run_with_externalities(&mut t, || { assert_eq!(storage(b"hello"), None); set_storage(b"hello", b"world"); assert_eq!(storage(b"hello"), Some(b"world".to_vec())); @@ -493,7 +509,7 @@ mod std_tests { t = BasicExternalities::new(map![b"foo".to_vec() => b"bar".to_vec()], map![]); - assert!(!with_externalities(&mut t, || { + assert!(!set_and_run_with_externalities(&mut t, || { assert_eq!(storage(b"hello"), None); assert_eq!(storage(b"foo"), Some(b"bar".to_vec())); false @@ -506,7 +522,7 @@ mod std_tests { b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec() ], map![]); - with_externalities(&mut t, || { + set_and_run_with_externalities(&mut t, || { let mut v = [0u8; 4]; assert!(read_storage(b":test", &mut v[..], 0).unwrap() >= 4); assert_eq!(v, [11u8, 0, 0, 0]); @@ -525,7 +541,7 @@ mod std_tests { b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec() ], map![]); - with_externalities(&mut t, || { + set_and_run_with_externalities(&mut t, || { clear_prefix(b":abc"); assert!(storage(b":a").is_some()); diff --git a/core/sr-io/without_std.rs b/core/sr-io/without_std.rs index 90ec5a9ee4501ccb3fa5c1e77a0b566ca858b4cc..c3f7d62031b931dba928f240015f1a9475e8fdc3 100644 --- a/core/sr-io/without_std.rs +++ b/core/sr-io/without_std.rs @@ -160,6 +160,14 @@ pub mod ext { fn ext_print_hex(data: *const u8, len: u32); /// Print a number fn ext_print_num(value: u64); + /// Print a log line if logging for given level and target is enabled. + fn ext_log( + level: u32, + target_data: *const u8, + target_len: u32, + message_data: *const u8, + message_len: u32, + ); /// Set value for key in storage. fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); @@ -386,12 +394,23 @@ pub mod ext { ) -> u32; /// Note: ext_secp256k1_ecdsa_recover returns 0 if the signature is correct, nonzero otherwise. + /// + /// pubkey_data must point to 64 bytes. fn ext_secp256k1_ecdsa_recover( msg_data: *const u8, sig_data: *const u8, pubkey_data: *mut u8, ) -> u32; + /// Note: ext_secp256k1_ecdsa_recover_compressed returns 0 if the signature is correct, nonzero otherwise. + /// + /// pubkey_data must point to 33 bytes. + fn ext_secp256k1_ecdsa_recover_compressed( + msg_data: *const u8, + sig_data: *const u8, + pubkey_data: *mut u8, + ) -> u32; + //================================ // Offchain-worker Context //================================ @@ -780,6 +799,22 @@ impl OtherApi for () { ext_print_hex.get()(data.as_ptr(), data.len() as u32); } } + + fn log( + level: LogLevel, + target: &[u8], + message: &[u8] + ) { + unsafe { + ext_log.get()( + level as u32, + target.as_ptr(), + target.len() as u32, + message.as_ptr(), + message.len() as u32, + ) + } + } } impl HashingApi for () { @@ -947,6 +982,19 @@ impl CryptoApi for () { _ => unreachable!("`ext_secp256k1_ecdsa_recover` only returns 0, 1, 2 or 3; qed"), } } + + fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError> { + let mut pubkey = [0u8; 33]; + match unsafe { + ext_secp256k1_ecdsa_recover_compressed.get()(msg.as_ptr(), sig.as_ptr(), pubkey.as_mut_ptr()) + } { + 0 => Ok(pubkey), + 1 => Err(EcdsaVerifyError::BadRS), + 2 => Err(EcdsaVerifyError::BadV), + 3 => Err(EcdsaVerifyError::BadSignature), + _ => unreachable!("`ext_secp256k1_ecdsa_recover_compressed` only returns 0, 1, 2 or 3; qed"), + } + } } impl OffchainApi for () { diff --git a/core/sr-primitives/Cargo.toml b/core/sr-primitives/Cargo.toml index fc8b744d370f4cbc562aaf5fc33e0de2f9ded535..1b1434b91930d19186504fe6f563fa873a71dc1c 100644 --- a/core/sr-primitives/Cargo.toml +++ b/core/sr-primitives/Cargo.toml @@ -5,35 +5,34 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -num-traits = { version = "0.2.8", default-features = false } -integer-sqrt = "0.1.2" serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } app-crypto = { package = "substrate-application-crypto", path = "../application-crypto", default-features = false } +arithmetic = { package = "sr-arithmetic", path = "../sr-arithmetic", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../sr-io", default-features = false } log = { version = "0.4.8", optional = true } paste = "0.1.6" rand = { version = "0.7.2", optional = true } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [dev-dependencies] -serde_json = "1.0.40" -primitive-types = "0.5.1" +serde_json = "1.0.41" rand = "0.7.2" substrate-offchain = { path = "../offchain" } [features] +bench = [] default = ["std"] std = [ - "num-traits/std", - "serde", - "log", - "rstd/std", - "runtime_io/std", + "app-crypto/std", + "arithmetic/std", "codec/std", + "log", "primitives/std", - "app-crypto/std", "rand", + "rstd/std", + "runtime_io/std", + "serde", ] diff --git a/core/sr-primitives/src/curve.rs b/core/sr-primitives/src/curve.rs index dc6316bd470c73d176d0b73c21fe87bd6110a23a..6f25af3e10b6e3e96073cd07f19359b40a4a9a08 100644 --- a/core/sr-primitives/src/curve.rs +++ b/core/sr-primitives/src/curve.rs @@ -20,8 +20,7 @@ use crate::{Perbill, traits::{SimpleArithmetic, SaturatedConversion}}; use core::ops::Sub; /// Piecewise Linear function in [0, 1] -> [0, 1]. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, primitives::RuntimeDebug)] pub struct PiecewiseLinear<'a> { /// Array of points. Must be in order from the lowest abscissas to the highest. pub points: &'a [(Perbill, Perbill)] diff --git a/core/sr-primitives/src/generic/block.rs b/core/sr-primitives/src/generic/block.rs index 736ad0cbbb6368199ce4fa046c253bdddf66f0a3..3383e257605aa7498f35b39384c59bf86a3c50a1 100644 --- a/core/sr-primitives/src/generic/block.rs +++ b/core/sr-primitives/src/generic/block.rs @@ -23,13 +23,14 @@ use std::fmt; use serde::{Deserialize, Serialize}; use rstd::prelude::*; +use primitives::RuntimeDebug; use crate::codec::{Codec, Encode, Decode}; use crate::traits::{self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize}; use crate::Justification; /// Something to identify a block. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))] pub enum BlockId { @@ -61,8 +62,8 @@ impl fmt::Display for BlockId { } /// Abstraction over a substrate block. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))] pub struct Block { @@ -93,11 +94,14 @@ where fn new(header: Self::Header, extrinsics: Vec) -> Self { Block { header, extrinsics } } + fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec { + (header, extrinsics).encode() + } } /// Abstraction over a substrate block and justification. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))] pub struct SignedBlock { diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index e8d41325c432427c52e3c08333321167749ceb00..510a7ac8c85c9c4ad1bb1d2ef3476721ad8c6e03 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -26,8 +26,7 @@ use crate::transaction_validity::TransactionValidity; /// Definition of something that the external world might want to say; its /// existence implies that it has been checked and is good, particularly with /// regards to the signature. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, primitives::RuntimeDebug)] pub struct CheckedExtrinsic { /// Who this purports to be from and the number of extrinsics have come before /// from the same signer, if anyone (note this is not a signature). @@ -37,8 +36,7 @@ pub struct CheckedExtrinsic { pub function: Call, } -impl traits::Applyable -for +impl traits::Applyable for CheckedExtrinsic where AccountId: Member + MaybeDisplay, diff --git a/core/sr-primitives/src/generic/digest.rs b/core/sr-primitives/src/generic/digest.rs index d2974444e231b48a270c6c3d715408fe7b213639..83f2c6f1745dedd5092b28c2f013fd3caa0fa9a9 100644 --- a/core/sr-primitives/src/generic/digest.rs +++ b/core/sr-primitives/src/generic/digest.rs @@ -23,10 +23,11 @@ use rstd::prelude::*; use crate::ConsensusEngineId; use crate::codec::{Decode, Encode, Input, Error}; +use primitives::RuntimeDebug; /// Generic header digest. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct Digest { /// A list of logs in the digest. pub logs: Vec>, @@ -72,8 +73,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)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] 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 @@ -123,8 +123,7 @@ impl<'a, Hash: Decode> serde::Deserialize<'a> for DigestItem { /// A 'referencing view' for digest item. Does not own its contents. Used by /// final runtime implementations for encoding/decoding its log items. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub enum DigestItemRef<'a, Hash: 'a> { /// Reference to `DigestItem::ChangesTrieRoot`. ChangesTrieRoot(&'a Hash), diff --git a/core/sr-primitives/src/generic/era.rs b/core/sr-primitives/src/generic/era.rs index 7308a8adc5cd5d97a43adfc10869008be7c8285b..305951b1ee3914e3ae2fc93839dd155090d12db3 100644 --- a/core/sr-primitives/src/generic/era.rs +++ b/core/sr-primitives/src/generic/era.rs @@ -28,8 +28,8 @@ pub type Period = u64; pub type Phase = u64; /// An era to describe the longevity of a transaction. -#[derive(PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(PartialEq, Eq, Clone, Copy, primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Era { /// The transaction is valid forever. The genesis hash must be present in the signed content. Immortal, diff --git a/core/sr-primitives/src/generic/header.rs b/core/sr-primitives/src/generic/header.rs index e9a8405fe21b0a45d62f9bcc341b14ee1712982b..75994749c572e3a5c144ce738608902dde91e2a0 100644 --- a/core/sr-primitives/src/generic/header.rs +++ b/core/sr-primitives/src/generic/header.rs @@ -18,20 +18,21 @@ #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use log::debug; use crate::codec::{Decode, Encode, Codec, Input, Output, HasCompact, EncodeAsRef, Error}; use crate::traits::{ - self, Member, SimpleArithmetic, SimpleBitOps, MaybeDisplay, Hash as HashT, MaybeSerializeDebug, - MaybeSerializeDebugButNotDeserialize + self, Member, SimpleArithmetic, SimpleBitOps, Hash as HashT, + MaybeSerializeDeserialize, MaybeSerialize, MaybeDisplay, }; use crate::generic::Digest; use primitives::U256; -use core::convert::TryFrom; +use rstd::{ + convert::TryFrom, + fmt::Debug, +}; /// Abstraction over a block header for a substrate chain. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(PartialEq, Eq, Clone, primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "std", serde(deny_unknown_fields))] pub struct Header + TryFrom, Hash: HashT> { @@ -103,11 +104,11 @@ impl codec::EncodeLike for Header where {} impl traits::Header for Header where - Number: Member + MaybeSerializeDebug + rstd::hash::Hash + MaybeDisplay + + Number: Member + MaybeSerializeDeserialize + Debug + rstd::hash::Hash + MaybeDisplay + SimpleArithmetic + Codec + Copy + Into + TryFrom, Hash: HashT, Hash::Output: Default + rstd::hash::Hash + Copy + Member + - MaybeSerializeDebugButNotDeserialize + MaybeDisplay + SimpleBitOps + Codec, + MaybeSerialize + Debug + MaybeDisplay + SimpleBitOps + Codec, { type Number = Number; type Hash = ::Output; @@ -127,15 +128,12 @@ impl traits::Header for Header where fn digest(&self) -> &Digest { &self.digest } - #[cfg(feature = "std")] fn digest_mut(&mut self) -> &mut Digest { - debug!(target: "header", "Retrieving mutable reference to digest"); + #[cfg(feature = "std")] + log::debug!(target: "header", "Retrieving mutable reference to digest"); &mut self.digest } - #[cfg(not(feature = "std"))] - fn digest_mut(&mut self) -> &mut Digest { &mut self.digest } - fn new( number: Self::Number, extrinsics_root: Self::Hash, diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index c5ee76e21c0114cbb63c409c7a33e3f68a1df97e..befa857dffd64d1ac13a90f7571e60ad8ca4050a 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -16,18 +16,17 @@ //! Generic implementation of an unchecked (pre-verification) extrinsic. -#[cfg(feature = "std")] -use std::fmt; - use rstd::prelude::*; +use rstd::fmt; use runtime_io::blake2_256; use codec::{Decode, Encode, EncodeLike, Input, Error}; use crate::{ - traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic}, + traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, IdentifyAccount}, generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction}, + weights::{GetDispatchInfo, DispatchInfo}, }; -const TRANSACTION_VERSION: u8 = 3; +const TRANSACTION_VERSION: u8 = 4; /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. @@ -100,7 +99,8 @@ for where Address: Member + MaybeDisplay, Call: Encode + Member, - Signature: Member + traits::Verify, + Signature: Member + traits::Verify, + ::Signer: IdentifyAccount, Extra: SignedExtension, AccountId: Member + MaybeDisplay, Lookup: traits::Lookup, @@ -264,7 +264,6 @@ impl s } } -#[cfg(feature = "std")] impl fmt::Debug for UncheckedExtrinsic where @@ -282,22 +281,42 @@ where } } +impl GetDispatchInfo + for UncheckedExtrinsic +where + Call: GetDispatchInfo, + Extra: SignedExtension, +{ + fn get_dispatch_info(&self) -> DispatchInfo { + self.function.get_dispatch_info() + } +} + #[cfg(test)] mod tests { use super::*; use runtime_io::blake2_256; use crate::codec::{Encode, Decode}; - use crate::traits::{SignedExtension, IdentityLookup}; + use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup}; use serde::{Serialize, Deserialize}; type TestContext = IdentityLookup; + #[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Encode, Decode)] + pub struct TestSigner(pub u64); + impl From for TestSigner { fn from(x: u64) -> Self { Self(x) } } + impl From for u64 { fn from(x: TestSigner) -> Self { x.0 } } + impl IdentifyAccount for TestSigner { + type AccountId = u64; + fn into_account(self) -> u64 { self.into() } + } + #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)] struct TestSig(u64, Vec); impl traits::Verify for TestSig { - type Signer = u64; - fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { - *signer == self.0 && msg.get() == &self.1[..] + type Signer = TestSigner; + fn verify>(&self, mut msg: L, signer: &u64) -> bool { + signer == &self.0 && msg.get() == &self.1[..] } } diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index 1e26a3d4234e83e86aa6bc189ed2e3d9fa6fb262..fce9f7def05b36df85d5f4470fcf81d3006688e5 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -19,6 +19,10 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +// to allow benchmarking +#![cfg_attr(feature = "bench", feature(test))] +#[cfg(feature = "bench")] extern crate test; + #[doc(hidden)] pub use codec; #[cfg(feature = "std")] @@ -38,7 +42,7 @@ pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; use rstd::prelude::*; use rstd::convert::TryFrom; -use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}}; +use primitives::{crypto, ed25519, sr25519, ecdsa, hash::{H256, H512}}; use codec::{Encode, Decode}; #[cfg(feature = "std")] @@ -47,7 +51,6 @@ pub mod testing; pub mod curve; pub mod generic; pub mod offchain; -pub mod sr_arithmetic; pub mod traits; pub mod transaction_validity; pub mod weights; @@ -56,16 +59,21 @@ pub mod weights; pub use generic::{DigestItem, Digest}; /// Re-export this since it's part of the API of this crate. -pub use primitives::crypto::{key_types, KeyTypeId, CryptoType}; -pub use app_crypto::RuntimeAppPublic; +pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType, AccountId32}}; +pub use app_crypto::{RuntimeAppPublic, BoundToRuntimeAppPublic}; + +/// Re-export `RuntimeDebug`, to avoid dependency clutter. +pub use primitives::RuntimeDebug; -/// Re-export arithmetic stuff. -pub use sr_arithmetic::{ +/// Re-export top-level arithmetic stuff. +pub use arithmetic::{ Perquintill, Perbill, Permill, Percent, Rational128, Fixed64 }; -/// Re-export 128 bit helpers from sr_arithmetic -pub use sr_arithmetic::helpers_128bit; +/// Re-export 128 bit helpers. +pub use arithmetic::helpers_128bit; +/// Re-export big_uint stuff. +pub use arithmetic::biguint; /// An abstraction over justification for a block's validity under a consensus algorithm. /// @@ -82,7 +90,7 @@ use traits::{Verify, Lazy}; #[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)] pub struct ModuleId(pub [u8; 8]); -impl traits::TypeId for ModuleId { +impl TypeId for ModuleId { const TYPE_ID: [u8; 4] = *b"modl"; } @@ -109,19 +117,20 @@ macro_rules! create_runtime_str { #[cfg(feature = "std")] pub use serde::{Serialize, Deserialize, de::DeserializeOwned}; +use crate::traits::IdentifyAccount; /// Complex storage builder stuff. #[cfg(feature = "std")] pub trait BuildStorage: Sized { /// Build the storage out of this builder. - fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { + fn build_storage(&self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { let mut storage = (Default::default(), Default::default()); self.assimilate_storage(&mut storage)?; Ok(storage) } /// Assimilate the storage for this module into pre-existing overlays. fn assimilate_storage( - self, + &self, storage: &mut (StorageOverlay, ChildrenStorageOverlay), ) -> Result<(), String>; } @@ -131,26 +140,24 @@ pub trait BuildStorage: Sized { pub trait BuildModuleGenesisStorage: Sized { /// Create the module genesis storage into the given `storage` and `child_storage`. fn build_module_genesis_storage( - self, + &self, storage: &mut (StorageOverlay, ChildrenStorageOverlay), ) -> Result<(), String>; } #[cfg(feature = "std")] impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) { - fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> { - Ok(self) - } fn assimilate_storage( - self, + &self, storage: &mut (StorageOverlay, ChildrenStorageOverlay), )-> Result<(), String> { - storage.0.extend(self.0); - for (k, other_map) in self.1.into_iter() { + storage.0.extend(self.0.iter().map(|(k, v)| (k.clone(), v.clone()))); + for (k, other_map) in self.1.iter() { + let k = k.clone(); if let Some(map) = storage.1.get_mut(&k) { - map.extend(other_map); + map.extend(other_map.iter().map(|(k, v)| (k.clone(), v.clone()))); } else { - storage.1.insert(k, other_map); + storage.1.insert(k, other_map.clone()); } } Ok(()) @@ -161,13 +168,14 @@ impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) { pub type ConsensusEngineId = [u8; 4]; /// Signature verify that can work with any known signature types.. -#[derive(Eq, PartialEq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug)] pub enum MultiSignature { /// An Ed25519 signature. Ed25519(ed25519::Signature), /// An Sr25519 signature. Sr25519(sr25519::Signature), + /// An ECDSA/SECP256k1 signature. + Ecdsa(ecdsa::Signature), } impl From for MultiSignature { @@ -182,6 +190,12 @@ impl From for MultiSignature { } } +impl From for MultiSignature { + fn from(x: ecdsa::Signature) -> Self { + MultiSignature::Ecdsa(x) + } +} + impl Default for MultiSignature { fn default() -> Self { MultiSignature::Ed25519(Default::default()) @@ -189,13 +203,15 @@ impl Default for MultiSignature { } /// Public key for any known crypto algorithm. -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum MultiSigner { /// An Ed25519 identity. Ed25519(ed25519::Public), /// An Sr25519 identity. Sr25519(sr25519::Public), + /// An SECP256k1/ECDSA identity (actually, the Blake2 hash of the pub key). + Ecdsa(ecdsa::Public), } impl Default for MultiSigner { @@ -217,6 +233,18 @@ impl AsRef<[u8]> for MultiSigner { match *self { MultiSigner::Ed25519(ref who) => who.as_ref(), MultiSigner::Sr25519(ref who) => who.as_ref(), + MultiSigner::Ecdsa(ref who) => who.as_ref(), + } + } +} + +impl traits::IdentifyAccount for MultiSigner { + type AccountId = AccountId32; + fn into_account(self) -> AccountId32 { + match self { + MultiSigner::Ed25519(who) => <[u8; 32]>::from(who).into(), + MultiSigner::Sr25519(who) => <[u8; 32]>::from(who).into(), + MultiSigner::Ecdsa(who) => runtime_io::blake2_256(who.as_ref()).into(), } } } @@ -227,47 +255,83 @@ impl From for MultiSigner { } } +impl TryFrom for ed25519::Public { + type Error = (); + fn try_from(m: MultiSigner) -> Result { + if let MultiSigner::Ed25519(x) = m { Ok(x) } else { Err(()) } + } +} + impl From for MultiSigner { fn from(x: sr25519::Public) -> Self { MultiSigner::Sr25519(x) } } - #[cfg(feature = "std")] +impl TryFrom for sr25519::Public { + type Error = (); + fn try_from(m: MultiSigner) -> Result { + if let MultiSigner::Sr25519(x) = m { Ok(x) } else { Err(()) } + } +} + +impl From for MultiSigner { + fn from(x: ecdsa::Public) -> Self { + MultiSigner::Ecdsa(x) + } +} + +impl TryFrom for ecdsa::Public { + type Error = (); + fn try_from(m: MultiSigner) -> Result { + if let MultiSigner::Ecdsa(x) = m { Ok(x) } else { Err(()) } + } +} + +#[cfg(feature = "std")] impl std::fmt::Display for MultiSigner { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { MultiSigner::Ed25519(ref who) => write!(fmt, "ed25519: {}", who), MultiSigner::Sr25519(ref who) => write!(fmt, "sr25519: {}", who), + MultiSigner::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who), } } } impl Verify for MultiSignature { type Signer = MultiSigner; - fn verify>(&self, msg: L, signer: &Self::Signer) -> bool { + fn verify>(&self, mut msg: L, signer: &AccountId32) -> bool { + use primitives::crypto::Public; match (self, signer) { - (MultiSignature::Ed25519(ref sig), &MultiSigner::Ed25519(ref who)) => sig.verify(msg, who), - (MultiSignature::Sr25519(ref sig), &MultiSigner::Sr25519(ref who)) => sig.verify(msg, who), - _ => false, + (MultiSignature::Ed25519(ref sig), who) => sig.verify(msg, &ed25519::Public::from_slice(who.as_ref())), + (MultiSignature::Sr25519(ref sig), who) => sig.verify(msg, &sr25519::Public::from_slice(who.as_ref())), + (MultiSignature::Ecdsa(ref sig), who) => { + let m = runtime_io::blake2_256(msg.get()); + match runtime_io::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { + Ok(pubkey) => &runtime_io::blake2_256(pubkey.as_ref()) == >::as_ref(who), + _ => false, + } + } } } } /// Signature verify that can work with any known signature types.. -#[derive(Eq, PartialEq, Clone, Default, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(Eq, PartialEq, Clone, Default, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct AnySignature(H512); impl Verify for AnySignature { type Signer = sr25519::Public; fn verify>(&self, mut msg: L, signer: &sr25519::Public) -> bool { + use primitives::crypto::Public; + let msg = msg.get(); sr25519::Signature::try_from(self.0.as_fixed_bytes().as_ref()) - .map(|s| runtime_io::sr25519_verify(&s, msg.get(), &signer)) + .map(|s| s.verify(msg, signer)) .unwrap_or(false) || ed25519::Signature::try_from(self.0.as_fixed_bytes().as_ref()) - .and_then(|s| ed25519::Public::try_from(signer.0.as_ref()).map(|p| (s, p))) - .map(|(s, p)| runtime_io::ed25519_verify(&s, msg.get(), &p)) + .map(|s| s.verify(msg, &ed25519::Public::from_slice(signer.as_ref()))) .unwrap_or(false) } } @@ -284,8 +348,8 @@ impl From for AnySignature { } } -#[derive(Eq, PartialEq, Clone, Copy, Decode, Encode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[derive(Eq, PartialEq, Clone, Copy, Decode, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize))] /// Reason why an extrinsic couldn't be applied (i.e. invalid extrinsic). pub enum ApplyError { /// General error to do with the permissions of the sender. @@ -336,8 +400,8 @@ impl From for ApplyOutcome { /// Result from attempt to apply an extrinsic. pub type ApplyResult = Result; -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize))] /// Reason why a dispatch call failed pub struct DispatchError { /// Module index, matching the metadata module index @@ -391,7 +455,7 @@ impl From<&'static str> for DispatchError { /// Verify a signature on an encoded value in a lazy manner. This can be /// an optimization if the signature scheme has an "unsigned" escape hash. -pub fn verify_encoded_lazy(sig: &V, item: &T, signer: &V::Signer) -> bool { +pub fn verify_encoded_lazy(sig: &V, item: &T, signer: &::AccountId) -> bool { // The `Lazy` trait expresses something like `X: FnMut &'a T>`. // unfortunately this is a lifetime relationship that can't // be expressed without generic associated types, better unification of HRTBs in type position, @@ -477,11 +541,11 @@ macro_rules! impl_outer_config { #[cfg(any(feature = "std", test))] impl $crate::BuildStorage for $main { fn assimilate_storage( - self, + &self, storage: &mut ($crate::StorageOverlay, $crate::ChildrenStorageOverlay), ) -> std::result::Result<(), String> { $( - if let Some(extra) = self.[< $snake $(_ $instance )? >] { + if let Some(ref extra) = self.[< $snake $(_ $instance )? >] { $crate::impl_outer_config! { @CALL_FN $concrete; @@ -559,13 +623,19 @@ macro_rules! assert_eq_error_rate { #[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] pub struct OpaqueExtrinsic(pub Vec); -#[cfg(feature = "std")] -impl std::fmt::Debug for OpaqueExtrinsic { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { +impl rstd::fmt::Debug for OpaqueExtrinsic { + #[cfg(feature = "std")] + fn fmt(&self, fmt: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0)) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _fmt: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } + #[cfg(feature = "std")] impl ::serde::Serialize for OpaqueExtrinsic { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { diff --git a/core/sr-primitives/src/offchain/http.rs b/core/sr-primitives/src/offchain/http.rs index 6df18da83ec5a09204fa491443b858ee2ad0b907..77e514d65342bb605f030cf7b8f86e95a9d34d40 100644 --- a/core/sr-primitives/src/offchain/http.rs +++ b/core/sr-primitives/src/offchain/http.rs @@ -51,6 +51,7 @@ use rstd::str; use rstd::prelude::Vec; #[cfg(not(feature = "std"))] use rstd::prelude::vec; +use primitives::RuntimeDebug; use primitives::offchain::{ Timestamp, HttpRequestId as RequestId, @@ -59,8 +60,7 @@ use primitives::offchain::{ }; /// Request method (HTTP verb) -#[derive(Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, RuntimeDebug)] pub enum Method { /// GET request Get, @@ -93,8 +93,7 @@ mod header { use super::*; /// A header type. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Clone, PartialEq, Eq, RuntimeDebug)] pub struct Header { name: Vec, value: Vec, @@ -128,8 +127,7 @@ mod header { } /// An HTTP request builder. -#[derive(Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, RuntimeDebug)] pub struct Request<'a, T = Vec<&'static [u8]>> { /// Request method pub method: Method, @@ -249,8 +247,7 @@ impl<'a, I: AsRef<[u8]>, T: IntoIterator> Request<'a, T> { } /// A request error -#[derive(Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, RuntimeDebug)] pub enum Error { /// Deadline has been reached. DeadlineReached, @@ -261,8 +258,7 @@ pub enum Error { } /// A struct representing an uncompleted http request. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, RuntimeDebug)] pub struct PendingRequest { /// Request ID pub id: RequestId, @@ -323,7 +319,7 @@ impl PendingRequest { } /// A HTTP response. -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(RuntimeDebug)] pub struct Response { /// Request id pub id: RequestId, @@ -452,8 +448,7 @@ impl Iterator for ResponseBody { } /// A collection of Headers in the response. -#[derive(Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, RuntimeDebug)] pub struct Headers { /// Raw headers pub raw: Vec<(Vec, Vec)>, @@ -483,8 +478,7 @@ impl Headers { } /// A custom iterator traversing all the headers. -#[derive(Clone)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, RuntimeDebug)] pub struct HeadersIterator<'a> { collection: &'a [(Vec, Vec)], index: Option, @@ -512,16 +506,17 @@ impl<'a> HeadersIterator<'a> { #[cfg(test)] mod tests { use super::*; - use runtime_io::{TestExternalities, with_externalities}; + use runtime_io::TestExternalities; use substrate_offchain::testing; + use primitives::offchain::OffchainExt; #[test] fn should_send_a_basic_request_and_get_response() { let (offchain, state) = testing::TestOffchainExt::new(); let mut t = TestExternalities::default(); - t.set_offchain_externalities(offchain); + t.register_extension(OffchainExt::new(offchain)); - with_externalities(&mut t, || { + t.execute_with(|| { let request: Request = Request::get("http://localhost:1234"); let pending = request .add_header("X-Auth", "hunter2") @@ -560,9 +555,9 @@ mod tests { fn should_send_a_post_request() { let (offchain, state) = testing::TestOffchainExt::new(); let mut t = TestExternalities::default(); - t.set_offchain_externalities(offchain); + t.register_extension(OffchainExt::new(offchain)); - with_externalities(&mut t, || { + t.execute_with(|| { let pending = Request::default() .method(Method::Post) .url("http://localhost:1234") diff --git a/core/sr-primitives/src/sr_arithmetic.rs b/core/sr-primitives/src/sr_arithmetic.rs deleted file mode 100644 index 20a7f51c1f1b5fb5f5dd1b18ad5a5bc0e31bd6c3..0000000000000000000000000000000000000000 --- a/core/sr-primitives/src/sr_arithmetic.rs +++ /dev/null @@ -1,1373 +0,0 @@ -// 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 . - -//! Minimal fixed point arithmetic primitives and types for runtime. - -#[cfg(feature = "std")] -use crate::serde::{Serialize, Deserialize}; - -use rstd::{ - ops, prelude::*, - convert::{TryInto, TryFrom}, - cmp::Ordering, -}; -use codec::{Encode, Decode}; -use crate::traits::{ - SaturatedConversion, CheckedSub, CheckedAdd, Bounded, UniqueSaturatedInto, Saturating, Zero, -}; - -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, Debug, Ord, PartialOrd))] - #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] - pub struct $name($type); - - impl $name { - /// Nothing. - pub fn zero() -> Self { Self(0) } - - /// `true` if this is nothing. - pub fn is_zero(&self) -> bool { self.0 == 0 } - - /// Everything. - pub 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 } - - /// 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 from a percent. 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)) - } - - /// Return the product of multiplication of this value by itself. - pub 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 = $max as $upper_type * $max as $upper_type; - Self::from_rational_approximation(p, q) - } - - /// Converts a fraction into `Permill`. - #[cfg(feature = "std")] - pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) } - - /// 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 - { - // q cannot be zero. - let q = q.max((1 as $type).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()); - - // 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()) - .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()) - .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" - ); - - // `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. - let part = - p_reduce as $upper_type - * ($max as $upper_type) - / q_reduce as $upper_type; - - $name(part as $type) - } - } - - impl Saturating for $name { - fn saturating_add(self, rhs: Self) -> Self { - // defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow. - Self::from_parts(self.0.saturating_add(rhs.0)) - } - fn saturating_sub(self, rhs: Self) -> Self { - Self::from_parts(self.0.saturating_sub(rhs.0)) - } - fn saturating_mul(self, rhs: Self) -> Self { - let a = self.0 as $upper_type; - let b = rhs.0 as $upper_type; - let m = $max as $upper_type; - let parts = a * b / m; - // This will always fit into $type. - Self::from_parts(parts as $type) - } - } - - impl ops::Div for $name { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - let p = self.0; - let q = rhs.0; - Self::from_rational_approximation(p, q) - } - } - - /// Overflow-prune multiplication. - /// - /// tailored to be used with a balance type. - impl ops::Mul for $name - where - N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem - + ops::Div + ops::Mul + ops::Add, - { - type Output = N; - fn mul(self, b: N) -> Self::Output { - let maximum: N = $max.into(); - let upper_max: $upper_type = $max.into(); - let part: N = self.0.into(); - - let rem_multiplied_divided = { - let rem = b.clone().rem(maximum.clone()); - - // `rem_sized` is inferior to $max, thus it fits into $type. This is assured by - // a test. - let rem_sized = rem.saturated_into::<$type>(); - - // `self` and `rem_sized` are inferior to $max, thus the product is less than - // $max^2 and fits into $upper_type. This is assured by a test. - let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type; - - // `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits - // in $type. remember that $type always fits $max. - let mut rem_multiplied_divided_sized = - (rem_multiplied_upper / upper_max) as $type; - // fix a tiny rounding error - if rem_multiplied_upper % upper_max > upper_max / 2 { - rem_multiplied_divided_sized += 1; - } - - // `rem_multiplied_divided_sized` is inferior to b, thus it can be converted - // back to N type - rem_multiplied_divided_sized.into() - }; - - (b / maximum) * part + rem_multiplied_divided - } - } - - impl codec::CompactAs for $name { - type As = $type; - fn encode_as(&self) -> &$type { - &self.0 - } - fn decode_from(x: $type) -> Self { - Self(x) - } - } - - impl From> for $name { - fn from(x: codec::Compact<$name>) -> Self { - x.0 - } - } - - #[cfg(test)] - mod $test_mod { - use codec::{Encode, Decode}; - use super::{$name, Saturating}; - use crate::{assert_eq_error_rate, traits::Zero}; - - - #[test] - fn macro_expanded_correctly() { - // needed for the `from_percent` to work. - assert!($max >= 100); - assert!($max % 100 == 0); - - // needed for `from_rational_approximation` - assert!(2 * $max < <$type>::max_value()); - assert!(($max as $upper_type) < <$upper_type>::max_value()); - - // for something like percent they can be the same. - assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value()); - assert!(($max as $upper_type).checked_mul($max.into()).is_some()); - } - - #[derive(Encode, Decode, PartialEq, Eq, Debug)] - struct WithCompact { - data: T, - } - - #[test] - fn has_compact() { - let data = WithCompact { data: $name(1) }; - let encoded = data.encode(); - assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap()); - } - - #[test] - fn compact_encoding() { - let tests = [ - // assume all per_things have the size u8 at least. - (0 as $type, 1usize), - (1 as $type, 1usize), - (63, 1), - (64, 2), - (65, 2), - (<$type>::max_value(), <$type>::max_value().encode().len() + 1) - ]; - for &(n, l) in &tests { - let compact: crate::codec::Compact<$name> = $name(n).into(); - let encoded = compact.encode(); - assert_eq!(encoded.len(), l); - let decoded = >::decode(&mut & encoded[..]) - .unwrap(); - let per_thingy: $name = decoded.into(); - assert_eq!(per_thingy, $name(n)); - } - } - - #[test] - fn per_thing_api_works() { - // 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::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)); - } - - macro_rules! per_thing_mul_test { - ($num_type:tt) => { - // multiplication from all sort of from_percent - assert_eq!( - $name::from_percent(100) * $num_type::max_value(), - $num_type::max_value() - ); - assert_eq_error_rate!( - $name::from_percent(99) * $num_type::max_value(), - ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, - 1, - ); - assert_eq!( - $name::from_percent(50) * $num_type::max_value(), - $num_type::max_value() / 2, - ); - assert_eq_error_rate!( - $name::from_percent(1) * $num_type::max_value(), - $num_type::max_value() / 100, - 1, - ); - assert_eq!($name::from_percent(0) * $num_type::max_value(), 0); - - // // multiplication with bounds - assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value()); - assert_eq!($name::zero() * $num_type::max_value(), 0); - } - } - - #[test] - fn per_thing_mul_works() { - use primitive_types::U256; - - // accuracy test - assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10); - - $(per_thing_mul_test!($test_units);)* - } - - #[test] - fn per_thing_mul_rounds_to_nearest_number() { - assert_eq!($name::from_percent(33) * 10u64, 3); - assert_eq!($name::from_percent(34) * 10u64, 3); - assert_eq!($name::from_percent(35) * 10u64, 3); - assert_eq!($name::from_percent(36) * 10u64, 4); - assert_eq!($name::from_percent(36) * 10u64, 4); - } - - #[test] - fn per_thing_multiplication_with_large_number() { - use primitive_types::U256; - let max_minus_one = $max - 1; - assert_eq_error_rate!( - $name::from_parts(max_minus_one) * std::u128::MAX, - ((Into::::into(std::u128::MAX) * max_minus_one) / $max).as_u128(), - 1, - ); - } - - macro_rules! per_thing_from_rationale_approx_test { - ($num_type:tt) => { - // within accuracy boundary - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 0), - $name::one(), - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 1), - $name::one(), - ); - assert_eq_error_rate!( - $name::from_rational_approximation(1 as $num_type, 3).0, - $name::from_parts($max / 3).0, - 2 - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 10), - $name::from_percent(10), - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 4), - $name::from_percent(25), - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 4), - $name::from_rational_approximation(2 as $num_type, 8), - ); - // no accurate anymore but won't overflow. - assert_eq!( - $name::from_rational_approximation( - $num_type::max_value() - 1, - $num_type::max_value() - ), - $name::one(), - ); - assert_eq_error_rate!( - $name::from_rational_approximation( - $num_type::max_value() / 3, - $num_type::max_value() - ).0, - $name::from_parts($max / 3).0, - 2 - ); - assert_eq!( - $name::from_rational_approximation(1, $num_type::max_value()), - $name::zero(), - ); - }; - } - - #[test] - fn per_thing_from_rationale_approx_works() { - // This is just to make sure something like Percent which _might_ get built from a - // u8 does not overflow in the context of this test. - let max_value = $max as $upper_type; - // almost at the edge - assert_eq!( - $name::from_rational_approximation($max - 1, $max + 1), - $name::from_parts($max - 2), - ); - assert_eq!( - $name::from_rational_approximation(1, $max-1), - $name::from_parts(1), - ); - assert_eq!( - $name::from_rational_approximation(1, $max), - $name::from_parts(1), - ); - assert_eq!( - $name::from_rational_approximation(2, 2 * $max - 1), - $name::from_parts(1), - ); - assert_eq!( - $name::from_rational_approximation(1, $max+1), - $name::zero(), - ); - assert_eq!( - $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), - $name::from_percent(50), - ); - $(per_thing_from_rationale_approx_test!($test_units);)* - } - - #[test] - fn per_things_mul_operates_in_output_type() { - // assert_eq!($name::from_percent(50) * 100u32, 50u32); - assert_eq!($name::from_percent(50) * 100u64, 50u64); - assert_eq!($name::from_percent(50) * 100u128, 50u128); - } - - #[test] - fn per_thing_saturating_op_works() { - assert_eq!( - $name::from_percent(50).saturating_add($name::from_percent(40)), - $name::from_percent(90) - ); - assert_eq!( - $name::from_percent(50).saturating_add($name::from_percent(50)), - $name::from_percent(100) - ); - assert_eq!( - $name::from_percent(60).saturating_add($name::from_percent(50)), - $name::from_percent(100) - ); - - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(50)), - $name::from_percent(10) - ); - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(60)), - $name::from_percent(0) - ); - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(70)), - $name::from_percent(0) - ); - - assert_eq!( - $name::from_percent(50).saturating_mul($name::from_percent(50)), - $name::from_percent(25) - ); - assert_eq!( - $name::from_percent(20).saturating_mul($name::from_percent(20)), - $name::from_percent(4) - ); - assert_eq!( - $name::from_percent(10).saturating_mul($name::from_percent(10)), - $name::from_percent(1) - ); - } - - #[test] - fn per_thing_square_works() { - assert_eq!($name::from_percent(100).square(), $name::from_percent(100)); - assert_eq!($name::from_percent(50).square(), $name::from_percent(25)); - assert_eq!($name::from_percent(10).square(), $name::from_percent(1)); - assert_eq!( - $name::from_percent(2).square(), - $name::from_parts((4 * ($max as $upper_type) / 100 / 100) as $type) - ); - } - - #[test] - fn per_things_div_works() { - // normal - assert_eq!($name::from_percent(10) / $name::from_percent(20), - $name::from_percent(50) - ); - assert_eq!($name::from_percent(10) / $name::from_percent(10), - $name::from_percent(100) - ); - assert_eq!($name::from_percent(10) / $name::from_percent(0), - $name::from_percent(100) - ); - - // will not overflow - assert_eq!($name::from_percent(10) / $name::from_percent(5), - $name::from_percent(100) - ); - assert_eq!($name::from_percent(100) / $name::from_percent(50), - $name::from_percent(100) - ); - } - } - }; -} - -implement_per_thing!( - Percent, - test_per_cent, - [u32, u64, u128], - 100u8, - u8, - u16, - "_Percent_", -); -implement_per_thing!( - Permill, - test_permill, - [u32, u64, u128], - 1_000_000u32, - u32, - u64, - "_Parts per Million_", -); -implement_per_thing!( - Perbill, - test_perbill, - [u32, u64, u128], - 1_000_000_000u32, - u32, - u64, - "_Parts per Billion_", -); -implement_per_thing!( - Perquintill, - test_perquintill, - [u64, u128], - 1_000_000_000_000_000_000u64, - u64, - u128, - "_Parts per Quintillion_", -); - -/// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036] -/// with fixed point accuracy of one billion. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Fixed64(i64); - -/// The accuracy of the `Fixed64` type. -const DIV: i64 = 1_000_000_000; - -impl Fixed64 { - /// creates self from a natural number. - /// - /// Note that this might be lossy. - pub fn from_natural(int: i64) -> Self { - Self(int.saturating_mul(DIV)) - } - - /// Return the accuracy of the type. Given that this function returns the value `X`, it means - /// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`. - pub fn accuracy() -> i64 { - DIV - } - - /// Raw constructor. Equal to `parts / 1_000_000_000`. - pub fn from_parts(parts: i64) -> Self { - Self(parts) - } - - /// creates self from a rational number. Equal to `n/d`. - /// - /// Note that this might be lossy. - pub fn from_rational(n: i64, d: u64) -> Self { - Self( - ((n as i128).saturating_mul(DIV as i128) / (d as i128).max(1)) - .try_into() - .unwrap_or(Bounded::max_value()) - ) - } - - /// Performs a saturated multiply and accumulate by unsigned number. - /// - /// Returns a saturated `int + (self * int)`. - pub fn saturated_multiply_accumulate(&self, int: N) -> N - where - N: TryFrom + From + UniqueSaturatedInto + Bounded + Clone + Saturating + - ops::Rem + ops::Div + ops::Mul + - ops::Add, - { - let div = DIV as u64; - let positive = self.0 > 0; - // safe to convert as absolute value. - let parts = self.0.checked_abs().map(|v| v as u64).unwrap_or(i64::max_value() as u64 + 1); - - - // will always fit. - let natural_parts = parts / div; - // might saturate. - let natural_parts: N = natural_parts.saturated_into(); - // fractional parts can always fit into u32. - let perbill_parts = (parts % div) as u32; - - let n = int.clone().saturating_mul(natural_parts); - let p = Perbill::from_parts(perbill_parts) * int.clone(); - - // everything that needs to be either added or subtracted from the original weight. - let excess = n.saturating_add(p); - - if positive { - int.saturating_add(excess) - } else { - int.saturating_sub(excess) - } - } -} - -impl Saturating for Fixed64 { - fn saturating_add(self, rhs: Self) -> Self { - Self(self.0.saturating_add(rhs.0)) - } - fn saturating_mul(self, rhs: Self) -> Self { - Self(self.0.saturating_mul(rhs.0) / DIV) - } - fn saturating_sub(self, rhs: Self) -> Self { - Self(self.0.saturating_sub(rhs.0)) - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe addition. -impl ops::Add for Fixed64 { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe subtraction. -impl ops::Sub for Fixed64 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } -} - -impl CheckedSub for Fixed64 { - fn checked_sub(&self, rhs: &Self) -> Option { - if let Some(v) = self.0.checked_sub(rhs.0) { - Some(Self(v)) - } else { - None - } - } -} - -impl CheckedAdd for Fixed64 { - fn checked_add(&self, rhs: &Self) -> Option { - if let Some(v) = self.0.checked_add(rhs.0) { - Some(Self(v)) - } else { - None - } - } -} - -/// Some helper functions to work with 128bit numbers. Note that the functionality provided here is -/// only sensible to use with 128bit numbers because for smaller sizes, you can always rely on -/// assumptions of a bigger type (u128) being available, or simply create a per-thing and use the -/// multiplication implementation provided there. -pub mod helpers_128bit { - use super::Perquintill; - use crate::traits::Zero; - use rstd::cmp::{min, max}; - - const ERROR: &'static str = "can not accurately multiply by rational in this type"; - - /// Helper gcd function used in Rational128 implementation. - pub fn gcd(a: u128, b: u128) -> u128 { - match ((a, b), (a & 1, b & 1)) { - ((x, y), _) if x == y => y, - ((0, x), _) | ((x, 0), _) => x, - ((x, y), (0, 1)) | ((y, x), (1, 0)) => gcd(x >> 1, y), - ((x, y), (0, 0)) => gcd(x >> 1, y >> 1) << 1, - ((x, y), (1, 1)) => { - let (x, y) = (min(x, y), max(x, y)); - gcd((y - x) >> 1, x) - }, - _ => unreachable!(), - } - } - - /// Safely and accurately compute `a * b / c`. The approach is: - /// - Simply try `a * b / c`. - /// - Else, swap the operations (divide first) if `a > c` (division is possible) and `b <= c` - /// (overflow cannot happen) - /// - At any point, given an overflow or accuracy loss, return an Error. - /// - /// Invariant: c must be greater than or equal to 1. - /// This might not return Ok even if `b < c`. - pub fn multiply_by_rational(a: u128, b: u128, c: u128) -> Result { - if a.is_zero() || b.is_zero() { return Ok(Zero::zero()); } - - // invariant: C cannot be zero. - let c = c.max(1); - - // a and b are interchangeable by definition in this function. It always helps to assume the - // bigger of which is being multiplied by a `0 < b/c < 1`. Hence, a should be the bigger and - // b the smaller one. - let t = a; - let a = a.max(b); - let b = t.min(b); - - if let Some(x) = a.checked_mul(b) { - // This is the safest way to go. Try it. - Ok(x / c) - } else if a > c { - // if it can be safely swapped and it is a fraction, then swap. - let q = a / c; - let r = a % c; - let r_additional = multiply_by_rational(r, b, c)?; - - let q_part = q.checked_mul(b) - .ok_or(ERROR)?; - let result = q_part.checked_add(r_additional) - .ok_or(ERROR)?; - Ok(result) - } else { - Err(ERROR) - } - } - - /// Performs [`multiply_by_rational`]. In case of failure, if `b < c` it tries to approximate - /// the ratio into a perquintillion and return a lossy result. Otherwise, a best effort approach - /// of shifting both b and c is performed until multiply_by_rational can work. - /// - /// This function can very well be lossy and as the name suggests, perform a best effort in the - /// scope of u128 numbers. In case `b > c` and overflow happens, `a` is returned. - /// - /// c must be greater than or equal to 1. - pub fn multiply_by_rational_best_effort(a: u128, b: u128, c: u128) -> u128 { - if a.is_zero() || b.is_zero() { return Zero::zero(); } - let c = c.max(1); - - // unwrap the loop once. This favours performance over readability. - multiply_by_rational(a, b, c).unwrap_or_else(|_| { - if b <= c { - let per_thing = Perquintill::from_rational_approximation(b, c); - per_thing * a - } else { - let mut shift = 1; - let mut shifted_b = b.checked_shr(shift).unwrap_or(0); - let mut shifted_c = c.checked_shr(shift).unwrap_or(0); - - loop { - if shifted_b.is_zero() || shifted_c.is_zero() { - break a - } - match multiply_by_rational(a, shifted_b, shifted_c) { - Ok(r) => break r, - Err(_) => { - shift = shift + 1; - // by the time we reach here, b >= 1 && c >= 1. Before panic, they have - // to be zero which is prevented to happen by the break. - shifted_b = b.checked_shr(shift) - .expect( - "b >= 1 && c >= 1; break prevents them from ever being zero; \ - panic can only happen after either is zero; qed" - ); - shifted_c = c.checked_shr(shift) - .expect( - "b >= 1 && c >= 1; break prevents them from ever being zero; \ - panic can only happen after either is zero; qed" - ); - } - } - } - } - }) - } -} - -/// A wrapper for any rational number with a 128 bit numerator and denominator. -#[derive(Clone, Copy, Default, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct Rational128(u128, u128); - -impl Rational128 { - /// Nothing. - pub fn zero() -> Self { - Self(0, 1) - } - - /// If it is zero or not - pub fn is_zero(&self) -> bool { - self.0.is_zero() - } - - /// Build from a raw `n/d`. - pub fn from(n: u128, d: u128) -> Self { - Self(n, d.max(1)) - } - - /// Build from a raw `n/d`. This could lead to / 0 if not properly handled. - pub fn from_unchecked(n: u128, d: u128) -> Self { - Self(n, d) - } - - /// Return the numerator. - pub fn n(&self) -> u128 { - self.0 - } - - /// Return the denominator. - pub fn d(&self) -> u128 { - self.1 - } - - /// Convert `self` to a similar rational number where denominator is the given `den`. - // - /// This only returns if the result is accurate. `Err` is returned if the result cannot be - /// accurately calculated. - pub fn to_den(self, den: u128) -> Result { - if den == self.1 { - Ok(self) - } else { - if den > self.1 { - let n = helpers_128bit::multiply_by_rational(self.0, den, self.1)?; - Ok(Self(n, den)) - } else { - let div = self.1 / den; - Ok(Self(self.0 / div.max(1), den)) - } - } - } - - /// Get the least common divisor of `self` and `other`. - /// - /// This only returns if the result is accurate. `Err` is returned if the result cannot be - /// accurately calculated. - pub fn lcm(&self, other: &Self) -> Result { - // this should be tested better: two large numbers that are almost the same. - if self.1 == other.1 { return Ok(self.1) } - let g = helpers_128bit::gcd(self.1, other.1); - helpers_128bit::multiply_by_rational(self.1 , other.1, g) - } - - /// A saturating add that assumes `self` and `other` have the same denominator. - pub fn lazy_saturating_add(self, other: Self) -> Self { - if other.is_zero() { - self - } else { - Self(self.0.saturating_add(other.0) ,self.1) - } - } - - /// A saturating subtraction that assumes `self` and `other` have the same denominator. - pub fn lazy_saturating_sub(self, other: Self) -> Self { - if other.is_zero() { - self - } else { - Self(self.0.saturating_sub(other.0) ,self.1) - } - } - - /// Addition. Simply tries to unify the denominators and add the numerators. - /// - /// Overflow might happen during any of the steps. Error is returned in such cases. - pub fn checked_add(self, other: Self) -> Result { - let lcm = self.lcm(&other)?; - let self_scaled = self.to_den(lcm)?; - let other_scaled = other.to_den(lcm)?; - let n = self_scaled.0.checked_add(other_scaled.0) - .ok_or("overflow while adding numerators")?; - Ok(Self(n, self_scaled.1)) - } - - /// Subtraction. Simply tries to unify the denominators and subtract the numerators. - /// - /// Overflow might happen during any of the steps. None is returned in such cases. - pub fn checked_sub(self, other: Self) -> Result { - let lcm = self.lcm(&other)?; - let self_scaled = self.to_den(lcm)?; - let other_scaled = other.to_den(lcm)?; - - let n = self_scaled.0.checked_sub(other_scaled.0) - .ok_or("overflow while subtracting numerators")?; - Ok(Self(n, self_scaled.1)) - } -} - -impl PartialOrd for Rational128 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// Note that this implementation can very well be lossy. TODO #3670 -impl Ord for Rational128 { - fn cmp(&self, other: &Self) -> Ordering { - // handle some edge cases. - if self.1 == other.1 { - self.0.cmp(&other.0) - } else if self.1.is_zero() { - Ordering::Greater - } else if other.1.is_zero() { - Ordering::Less - } else { - // general lossy case - let saturated_lcm = helpers_128bit::multiply_by_rational_best_effort( - self.1, - other.1, - helpers_128bit::gcd(self.1, other.1) - ); - let self_scaled = self.to_den(saturated_lcm) - .unwrap_or(Self(Bounded::max_value(), self.1)); - let other_scaled = other.to_den(saturated_lcm) - .unwrap_or(Self(Bounded::max_value(), other.1)); - self_scaled.n().cmp(&other_scaled.n()) - } - } -} - -/// Note that this implementation can very well be lossy. TODO #3670 -impl PartialEq for Rational128 { - fn eq(&self, other: &Self) -> bool { - // handle some edge cases. - if self.1 == other.1 { - self.0.eq(&other.0) - } else { - // general lossy case - let saturated_lcm = helpers_128bit::multiply_by_rational_best_effort( - self.1, - other.1, - helpers_128bit::gcd(self.1, other.1) - ); - let self_scaled = self.to_den(saturated_lcm) - .unwrap_or(Self(Bounded::max_value(), self.1)); - let other_scaled = other.to_den(saturated_lcm) - .unwrap_or(Self(Bounded::max_value(), other.1)); - self_scaled.n().eq(&other_scaled.n()) - } - } -} - - -#[cfg(test)] -mod test_rational128 { - use super::*; - use super::helpers_128bit::*; - use crate::assert_eq_error_rate; - use rand::Rng; - - const MAX128: u128 = u128::max_value(); - const MAX64: u128 = u64::max_value() as u128; - const MAX64_2: u128 = 2 * u64::max_value() as u128; - - fn r(p: u128, q: u128) -> Rational128 { - Rational128(p, q) - } - - fn mul_div(a: u128, b: u128, c: u128) -> u128 { - use primitive_types::U256; - if a.is_zero() { return Zero::zero(); } - let c = c.max(1); - - // e for extended - let ae: U256 = a.into(); - let be: U256 = b.into(); - let ce: U256 = c.into(); - - let r = ae * be / ce; - if r > u128::max_value().into() { - a - } else { - r.as_u128() - } - } - - #[test] - fn truth_value_function_works() { - assert_eq!( - mul_div(2u128.pow(100), 8, 4), - 2u128.pow(101) - ); - assert_eq!( - mul_div(2u128.pow(100), 4, 8), - 2u128.pow(99) - ); - - // and it returns a if result cannot fit - assert_eq!(mul_div(MAX128 - 10, 2, 1), MAX128 - 10); - } - - #[test] - fn to_denom_works() { - // simple up and down - assert_eq!(r(1, 5).to_den(10), Ok(r(2, 10))); - assert_eq!(r(4, 10).to_den(5), Ok(r(2, 5))); - - // up and down with large numbers - assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Ok(r(9, 10))); - assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Ok(r(5, 10))); - - // large to perbill. This is very well needed for phragmen. - assert_eq!( - r(MAX128 / 2, MAX128).to_den(1000_000_000), - Ok(r(500_000_000, 1000_000_000)) - ); - - // large to large - assert_eq!(r(MAX128 / 2, MAX128).to_den(MAX128/2), Ok(r(MAX128/4, MAX128/2))); - } - - #[test] - fn gdc_works() { - assert_eq!(gcd(10, 5), 5); - assert_eq!(gcd(7, 22), 1); - } - - #[test] - fn lcm_works() { - // simple stuff - assert_eq!(r(3, 10).lcm(&r(4, 15)).unwrap(), 30); - assert_eq!(r(5, 30).lcm(&r(1, 7)).unwrap(), 210); - assert_eq!(r(5, 30).lcm(&r(1, 10)).unwrap(), 30); - - // large numbers - assert_eq!( - r(1_000_000_000, MAX128).lcm(&r(7_000_000_000, MAX128-1)), - Err("can not accurately multiply by rational in this type"), - ); - assert_eq!( - r(1_000_000_000, MAX64).lcm(&r(7_000_000_000, MAX64-1)), - Ok(340282366920938463408034375210639556610), - ); - assert!(340282366920938463408034375210639556610 < MAX128); - assert!(340282366920938463408034375210639556610 == MAX64 * (MAX64 - 1)); - } - - #[test] - fn add_works() { - // works - assert_eq!(r(3, 10).checked_add(r(1, 10)).unwrap(), r(2, 5)); - assert_eq!(r(3, 10).checked_add(r(3, 7)).unwrap(), r(51, 70)); - - // errors - assert_eq!( - r(1, MAX128).checked_add(r(1, MAX128-1)), - Err("can not accurately multiply by rational in this type"), - ); - assert_eq!( - r(7, MAX128).checked_add(r(MAX128, MAX128)), - Err("overflow while adding numerators"), - ); - assert_eq!( - r(MAX128, MAX128).checked_add(r(MAX128, MAX128)), - Err("overflow while adding numerators"), - ); - } - - #[test] - fn sub_works() { - // works - assert_eq!(r(3, 10).checked_sub(r(1, 10)).unwrap(), r(1, 5)); - assert_eq!(r(6, 10).checked_sub(r(3, 7)).unwrap(), r(12, 70)); - - // errors - assert_eq!( - r(2, MAX128).checked_sub(r(1, MAX128-1)), - Err("can not accurately multiply by rational in this type"), - ); - assert_eq!( - r(7, MAX128).checked_sub(r(MAX128, MAX128)), - Err("overflow while subtracting numerators"), - ); - assert_eq!( - r(1, 10).checked_sub(r(2,10)), - Err("overflow while subtracting numerators"), - ); - } - - #[test] - fn ordering_and_eq_works() { - assert!(r(1, 2) > r(1, 3)); - assert!(r(1, 2) > r(2, 6)); - - assert!(r(1, 2) < r(6, 6)); - assert!(r(2, 1) > r(2, 6)); - - assert!(r(5, 10) == r(1, 2)); - assert!(r(1, 2) == r(1, 2)); - - assert!(r(1, 1490000000000200000) > r(1, 1490000000000200001)); - } - - #[test] - fn multiply_by_rational_works() { - assert_eq!(multiply_by_rational(7, 2, 3).unwrap(), 7 * 2 / 3); - assert_eq!(multiply_by_rational(7, 20, 30).unwrap(), 7 * 2 / 3); - assert_eq!(multiply_by_rational(20, 7, 30).unwrap(), 7 * 2 / 3); - - // computed with swap - assert_eq!( - // MAX128 % 3 == 0 - multiply_by_rational(MAX128, 2, 3).unwrap(), - MAX128 / 3 * 2, - ); - assert_eq!( - // MAX128 % 7 == 3 - multiply_by_rational(MAX128, 5, 7).unwrap(), - (MAX128 / 7 * 5) + (3 * 5 / 7), - ); - assert_eq!( - // MAX128 % 7 == 3 - multiply_by_rational(MAX128, 11 , 13).unwrap(), - (MAX128 / 13 * 11) + (8 * 11 / 13), - ); - assert_eq!( - // MAX128 % 1000 == 455 - multiply_by_rational(MAX128, 555, 1000).unwrap(), - (MAX128 / 1000 * 555) + (455 * 555 / 1000), - ); - - assert_eq!( - multiply_by_rational(2 * MAX64 - 1, MAX64, MAX64).unwrap(), - 2 * MAX64 - 1, - ); - assert_eq!( - multiply_by_rational(2 * MAX64 - 1, MAX64 - 1, MAX64).unwrap(), - 2 * MAX64 - 3, - ); - - - assert_eq!( - multiply_by_rational(MAX64 + 100, MAX64_2, MAX64_2 / 2).unwrap(), - (MAX64 + 100) * 2, - ); - assert_eq!( - multiply_by_rational(MAX64 + 100, MAX64_2 / 100, MAX64_2 / 200).unwrap(), - (MAX64 + 100) * 2, - ); - - // fails to compute. have to use the greedy, lossy version here - assert!(multiply_by_rational(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)).is_err()); - assert!(multiply_by_rational(1_000_000_000, MAX128 / 8, MAX128 / 2).is_err()); - } - - #[test] - fn multiply_by_rational_a_b_are_interchangeable() { - assert_eq!( - multiply_by_rational(10, MAX128, MAX128 / 2), - Ok(20), - ); - assert_eq!( - multiply_by_rational(MAX128, 10, MAX128 / 2), - Ok(20), - ); - } - - #[test] - fn multiply_by_rational_best_effort_works() { - assert_eq_error_rate!( - multiply_by_rational_best_effort(MAX64 + 100, MAX64_2, MAX64_2 / 2), - (MAX64 + 100) * 2, - 10, - ); - assert_eq_error_rate!( - multiply_by_rational_best_effort(MAX64 + 100, MAX64_2 * 100, MAX64_2 * 100 / 2), - (MAX64 + 100) * 2, - 10, - ); - assert_eq_error_rate!( - multiply_by_rational_best_effort(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)), - mul_div(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)), - 10, - ); - assert_eq_error_rate!( - multiply_by_rational_best_effort(1_000_000_000, MAX128 / 8, MAX128 / 2), - 1_000_000_000 / 4, - 10, - ); - - assert_eq!( - multiply_by_rational_best_effort(MAX128, MAX128 - 1, MAX128), - MAX128, - ); - - assert_eq!( - multiply_by_rational_best_effort(MAX64, MAX128 / 2, MAX128), - MAX64 / 2, - ); - } - - fn do_fuzz_multiply_by_rational( - iters: usize, - bits: u32, - maximum_error: u128, - do_print: bool, - bounded: bool, - mul_fn: F, - ) where F: Fn(u128, u128, u128) -> u128 { - let mut rng = rand::thread_rng(); - let mut average_diff = 0.0; - let upper_bound = 2u128.pow(bits); - - for _ in 0..iters { - let a = rng.gen_range(0u128, upper_bound); - let c = rng.gen_range(0u128, upper_bound); - let b = rng.gen_range( - 0u128, - if bounded { c } else { upper_bound } - ); - - let a: u128 = a.into(); - let b: u128 = b.into(); - let c: u128 = c.into(); - - let truth = mul_div(a, b, c); - let result = mul_fn(a, b, c); - let diff = truth.max(result) - truth.min(result); - let loss_ratio = diff as f64 / truth as f64; - average_diff += loss_ratio; - - if do_print && diff > maximum_error { - println!("++ Computed with more loss than expected: {} * {} / {}", a, b, c); - println!("++ Expected {}", truth); - println!("+++++++ Got {}", result); - } - } - - // print report - println!( - "## Fuzzed with {} numbers. Average error was {}", - iters, - average_diff / (iters as f64), - ); - } - - #[test] - fn fuzz_multiply_by_rational_best_effort_32() { - let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_best_effort_64() { - let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_best_effort_96() { - let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); - // println!("\nInvariant: b < c"); - // do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, true, f); - println!("every possibility"); - // do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, false, f); - do_fuzz_multiply_by_rational(10, 96, 0, true, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_best_effort_128() { - let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_32() { - // if Err just return the truth value. We don't care about such cases. The point of this - // fuzzing is to make sure: if `multiply_by_rational` returns `Ok`, it must be 100% accurate - // returning `Err` is fine. - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_64() { - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_96() { - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_128() { - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, false, f); - } -} - - -#[cfg(test)] -mod tests_fixed64 { - use super::*; - - fn max() -> Fixed64 { - Fixed64::from_parts(i64::max_value()) - } - - #[test] - fn fixed64_semantics() { - assert_eq!(Fixed64::from_rational(5, 2).0, 5 * 1_000_000_000 / 2); - assert_eq!(Fixed64::from_rational(5, 2), Fixed64::from_rational(10, 4)); - assert_eq!(Fixed64::from_rational(5, 0), Fixed64::from_rational(5, 1)); - - // biggest value that can be created. - assert_ne!(max(), Fixed64::from_natural(9_223_372_036)); - assert_eq!(max(), Fixed64::from_natural(9_223_372_037)); - } - - macro_rules! saturating_mul_acc_test { - ($num_type:tt) => { - assert_eq!( - Fixed64::from_rational(100, 1).saturated_multiply_accumulate(10 as $num_type), - 1010, - ); - assert_eq!( - Fixed64::from_rational(100, 2).saturated_multiply_accumulate(10 as $num_type), - 510, - ); - assert_eq!( - Fixed64::from_rational(100, 3).saturated_multiply_accumulate(0 as $num_type), - 0, - ); - assert_eq!( - Fixed64::from_rational(5, 1).saturated_multiply_accumulate($num_type::max_value()), - $num_type::max_value() - ); - assert_eq!( - max().saturated_multiply_accumulate($num_type::max_value()), - $num_type::max_value() - ); - } - } - - #[test] - fn fixed64_multiply_accumulate_works() { - saturating_mul_acc_test!(u32); - saturating_mul_acc_test!(u64); - saturating_mul_acc_test!(u128); - } -} diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index 5391735576aa212e49bc14083e504e0d358a02ad..d60e58bab1a5cc6e20e89632a12322fca2bb43a2 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -117,10 +117,10 @@ impl app_crypto::RuntimeAppPublic for UintAuthorityId { } impl OpaqueKeys for UintAuthorityId { - type KeyTypeIds = std::iter::Cloned>; + type KeyTypeIdProviders = (); - fn key_ids() -> Self::KeyTypeIds { - [key_types::DUMMY].iter().cloned() + fn key_ids() -> &'static [KeyTypeId] { + &[key_types::DUMMY] } fn get_raw(&self, _: KeyTypeId) -> &[u8] { @@ -132,6 +132,10 @@ impl OpaqueKeys for UintAuthorityId { } } +impl crate::BoundToRuntimeAppPublic for UintAuthorityId { + type Public = Self; +} + /// Digest item pub type DigestItem = generic::DigestItem; @@ -259,6 +263,9 @@ impl) -> Self { Block { header, extrinsics } } + fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec { + (header, extrinsics).encode() + } } impl<'a, Xt> Deserialize<'a> for Block where Block: Decode { diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index de200bdf55efd9de63261240c45d2528a8d67c55..ecaf6a78fcfe40b5ac289d58623ae67247255e13 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -17,27 +17,23 @@ //! Primitives for the runtime modules. use rstd::prelude::*; -use rstd::{self, result, marker::PhantomData, convert::{TryFrom, TryInto}}; +use rstd::{self, result, marker::PhantomData, convert::{TryFrom, TryInto}, fmt::Debug}; use runtime_io; #[cfg(feature = "std")] -use std::fmt::{Debug, Display}; +use std::fmt::Display; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; -use primitives::{self, Hasher, Blake2Hasher}; -use crate::codec::{Codec, Encode, Decode, HasCompact}; +use primitives::{self, Hasher, Blake2Hasher, TypeId}; +use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction, }; use crate::generic::{Digest, DigestItem}; use crate::weights::DispatchInfo; -pub use integer_sqrt::IntegerSquareRoot; -pub use num_traits::{ +pub use arithmetic::traits::{ + SimpleArithmetic, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion, Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, - CheckedShl, CheckedShr -}; -use rstd::ops::{ - Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, - RemAssign, Shl, Shr + CheckedShl, CheckedShr, IntegerSquareRoot }; use app_crypto::AppKey; use impl_trait_for_tuples::impl_for_tuples; @@ -54,46 +50,84 @@ impl<'a> Lazy<[u8]> for &'a [u8] { fn get(&mut self) -> &[u8] { &**self } } +/// Some type that is able to be collapsed into an account ID. It is not possible to recreate the original value from +/// the account ID. +pub trait IdentifyAccount { + /// The account ID that this can be transformed into. + type AccountId; + /// Transform into an account. + fn into_account(self) -> Self::AccountId; +} + +impl IdentifyAccount for primitives::ed25519::Public { + type AccountId = Self; + fn into_account(self) -> Self { self } +} + +impl IdentifyAccount for primitives::sr25519::Public { + type AccountId = Self; + fn into_account(self) -> Self { self } +} + +impl IdentifyAccount for primitives::ecdsa::Public { + type AccountId = Self; + fn into_account(self) -> Self { self } +} + /// Means of signature verification. pub trait Verify { /// Type of the signer. - type Signer; + type Signer: IdentifyAccount; /// Verify a signature. Return `true` if signature is valid for the value. - fn verify>(&self, msg: L, signer: &Self::Signer) -> bool; + fn verify>(&self, msg: L, signer: &::AccountId) -> bool; } impl Verify for primitives::ed25519::Signature { type Signer = primitives::ed25519::Public; - fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { + fn verify>(&self, mut msg: L, signer: &primitives::ed25519::Public) -> bool { runtime_io::ed25519_verify(self, msg.get(), signer) } } impl Verify for primitives::sr25519::Signature { type Signer = primitives::sr25519::Public; - fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { + fn verify>(&self, mut msg: L, signer: &primitives::sr25519::Public) -> bool { runtime_io::sr25519_verify(self, msg.get(), signer) } } +impl Verify for primitives::ecdsa::Signature { + type Signer = primitives::ecdsa::Public; + fn verify>(&self, mut msg: L, signer: &primitives::ecdsa::Public) -> bool { + match runtime_io::secp256k1_ecdsa_recover_compressed(self.as_ref(), &runtime_io::blake2_256(msg.get())) { + Ok(pubkey) => >::as_ref(signer) == &pubkey[..], + _ => false, + } + } +} + /// Means of signature verification of an application key. pub trait AppVerify { /// Type of the signer. - type Signer; + type AccountId; /// Verify a signature. Return `true` if signature is valid for the value. - fn verify>(&self, msg: L, signer: &Self::Signer) -> bool; + fn verify>(&self, msg: L, signer: &Self::AccountId) -> bool; } impl< S: Verify::Public as app_crypto::AppPublic>::Generic> + From, T: app_crypto::Wraps + app_crypto::AppKey + app_crypto::AppSignature + AsRef + AsMut + From, -> AppVerify for T { - type Signer = ::Public; - fn verify>(&self, msg: L, signer: &Self::Signer) -> bool { +> AppVerify for T where + ::Signer: IdentifyAccount::Signer>, + <::Public as app_crypto::AppPublic>::Generic: + IdentifyAccount::Public as app_crypto::AppPublic>::Generic>, +{ + type AccountId = ::Public; + fn verify>(&self, msg: L, signer: &::Public) -> bool { use app_crypto::IsWrappedBy; let inner: &S = self.as_ref(); - let inner_pubkey = ::Generic::from_ref(&signer); + let inner_pubkey = <::Public as app_crypto::AppPublic>::Generic::from_ref(&signer); Verify::verify(inner, msg, inner_pubkey) } } @@ -151,7 +185,7 @@ pub trait Lookup { /// context. pub trait StaticLookup { /// Type to lookup from. - type Source: Codec + Clone + PartialEq + MaybeDebug; + type Source: Codec + Clone + PartialEq + Debug; /// Type to lookup into. type Target; /// Attempt a lookup. @@ -163,7 +197,7 @@ pub trait StaticLookup { /// A lookup implementation returning the input value. #[derive(Default)] pub struct IdentityLookup(PhantomData); -impl StaticLookup for IdentityLookup { +impl StaticLookup for IdentityLookup { type Source = T; type Target = T; fn lookup(x: T) -> Result { Ok(x) } @@ -198,120 +232,6 @@ impl> Convert for ConvertInto { fn convert(a: A) -> B { a.into() } } -/// 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: - 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 -{} -impl + 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 {} - -/// Just like `From` except that if the source value is too big to fit into the destination type -/// then it'll saturate the destination. -pub trait UniqueSaturatedFrom: Sized { - /// Convert from a value of `T` into an equivalent instance of `Self`. - fn unique_saturated_from(t: T) -> Self; -} - -/// Just like `Into` except that if the source value is too big to fit into the destination type -/// then it'll saturate the destination. -pub trait UniqueSaturatedInto: Sized { - /// Consume self to return an equivalent value of `T`. - fn unique_saturated_into(self) -> T; -} - -impl + Bounded + Sized> UniqueSaturatedFrom for S { - fn unique_saturated_from(t: T) -> Self { - S::try_from(t).unwrap_or_else(|_| Bounded::max_value()) - } -} - -impl + Sized> UniqueSaturatedInto for S { - fn unique_saturated_into(self) -> T { - self.try_into().unwrap_or_else(|_| Bounded::max_value()) - } -} - -/// Simple trait to use checked mul and max value to give a saturated mul operation over -/// supported types. -pub trait Saturating { - /// Saturated addition - if the product can't fit in the type then just use max-value. - fn saturating_add(self, o: Self) -> Self; - - /// Saturated subtraction - if the product can't fit in the type then just use max-value. - fn saturating_sub(self, o: Self) -> Self; - - /// Saturated multiply - if the product can't fit in the type then just use max-value. - fn saturating_mul(self, o: Self) -> Self; -} - -impl Saturating for T { - fn saturating_add(self, o: Self) -> Self { - ::saturating_add(self, o) - } - fn saturating_sub(self, o: Self) -> Self { - ::saturating_sub(self, o) - } - fn saturating_mul(self, o: Self) -> Self { - self.checked_mul(&o).unwrap_or_else(Bounded::max_value) - } -} - -/// Convenience type to work around the highly unergonomic syntax needed -/// to invoke the functions of overloaded generic traits, in this case -/// `SaturatedFrom` and `SaturatedInto`. -pub trait SaturatedConversion { - /// Convert from a value of `T` into an equivalent instance of `Self`. - /// - /// This just uses `UniqueSaturatedFrom` internally but with this - /// variant you can provide the destination type using turbofish syntax - /// in case Rust happens not to assume the correct type. - fn saturated_from(t: T) -> Self where Self: UniqueSaturatedFrom { - >::unique_saturated_from(t) - } - - /// Consume self to return an equivalent value of `T`. - /// - /// This just uses `UniqueSaturatedInto` internally but with this - /// variant you can provide the destination type using turbofish syntax - /// in case Rust happens not to assume the correct type. - fn saturated_into(self) -> T where Self: UniqueSaturatedInto { - >::unique_saturated_into(self) - } -} -impl SaturatedConversion for T {} - /// Convenience type to work around the highly unergonomic syntax needed /// to invoke the functions of overloaded generic traits, in this case /// `TryFrom` and `TryInto`. @@ -441,10 +361,10 @@ pub trait OffchainWorker { /// Abstraction around hashing // Stupid bug in the Rust compiler believes derived // traits must be fulfilled by all type parameters. -pub trait Hash: 'static + MaybeSerializeDebug + Clone + Eq + PartialEq { +pub trait Hash: 'static + MaybeSerializeDeserialize + Debug + Clone + Eq + PartialEq { /// The hash type produced. - type Output: Member + MaybeSerializeDebug + rstd::hash::Hash + AsRef<[u8]> + AsMut<[u8]> + Copy - + Default + Encode + Decode; + type Output: Member + MaybeSerializeDeserialize + Debug + rstd::hash::Hash + + AsRef<[u8]> + AsMut<[u8]> + Copy + Default + Encode + Decode; /// The associated hash_db Hasher type. type Hasher: Hasher; @@ -471,8 +391,8 @@ pub trait Hash: 'static + MaybeSerializeDebug + Clone + Eq + PartialEq { } /// Blake2-256 Hash implementation. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(PartialEq, Eq, Clone, primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct BlakeTwo256; impl Hash for BlakeTwo256 { @@ -528,7 +448,7 @@ impl CheckEqual for primitives::H256 { } } -impl CheckEqual for super::generic::DigestItem where H: Encode { +impl CheckEqual for super::generic::DigestItem where H: Encode { #[cfg(feature = "std")] fn check_equal(&self, other: &Self) { if self != other { @@ -565,23 +485,17 @@ macro_rules! impl_maybe_marker { } impl_maybe_marker!( - /// A type that implements Debug when in std environment. - MaybeDebug: Debug; - /// A type that implements Display when in std environment. MaybeDisplay: Display; /// A type that implements Hash when in std environment. - MaybeHash: ::rstd::hash::Hash; + MaybeHash: rstd::hash::Hash; /// A type that implements Serialize when in std environment. MaybeSerialize: Serialize; /// A type that implements Serialize, DeserializeOwned and Debug when in std environment. - MaybeSerializeDebug: Debug, DeserializeOwned, Serialize; - - /// A type that implements Serialize and Debug when in std environment. - MaybeSerializeDebugButNotDeserialize: Debug, Serialize + MaybeSerializeDeserialize: DeserializeOwned, Serialize ); /// A type that provides a randomness beacon. @@ -601,8 +515,8 @@ pub trait RandomnessBeacon { } /// A type that can be used in runtime structures. -pub trait Member: Send + Sync + Sized + MaybeDebug + Eq + PartialEq + Clone + 'static {} -impl Member for T {} +pub trait Member: Send + Sync + Sized + Debug + Eq + PartialEq + Clone + 'static {} +impl Member for T {} /// Determine if a `MemberId` is a valid member. pub trait IsMember { @@ -611,15 +525,17 @@ pub trait IsMember { } /// Something which fulfills the abstract idea of a Substrate header. It has types for a `Number`, -/// a `Hash` and a `Digest`. It provides access to an `extrinsics_root`, `state_root` and +/// a `Hash` and a `Hashing`. It provides access to an `extrinsics_root`, `state_root` and /// `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 + MaybeSerializeDebugButNotDeserialize + 'static { +pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 'static { /// Header number. - type Number: Member + MaybeSerializeDebug + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec; + type Number: Member + MaybeSerializeDeserialize + Debug + rstd::hash::Hash + + Copy + MaybeDisplay + SimpleArithmetic + Codec; /// Header hash type - type Hash: Member + MaybeSerializeDebug + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>; + type Hash: Member + MaybeSerializeDeserialize + Debug + rstd::hash::Hash + + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>; /// Hashing algorithm type Hashing: Hash; @@ -667,13 +583,14 @@ pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebugButNotDe /// `Extrinsic` piece 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 + MaybeSerializeDebugButNotDeserialize + 'static { +pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 'static { /// Type of extrinsics. type Extrinsic: Member + Codec + Extrinsic + MaybeSerialize; /// Header type. type Header: Header; /// Block hash type. - type Hash: Member + MaybeSerializeDebug + ::rstd::hash::Hash + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>; + type Hash: Member + MaybeSerializeDeserialize + Debug + rstd::hash::Hash + + Copy + MaybeDisplay + Default + SimpleBitOps + Codec + AsRef<[u8]> + AsMut<[u8]>; /// Returns a reference to the header. fn header(&self) -> &Self::Header; @@ -687,6 +604,8 @@ pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebugButNotDes fn hash(&self) -> Self::Hash { <::Hashing as Hash>::hash_of(self.header()) } + /// Create an encoded block from the given `header` and `extrinsics` without requiring to create an instance. + fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec; } /// Something that acts like an `Extrinsic`. @@ -777,7 +696,7 @@ 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 + MaybeDebug + Sync + Send + Clone + Eq + PartialEq { +pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq { /// The type which encodes the sender identity. type AccountId; @@ -1063,11 +982,11 @@ pub trait ValidateUnsigned { /// Opaque datatype that may be destructured into a series of raw byte slices (which represent /// individual keys). pub trait OpaqueKeys: Clone { - /// An iterator over the type IDs of keys that this holds. - type KeyTypeIds: IntoIterator; + /// Types bound to this opaque keys that provide the key type ids returned. + type KeyTypeIdProviders; - /// Return an iterator over the key-type IDs supported by this set. - fn key_ids() -> Self::KeyTypeIds; + /// Return the key-type IDs supported by this set. + fn key_ids() -> &'static [crate::KeyTypeId]; /// Get the raw bytes of key with key-type ID `i`. fn get_raw(&self, i: super::KeyTypeId) -> &[u8]; /// Get the decoded key with index `i`. @@ -1123,12 +1042,6 @@ pub trait AccountIdConversion: Sized { fn try_from_sub_account(x: &AccountId) -> Option<(Self, S)>; } -/// Provide a simple 4 byte identifier for a type. -pub trait TypeId { - /// Simple 4 byte identifier. - const TYPE_ID: [u8; 4]; -} - /// Format is TYPE_ID ++ encode(parachain ID) ++ 00.... where 00... is indefinite trailing zeroes to /// fill AccountId. impl AccountIdConversion for Id { @@ -1173,22 +1086,25 @@ macro_rules! count { } /// Implement `OpaqueKeys` for a described struct. -/// Would be much nicer for this to be converted to `derive` code. /// -/// Every field type must be equivalent implement `as_ref()`, which is expected -/// to hold the standard SCALE-encoded form of that key. This is typically -/// just the bytes of the key. +/// Every field type must implement [`BoundToRuntimeAppPublic`](crate::BoundToRuntimeAppPublic). +/// `KeyTypeIdProviders` is set to the types given as fields. /// /// ```rust -/// use sr_primitives::{impl_opaque_keys, KeyTypeId, app_crypto::{sr25519, ed25519}}; -/// use primitives::testing::{SR25519, ED25519}; +/// use sr_primitives::{ +/// impl_opaque_keys, KeyTypeId, BoundToRuntimeAppPublic, app_crypto::{sr25519, ed25519} +/// }; +/// +/// pub struct KeyModule; +/// impl BoundToRuntimeAppPublic for KeyModule { type Public = ed25519::AppPublic; } +/// +/// pub struct KeyModule2; +/// impl BoundToRuntimeAppPublic for KeyModule2 { type Public = sr25519::AppPublic; } /// /// impl_opaque_keys! { /// pub struct Keys { -/// #[id(ED25519)] -/// pub ed25519: ed25519::AppPublic, -/// #[id(SR25519)] -/// pub sr25519: sr25519::AppPublic, +/// pub key_module: KeyModule, +/// pub key_module2: KeyModule2, /// } /// } /// ``` @@ -1197,16 +1113,20 @@ macro_rules! impl_opaque_keys { ( pub struct $name:ident { $( - #[id($key_id:expr)] pub $field:ident: $type:ty, )* } ) => { - #[derive(Default, Clone, PartialEq, Eq, $crate::codec::Encode, $crate::codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug, $crate::serde::Serialize, $crate::serde::Deserialize))] + #[derive( + Default, Clone, PartialEq, Eq, + $crate::codec::Encode, + $crate::codec::Decode, + $crate::RuntimeDebug, + )] + #[cfg_attr(feature = "std", derive($crate::serde::Serialize, $crate::serde::Deserialize))] pub struct $name { $( - pub $field: $type, + pub $field: <$type as $crate::BoundToRuntimeAppPublic>::Public, )* } @@ -1219,7 +1139,11 @@ macro_rules! impl_opaque_keys { pub fn generate(seed: Option<&str>) -> $crate::rstd::vec::Vec { let keys = Self{ $( - $field: <$type as $crate::app_crypto::RuntimeAppPublic>::generate_pair(seed), + $field: < + < + $type as $crate::BoundToRuntimeAppPublic + >::Public as $crate::RuntimeAppPublic + >::generate_pair(seed), )* }; $crate::codec::Encode::encode(&keys) @@ -1227,17 +1151,30 @@ macro_rules! impl_opaque_keys { } impl $crate::traits::OpaqueKeys for $name { - type KeyTypeIds = $crate::rstd::iter::Cloned< - $crate::rstd::slice::Iter<'static, $crate::KeyTypeId> - >; + type KeyTypeIdProviders = ( $( $type, )* ); - fn key_ids() -> Self::KeyTypeIds { - [ $($key_id),* ].iter().cloned() + fn key_ids() -> &'static [$crate::KeyTypeId] { + &[ + $( + < + < + $type as $crate::BoundToRuntimeAppPublic + >::Public as $crate::RuntimeAppPublic + >::ID + ),* + ] } fn get_raw(&self, i: $crate::KeyTypeId) -> &[u8] { match i { - $( i if i == $key_id => self.$field.as_ref(), )* + $( + i if i == < + < + $type as $crate::BoundToRuntimeAppPublic + >::Public as $crate::RuntimeAppPublic + >::ID => + self.$field.as_ref(), + )* _ => &[], } } @@ -1253,7 +1190,25 @@ pub trait Printable { impl Printable for u8 { fn print(&self) { - u64::from(*self).print() + (*self as u64).print() + } +} + +impl Printable for u32 { + fn print(&self) { + (*self as u64).print() + } +} + +impl Printable for usize { + fn print(&self) { + (*self as u64).print() + } +} + +impl Printable for u64 { + fn print(&self) { + runtime_io::print_num(*self); } } @@ -1269,9 +1224,10 @@ impl Printable for &str { } } -impl Printable for u64 { +#[impl_for_tuples(1, 12)] +impl Printable for Tuple { fn print(&self) { - runtime_io::print_num(*self); + for_tuples!( #( Tuple.print(); )* ) } } @@ -1280,6 +1236,21 @@ mod tests { use super::AccountIdConversion; use crate::codec::{Encode, Decode, Input}; + mod t { + use primitives::crypto::KeyTypeId; + use app_crypto::{app_crypto, sr25519}; + app_crypto!(sr25519, KeyTypeId(*b"test")); + } + + #[test] + fn app_verify_works() { + use t::*; + use super::AppVerify; + + let s = Signature::default(); + let _ = s.verify(&[0u8; 100][..], &Public::default()); + } + #[derive(Encode, Decode, Default, PartialEq, Debug)] struct U32Value(u32); impl super::TypeId for U32Value { diff --git a/core/sr-primitives/src/transaction_validity.rs b/core/sr-primitives/src/transaction_validity.rs index eb6cf3bdbb00ec17e6449a4a642ac50814e0c884..3e765215b979a950d9b37a083415b24f56c09766 100644 --- a/core/sr-primitives/src/transaction_validity.rs +++ b/core/sr-primitives/src/transaction_validity.rs @@ -18,6 +18,7 @@ use rstd::prelude::*; use crate::codec::{Encode, Decode}; +use crate::RuntimeDebug; /// Priority for a transaction. Additive. Higher is better. pub type TransactionPriority = u64; @@ -30,8 +31,8 @@ pub type TransactionLongevity = u64; pub type TransactionTag = Vec; /// An invalid transaction validity. -#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy)] -#[cfg_attr(feature = "std", derive(Debug, serde::Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(serde::Serialize))] pub enum InvalidTransaction { /// The call of the transaction is not expected. Call, @@ -81,8 +82,8 @@ impl From for &'static str { } /// An unknown transaction validity. -#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy)] -#[cfg_attr(feature = "std", derive(Debug, serde::Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(serde::Serialize))] pub enum UnknownTransaction { /// Could not lookup some information that is required to validate the transaction. CannotLookup, @@ -105,8 +106,8 @@ impl From for &'static str { } /// Errors that can occur while checking the validity of a transaction. -#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy)] -#[cfg_attr(feature = "std", derive(Debug, serde::Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(serde::Serialize))] pub enum TransactionValidityError { /// The transaction is invalid. Invalid(InvalidTransaction), @@ -173,8 +174,7 @@ impl Into for UnknownTransaction { } /// Information concerning a valid transaction. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] pub struct ValidTransaction { /// Priority of the transaction. /// diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index 28f1b2fe0cfc4ebcb54f842111b84559f31a02c7..088f13244eb31d3943719f82a8f58492b8db834c 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -14,28 +14,58 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Primitives for transaction weighting. +//! # Primitives for transaction weighting. //! -//! Each dispatch function within `decl_module!` can have an optional `#[weight = $x]` attribute. -//! `$x` can be any type that implements the `ClassifyDispatch` and `WeighData` traits. By -//! default, All transactions are annotated with `#[weight = SimpleDispatchInfo::default()]`. +//! All dispatchable functions defined in `decl_module!` must provide two trait implementations: +//! - [`WeightData`]: To determine the weight of the dispatch. +//! - [`ClassifyDispatch`]: To determine the class of the dispatch. See the enum definition for +//! more information on dispatch classes. +//! +//! Every dispatchable function is responsible for providing this data via an optional `#[weight = +//! $x]` attribute. In this snipped, `$x` can be any user provided struct that implements the +//! two aforementioned traits. +//! +//! Substrate then bundles then output information of the two traits into [`DispatchInfo`] struct +//! and provides it by implementing the [`GetDispatchInfo`] for all `Call` variants, and opaque +//! extrinsic types. +//! +//! If no `#[weight]` is defined, the macro automatically injects the `Default` implementation of +//! the [`SimpleDispatchInfo`]. //! //! Note that the decl_module macro _cannot_ enforce this and will simply fail if an invalid struct //! (something that does not implement `Weighable`) is passed in. -use crate::{Fixed64, traits::Saturating}; -use crate::codec::{Encode, Decode}; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; +use codec::{Encode, Decode}; +use arithmetic::traits::Bounded; +use crate::RuntimeDebug; +/// Re-export priority as type pub use crate::transaction_validity::TransactionPriority; -use crate::traits::Bounded; /// Numeric range of a transaction weight. pub type Weight = u32; +/// Means of weighing some particular kind of data (`T`). +pub trait WeighData { + /// Weigh the data `T` given by `target`. When implementing this for a dispatchable, `T` will be + /// a tuple of all arguments given to the function (except origin). + fn weigh_data(&self, target: T) -> Weight; +} + +/// Means of classifying a dispatchable function. +pub trait ClassifyDispatch { + /// Classify the dispatch function based on input data `target` of type `T`. When implementing + /// this for a dispatchable, `T` will be a tuple of all arguments given to the function (except + /// origin). + fn classify_dispatch(&self, target: T) -> DispatchClass; +} + /// A generalized group of dispatch types. This is only distinguishing normal, user-triggered transactions /// (`Normal`) and anything beyond which serves a higher purpose to the system (`Operational`). -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] pub enum DispatchClass { /// A normal dispatch. Normal, @@ -64,8 +94,8 @@ impl From for DispatchClass { } /// A bundle of static information collected from the `#[weight = $x]` attributes. -#[cfg_attr(feature = "std", derive(PartialEq, Eq, Debug))] -#[derive(Clone, Copy, Default)] +#[cfg_attr(feature = "std", derive(PartialEq, Eq))] +#[derive(Clone, Copy, Default, RuntimeDebug)] pub struct DispatchInfo { /// Weight of this transaction. pub weight: Weight, @@ -84,8 +114,8 @@ impl DispatchInfo { } } -/// A `Dispatchable` function (aka transaction) that can carry some static information along with it, using the -/// `#[weight]` attribute. +/// A `Dispatchable` function (aka transaction) that can carry some static information along with +/// it, using the `#[weight]` attribute. pub trait GetDispatchInfo { /// Return a `DispatchInfo`, containing relevant information of this dispatch. /// @@ -93,18 +123,6 @@ pub trait GetDispatchInfo { fn get_dispatch_info(&self) -> DispatchInfo; } -/// Means of weighing some particular kind of data (`T`). -pub trait WeighData { - /// Weigh the data `T` given by `target`. - fn weigh_data(&self, target: T) -> Weight; -} - -/// Means of classifying a dispatchable function. -pub trait ClassifyDispatch { - /// Classify the dispatch function based on input data `target` of type `T`. - fn classify_dispatch(&self, target: T) -> DispatchClass; -} - /// Default type used with the `#[weight = x]` attribute in a substrate chain. /// /// A user may pass in any other type that implements the correct traits. If not, the `Default` @@ -116,13 +134,9 @@ pub trait ClassifyDispatch { /// - A `Free` variant is equal to `::Fixed(0)`. Note that this does not guarantee inclusion. /// - A `Max` variant is equal to `::Fixed(Weight::max_value())`. /// -/// Based on the final weight value, based on the above variants: -/// - A _weight-fee_ is deducted. -/// - The block weight is consumed proportionally. -/// /// As for the generalized groups themselves: /// - `Normal` variants will be assigned a priority proportional to their weight. They can only -/// consume a portion (1/4) of the maximum block resource limits. +/// consume a portion (defined in the system module) of the maximum block resource limits. /// - `Operational` variants will be assigned the maximum priority. They can potentially consume /// the entire block resource limit. #[derive(Clone, Copy)] @@ -167,76 +181,3 @@ impl Default for SimpleDispatchInfo { SimpleDispatchInfo::FixedNormal(10_000) } } - -/// Representation of a weight multiplier. This represents how a fee value can be computed from a -/// weighted transaction. -/// -/// This is basically a wrapper for the `Fixed64` type a slightly tailored multiplication to u32 -/// in the form of the `apply_to` method. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct WeightMultiplier(Fixed64); - -impl WeightMultiplier { - /// Apply the inner Fixed64 as a weight multiplier to a weight value. - /// - /// This will perform a saturated `weight + weight * self.0`. - pub fn apply_to(&self, weight: Weight) -> Weight { - self.0.saturated_multiply_accumulate(weight) - } - - /// build self from raw parts per billion. - #[cfg(feature = "std")] - pub fn from_parts(parts: i64) -> Self { - Self(Fixed64::from_parts(parts)) - } - - /// build self from a fixed64 value. - pub fn from_fixed(f: Fixed64) -> Self { - Self(f) - } - - /// Approximate the fraction `n/d`. - pub fn from_rational(n: i64, d: u64) -> Self { - Self(Fixed64::from_rational(n, d)) - } -} - -impl Saturating for WeightMultiplier { - fn saturating_add(self, rhs: Self) -> Self { - Self(self.0.saturating_add(rhs.0)) - } - fn saturating_mul(self, rhs: Self) -> Self { - Self(self.0.saturating_mul(rhs.0)) - - } - fn saturating_sub(self, rhs: Self) -> Self { - Self(self.0.saturating_sub(rhs.0)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn multiplier_apply_to_works() { - let test_set = vec![0, 1, 10, 1000, 1_000_000_000]; - - // negative (1/2) - let mut fm = WeightMultiplier::from_rational(-1, 2); - test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i) as i32, i as i32 - i as i32 / 2); }); - - // unit (1) multiplier - fm = WeightMultiplier::from_parts(0); - test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i), i); }); - - // i.5 multiplier - fm = WeightMultiplier::from_rational(1, 2); - test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i), i * 3 / 2); }); - - // dual multiplier - fm = WeightMultiplier::from_rational(1, 1); - test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i), i * 2); }); - } -} diff --git a/core/sr-sandbox/src/lib.rs b/core/sr-sandbox/src/lib.rs index e814a51acedf692b5df4082db9fce73408d5308e..c9f9135661586d034f1722ad30e822ac2eebc1c0 100755 --- a/core/sr-sandbox/src/lib.rs +++ b/core/sr-sandbox/src/lib.rs @@ -51,7 +51,7 @@ mod imp { } /// Error that can occur while using this crate. -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(primitives::RuntimeDebug)] pub enum Error { /// Module is not valid, couldn't be instantiated. Module, @@ -171,7 +171,7 @@ pub struct Instance { impl Instance { /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. It will - /// run the `start` function with the given `state`. + /// run the `start` function (if it is present in the module) with the given `state`. /// /// Returns `Err(Error::Module)` if this module can't be instantiated with the given /// environment. If execution of `start` function generated a trap, then `Err(Error::Execution)` will diff --git a/core/sr-staking-primitives/src/offence.rs b/core/sr-staking-primitives/src/offence.rs index c076103c18332841851be65a66faf5b444acaf63..db51f75df1bf3c973c8989446404b0fb8178182e 100644 --- a/core/sr-staking-primitives/src/offence.rs +++ b/core/sr-staking-primitives/src/offence.rs @@ -131,8 +131,7 @@ impl OnOffenceHandler for () { } /// A details about an offending authority for a particular kind of offence. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, sr_primitives::RuntimeDebug)] pub struct OffenceDetails { /// The offending authority id pub offender: Offender, diff --git a/core/sr-std/without_std.rs b/core/sr-std/without_std.rs index ed9eea6c31b74531670188004cfaacf555c9706c..9762c74367198fba65520e9a5b37ce0c9a192134 100755 --- a/core/sr-std/without_std.rs +++ b/core/sr-std/without_std.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -#[cfg(feature = "nightly")] #[doc(hidden)] pub extern crate alloc; @@ -54,6 +53,7 @@ pub use core::clone; pub use core::cmp; pub use core::convert; pub use core::default; +pub use core::fmt; pub use core::hash; pub use core::intrinsics; pub use core::iter; diff --git a/core/sr-version/Cargo.toml b/core/sr-version/Cargo.toml index fcae97b4d2a7aa289b90366cc37defcb6e5d17b1..5be3048f827b4351d7fbc1b091d0851aef7d7ac2 100644 --- a/core/sr-version/Cargo.toml +++ b/core/sr-version/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -impl-serde = { version = "0.2.1", optional = true } +impl-serde = { version = "0.2.3", optional = true } serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.0.5", default-features = false, features = ["derive"] } rstd = { package = "sr-std", path = "../sr-std", default-features = false } diff --git a/core/sr-version/src/lib.rs b/core/sr-version/src/lib.rs index ca98f3c2210fedfd6f39c395d103eb828125b410..24c54a739a2e0489a17aff64f84281fc280ebf1f 100644 --- a/core/sr-version/src/lib.rs +++ b/core/sr-version/src/lib.rs @@ -62,8 +62,8 @@ macro_rules! create_apis_vec { /// This triplet have different semantics and mis-interpretation could cause problems. /// In particular: bug fixes should result in an increment of `spec_version` and possibly `authoring_version`, /// absolutely not `impl_version` since they change the semantics of the runtime. -#[derive(Clone, PartialEq, Eq, Encode, Default)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize, Decode))] +#[derive(Clone, PartialEq, Eq, Encode, Default, sr_primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Decode))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct RuntimeVersion { /// Identifies the different Substrate runtimes. There'll be at least polkadot and node. @@ -147,7 +147,7 @@ impl RuntimeVersion { } #[cfg(feature = "std")] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Debug)] pub struct NativeVersion { /// Basic runtime version info. pub runtime_version: RuntimeVersion, diff --git a/core/state-db/Cargo.toml b/core/state-db/Cargo.toml index 86298ae909101ae843963ed45825b33428effb5e..d271a0e179d6d3eee5994b13ac1de5bd9ed4038c 100644 --- a/core/state-db/Cargo.toml +++ b/core/state-db/Cargo.toml @@ -11,4 +11,4 @@ primitives = { package = "substrate-primitives", path = "../../core/primitives" codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } [dev-dependencies] -env_logger = "0.6.2" +env_logger = "0.7.0" diff --git a/core/state-db/src/lib.rs b/core/state-db/src/lib.rs index 81772e554bc572849a79d257a1e9a420b4b2644d..e561d9ce9617cb4c4edf92270160805bd151e1b8 100644 --- a/core/state-db/src/lib.rs +++ b/core/state-db/src/lib.rs @@ -41,6 +41,11 @@ use noncanonical::NonCanonicalOverlay; use pruning::RefWindow; use log::trace; +const PRUNING_MODE: &[u8] = b"mode"; +const PRUNING_MODE_ARCHIVE: &[u8] = b"archive"; +const PRUNING_MODE_ARCHIVE_CANON: &[u8] = b"archive_canonical"; +const PRUNING_MODE_CONSTRAINED: &[u8] = b"constrained"; + /// Database value type. pub type DBValue = Vec; @@ -77,6 +82,8 @@ pub enum Error { InvalidBlockNumber, /// Trying to insert block with unknown parent. InvalidParent, + /// Invalid pruning mode specified. Contains expected mode. + InvalidPruningMode(String), } /// Pinning error type. @@ -99,6 +106,7 @@ impl fmt::Debug for Error { 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"), + Error::InvalidPruningMode(e) => write!(f, "Expected pruning mode: {}", e), } } } @@ -159,6 +167,14 @@ impl PruningMode { } } + /// Is this an archive (either ArchiveAll or ArchiveCanonical) pruning mode? + pub fn id(&self) -> &[u8] { + match self { + PruningMode::ArchiveAll => PRUNING_MODE_ARCHIVE, + PruningMode::ArchiveCanonical => PRUNING_MODE_ARCHIVE_CANON, + PruningMode::Constrained(_) => PRUNING_MODE_CONSTRAINED, + } + } } impl Default for PruningMode { @@ -183,6 +199,10 @@ struct StateDbSync { impl StateDbSync { pub fn new(mode: PruningMode, db: &D) -> Result, Error> { trace!(target: "state-db", "StateDb settings: {:?}", mode); + + // Check that settings match + Self::check_meta(&mode, db)?; + let non_canonical: NonCanonicalOverlay = NonCanonicalOverlay::new(db)?; let pruning: Option> = match mode { PruningMode::Constrained(Constraints { @@ -192,6 +212,7 @@ impl StateDbSync { PruningMode::Constrained(_) => Some(RefWindow::new(db)?), PruningMode::ArchiveAll | PruningMode::ArchiveCanonical => None, }; + Ok(StateDbSync { mode, non_canonical, @@ -200,18 +221,41 @@ impl StateDbSync { }) } + fn check_meta(mode: &PruningMode, db: &D) -> Result<(), Error> { + let db_mode = db.get_meta(&to_meta_key(PRUNING_MODE, &())).map_err(Error::Db)?; + trace!(target: "state-db", + "DB pruning mode: {:?}", + db_mode.as_ref().map(|v| std::str::from_utf8(&v)) + ); + match &db_mode { + Some(v) if v.as_slice() == mode.id() => Ok(()), + Some(v) => Err(Error::InvalidPruningMode(String::from_utf8_lossy(v).into())), + None => Ok(()), + } + } + pub fn insert_block(&mut self, hash: &BlockHash, number: u64, parent_hash: &BlockHash, mut changeset: ChangeSet) -> Result, Error> { + let mut meta = ChangeSet::default(); + if number == 0 { + // Save pruning mode when writing first block. + meta.inserted.push((to_meta_key(PRUNING_MODE, &()), self.mode.id().into())); + } + match self.mode { PruningMode::ArchiveAll => { changeset.deleted.clear(); // write changes immediately Ok(CommitSet { data: changeset, - meta: Default::default(), + meta: meta, }) }, PruningMode::Constrained(_) | PruningMode::ArchiveCanonical => { - self.non_canonical.insert(hash, number, parent_hash, changeset) + let commit = self.non_canonical.insert(hash, number, parent_hash, changeset); + commit.map(|mut c| { + c.meta.inserted.extend(meta.inserted); + c + }) } } } @@ -544,4 +588,23 @@ mod tests { assert!(sdb.is_pruned(&H256::from_low_u64_be(22), 2)); assert!(db.data_eq(&make_db(&[1, 21, 3, 921, 922, 93, 94]))); } + + #[test] + fn detects_incompatible_mode() { + let mut db = make_db(&[]); + let state_db = StateDb::new(PruningMode::ArchiveAll, &db).unwrap(); + db.commit( + &state_db + .insert_block::( + &H256::from_low_u64_be(0), + 0, + &H256::from_low_u64_be(0), + make_changeset(&[], &[]), + ) + .unwrap(), + ); + let new_mode = PruningMode::Constrained(Constraints { max_blocks: Some(2), max_mem: None }); + let state_db: Result, _> = StateDb::new(new_mode, &db); + assert!(state_db.is_err()); + } } diff --git a/core/state-machine/Cargo.toml b/core/state-machine/Cargo.toml index 914fa04db821490984574fd2c360a616a2831650..7cd8601a3b636b281309b2ae0256fb1def3ddfcb 100644 --- a/core/state-machine/Cargo.toml +++ b/core/state-machine/Cargo.toml @@ -17,6 +17,7 @@ panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" codec = { package = "parity-scale-codec", version = "1.0.0" } num-traits = "0.2.8" rand = "0.7.2" +externalities = { package = "substrate-externalities", path = "../externalities" } [dev-dependencies] hex-literal = "0.2.1" diff --git a/core/state-machine/src/basic.rs b/core/state-machine/src/basic.rs index e45af45c9f80b058a409b76833db1d49b36b8272..c2d1a0e3950d035a6de4bea3cc6c71b6985dff09 100644 --- a/core/state-machine/src/basic.rs +++ b/core/state-machine/src/basic.rs @@ -16,15 +16,14 @@ //! Basic implementation for Externalities. -use std::collections::HashMap; -use std::iter::FromIterator; +use std::{collections::HashMap, any::{TypeId, Any}, iter::FromIterator}; use crate::backend::{Backend, InMemory}; use hash_db::Hasher; use trie::{TrieConfiguration, default_child_trie_root}; use trie::trie_types::Layout; use primitives::{ - storage::well_known_keys::is_child_storage_key, child_storage_key::ChildStorageKey, offchain, - traits::Externalities, + storage::{well_known_keys::is_child_storage_key, ChildStorageKey}, + traits::Externalities, Blake2Hasher, hash::H256, }; use log::warn; @@ -88,21 +87,37 @@ impl From, Vec>> for BasicExternalities { } } -impl Externalities for BasicExternalities where H::Out: Ord { +impl Externalities for BasicExternalities { fn storage(&self, key: &[u8]) -> Option> { self.top.get(key).cloned() } + fn storage_hash(&self, key: &[u8]) -> Option { + self.storage(key).map(|v| Blake2Hasher::hash(&v)) + } + fn original_storage(&self, key: &[u8]) -> Option> { - Externalities::::storage(self, key) + self.storage(key) + } + + fn original_storage_hash(&self, key: &[u8]) -> Option { + self.storage_hash(key) } fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { self.children.get(storage_key.as_ref()).and_then(|child| child.get(key)).cloned() } + fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { + self.child_storage(storage_key, key).map(|v| Blake2Hasher::hash(&v)) + } + + fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { + self.child_storage_hash(storage_key, key) + } + fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { - Externalities::::child_storage(self, storage_key, key) + Externalities::child_storage(self, storage_key, key) } fn place_storage(&mut self, key: Vec, maybe_value: Option>) { @@ -155,16 +170,15 @@ impl Externalities for BasicExternalities where H::Out: Ord { fn chain_id(&self) -> u64 { 42 } - fn storage_root(&mut self) -> H::Out { + fn storage_root(&mut self) -> H256 { let mut top = self.top.clone(); let keys: Vec<_> = self.children.keys().map(|k| k.to_vec()).collect(); // Single child trie implementation currently allows using the same child // empty root for all child trie. Using null storage key until multiple // type of child trie support. - let empty_hash = default_child_trie_root::>(&[]); + let empty_hash = default_child_trie_root::>(&[]); for storage_key in keys { - let child_root = Externalities::::child_storage_root( - self, + let child_root = self.child_storage_root( ChildStorageKey::from_slice(storage_key.as_slice()) .expect("Map only feed by valid keys; qed"), ); @@ -175,30 +189,27 @@ impl Externalities for BasicExternalities where H::Out: Ord { } } - Layout::::trie_root(self.top.clone()) + Layout::::trie_root(self.top.clone()) } fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { if let Some(child) = self.children.get(storage_key.as_ref()) { let delta = child.clone().into_iter().map(|(k, v)| (k, Some(v))); - InMemory::::default().child_storage_root(storage_key.as_ref(), delta).0 + InMemory::::default().child_storage_root(storage_key.as_ref(), delta).0 } else { - default_child_trie_root::>(storage_key.as_ref()) + default_child_trie_root::>(storage_key.as_ref()) } } - fn storage_changes_root(&mut self, _parent: H::Out) -> Result, ()> { + fn storage_changes_root(&mut self, _parent: H256) -> Result, ()> { Ok(None) } +} - fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { - warn!("Call to non-existent offchain externalities set."); - None - } - - fn keystore(&self) -> Option { - warn!("Call to non-existent keystore."); +impl externalities::ExtensionStore for BasicExternalities { + fn extension_by_type_id(&mut self, _: TypeId) -> Option<&mut dyn Any> { + warn!("Extensions are not supported by `BasicExternalities`."); None } } @@ -206,14 +217,13 @@ impl Externalities for BasicExternalities where H::Out: Ord { #[cfg(test)] mod tests { use super::*; - use primitives::{Blake2Hasher, H256, map}; + use primitives::{H256, map}; use primitives::storage::well_known_keys::CODE; use hex_literal::hex; #[test] fn commit_should_work() { let mut ext = BasicExternalities::default(); - let ext = &mut ext as &mut dyn Externalities; ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); @@ -225,7 +235,6 @@ mod tests { #[test] fn set_and_retrieve_code() { let mut ext = BasicExternalities::default(); - let ext = &mut ext as &mut dyn Externalities; let code = vec![1, 2, 3]; ext.set_storage(CODE.to_vec(), code.clone()); @@ -246,8 +255,6 @@ mod tests { ] ); - let ext = &mut ext as &mut dyn Externalities; - let child = || ChildStorageKey::from_vec(child_storage.clone()).unwrap(); assert_eq!(ext.child_storage(child(), b"doe"), Some(b"reindeer".to_vec())); diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs index c4a2bd7f63b2166ba5973d2fc872679203e6ec1c..0e93302a95a547edf66cd498af701677642b5531 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -16,22 +16,23 @@ //! Concrete externalities implementation. -use std::{error, fmt, cmp::Ord}; -use log::{warn, trace}; use crate::{ backend::Backend, OverlayedChanges, changes_trie::{ Storage as ChangesTrieStorage, CacheAction as ChangesTrieCacheAction, build_changes_trie, }, }; + use hash_db::Hasher; use primitives::{ - offchain, storage::well_known_keys::is_child_storage_key, - traits::{BareCryptoStorePtr, Externalities}, child_storage_key::ChildStorageKey, - hexdisplay::HexDisplay, + storage::{ChildStorageKey, well_known_keys::is_child_storage_key}, + traits::Externalities, hexdisplay::HexDisplay, hash::H256, }; -use trie::{MemoryDB, default_child_trie_root}; -use trie::trie_types::Layout; +use trie::{trie_types::Layout, MemoryDB, default_child_trie_root}; +use externalities::Extensions; + +use std::{error, fmt, any::{Any, TypeId}}; +use log::{warn, trace}; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; @@ -65,11 +66,7 @@ impl error::Error for Error { } /// Wraps a read-only backend, call executor, and current overlayed changes. -pub struct Ext<'a, H, N, B, T, O> -where - H: Hasher, - B: 'a + Backend, -{ +pub struct Ext<'a, H, N, B, T> where H: Hasher, B: 'a + Backend { /// The overlayed changes to write to. overlay: &'a mut OverlayedChanges, /// The storage backend to read from. @@ -86,34 +83,28 @@ where /// `storage_changes_root` is called matters + we need to remember additional /// data at this moment (block number). changes_trie_transaction: Option<(MemoryDB, H::Out, ChangesTrieCacheAction)>, - /// Additional externalities for offchain workers. - /// - /// If None, some methods from the trait might not be supported. - offchain_externalities: Option<&'a mut O>, - /// The keystore that manages the keys of the node. - keystore: Option, /// Pseudo-unique id used for tracing. pub id: u16, /// Dummy usage of N arg. - _phantom: ::std::marker::PhantomData, + _phantom: std::marker::PhantomData, + /// Extensions registered with this instance. + extensions: Option<&'a mut Extensions>, } -impl<'a, H, N, B, T, O> Ext<'a, H, N, B, T, O> +impl<'a, H, N, B, T> Ext<'a, H, N, B, T> where - H: Hasher, + H: Hasher, B: 'a + Backend, T: 'a + ChangesTrieStorage, - O: 'a + offchain::Externalities, - H::Out: Ord + 'static, N: crate::changes_trie::BlockNumber, { + /// Create a new `Ext` from overlayed changes and read-only backend pub fn new( overlay: &'a mut OverlayedChanges, backend: &'a B, changes_trie_storage: Option<&'a T>, - offchain_externalities: Option<&'a mut O>, - keystore: Option, + extensions: Option<&'a mut Extensions>, ) -> Self { Ext { overlay, @@ -121,21 +112,25 @@ where storage_transaction: None, changes_trie_storage, changes_trie_transaction: None, - offchain_externalities, - keystore, id: rand::random(), _phantom: Default::default(), + extensions, } } /// Get the transaction necessary to update the backend. - pub fn transaction(mut self) -> ((B::Transaction, H::Out), Option>) { + pub fn transaction(&mut self) -> ( + (B::Transaction, H256), + Option>, + ) { let _ = self.storage_root(); let (storage_transaction, changes_trie_transaction) = ( self.storage_transaction + .take() .expect("storage_transaction always set after calling storage root; qed"), self.changes_trie_transaction + .take() .map(|(tx, _, cache)| (tx, cache)), ); @@ -151,16 +146,14 @@ where fn mark_dirty(&mut self) { self.storage_transaction = None; } - } #[cfg(test)] -impl<'a, H, N, B, T, O> Ext<'a, H, N, B, T, O> +impl<'a, H, N, B, T> Ext<'a, H, N, B, T> where - H: Hasher, + H: Hasher, B: 'a + Backend, T: 'a + ChangesTrieStorage, - O: 'a + offchain::Externalities, N: crate::changes_trie::BlockNumber, { pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { @@ -177,13 +170,12 @@ where } } -impl<'a, B, T, H, N, O> Externalities for Ext<'a, H, N, B, T, O> -where H: Hasher, +impl<'a, H, B, T, N> Externalities for Ext<'a, H, N, B, T> +where + H: Hasher, B: 'a + Backend, T: 'a + ChangesTrieStorage, - H::Out: Ord + 'static, N: crate::changes_trie::BlockNumber, - O: 'a + offchain::Externalities, { fn storage(&self, key: &[u8]) -> Option> { let _guard = panic_handler::AbortGuard::force_abort(); @@ -197,10 +189,14 @@ where H: Hasher, result } - fn storage_hash(&self, key: &[u8]) -> Option { + fn storage_hash(&self, key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); - let result = self.overlay.storage(key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(|| - self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)); + let result = self.overlay + .storage(key) + .map(|x| x.map(|x| H::hash(x))) + .unwrap_or_else(|| + self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL) + ); trace!(target: "state-trace", "{:04x}: Hash {}={:?}", self.id, HexDisplay::from(&key), @@ -220,7 +216,7 @@ where H: Hasher, result } - fn original_storage_hash(&self, key: &[u8]) -> Option { + fn original_storage_hash(&self, key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); let result = self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL); trace!(target: "state-trace", "{:04x}: GetOriginalHash {}={:?}", @@ -233,33 +229,48 @@ where H: Hasher, fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { let _guard = panic_handler::AbortGuard::force_abort(); - let result = self.overlay.child_storage(storage_key.as_ref(), key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| - self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL)); + let result = self.overlay + .child_storage(storage_key.as_ref(), key) + .map(|x| x.map(|x| x.to_vec())) + .unwrap_or_else(|| + self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL) + ); + trace!(target: "state-trace", "{:04x}: GetChild({}) {}={:?}", self.id, HexDisplay::from(&storage_key.as_ref()), HexDisplay::from(&key), result.as_ref().map(HexDisplay::from) ); + result } - fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { + fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); - let result = self.overlay.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)); + let result = self.overlay + .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) + ); + trace!(target: "state-trace", "{:04x}: ChildHash({}) {}={:?}", self.id, HexDisplay::from(&storage_key.as_ref()), HexDisplay::from(&key), result, ); + result } fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { let _guard = panic_handler::AbortGuard::force_abort(); - let result = self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL); + let result = self.backend + .child_storage(storage_key.as_ref(), key) + .expect(EXT_NOT_ALLOWED_TO_FAIL); + trace!(target: "state-trace", "{:04x}: ChildOriginal({}) {}={:?}", self.id, HexDisplay::from(&storage_key.as_ref()), @@ -269,9 +280,12 @@ where H: Hasher, result } - fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { + fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option { let _guard = panic_handler::AbortGuard::force_abort(); - let result = self.backend.child_storage_hash(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL); + let result = self.backend + .child_storage_hash(storage_key.as_ref(), key) + .expect(EXT_NOT_ALLOWED_TO_FAIL); + trace!(target: "state-trace", "{}: ChildHashOriginal({}) {}={:?}", self.id, HexDisplay::from(&storage_key.as_ref()), @@ -287,6 +301,7 @@ where H: Hasher, Some(x) => x.is_some(), _ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), }; + trace!(target: "state-trace", "{:04x}: Exists {}={:?}", self.id, HexDisplay::from(&key), @@ -301,8 +316,11 @@ where H: Hasher, let result = match self.overlay.child_storage(storage_key.as_ref(), key) { Some(x) => x.is_some(), - _ => self.backend.exists_child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL), + _ => self.backend + .exists_child_storage(storage_key.as_ref(), key) + .expect(EXT_NOT_ALLOWED_TO_FAIL), }; + trace!(target: "state-trace", "{:04x}: ChildExists({}) {}={:?}", self.id, HexDisplay::from(&storage_key.as_ref()), @@ -328,7 +346,12 @@ where H: Hasher, self.overlay.set_storage(key, value); } - fn place_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec, value: Option>) { + fn place_child_storage( + &mut self, + storage_key: ChildStorageKey, + key: Vec, + value: Option>, + ) { trace!(target: "state-trace", "{:04x}: PutChild({}) {}={:?}", self.id, HexDisplay::from(&storage_key.as_ref()), @@ -392,7 +415,7 @@ where H: Hasher, 42 } - fn storage_root(&mut self) -> H::Out { + fn storage_root(&mut self) -> H256 { let _guard = panic_handler::AbortGuard::force_abort(); if let Some((_, ref root)) = self.storage_transaction { trace!(target: "state-trace", "{:04x}: Root (cached) {}", @@ -444,16 +467,22 @@ where H: Hasher, } else { let storage_key = storage_key.as_ref(); - let delta = self.overlay.committed.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k.clone(), v.value.clone())))); + let (root, is_empty, _) = { + let delta = self.overlay.committed.children.get(storage_key) + .into_iter() + .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value))) + .chain(self.overlay.prospective.children.get(storage_key) + .into_iter() + .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value)))); - let root = self.backend.child_storage_root(storage_key, delta).0; + self.backend.child_storage_root(storage_key, delta) + }; - self.overlay.set_storage(storage_key.to_vec(), Some(root.to_vec())); + if is_empty { + self.overlay.set_storage(storage_key.into(), None); + } else { + self.overlay.set_storage(storage_key.into(), Some(root.clone())); + } trace!(target: "state-trace", "{:04x}: ChildRoot({}) {}", self.id, @@ -465,7 +494,7 @@ where H: Hasher, } } - fn storage_changes_root(&mut self, parent_hash: H::Out) -> Result, ()> { + fn storage_changes_root(&mut self, parent_hash: H256) -> Result, ()> { let _guard = panic_handler::AbortGuard::force_abort(); self.changes_trie_transaction = build_changes_trie::<_, T, H, N>( self.backend, @@ -481,32 +510,36 @@ where H: Hasher, ); result } +} - fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { - self.offchain_externalities.as_mut().map(|x| &mut **x as _) - } - - fn keystore(&self) -> Option { - self.keystore.clone() +impl<'a, H, B, T, N> externalities::ExtensionStore for Ext<'a, H, N, B, T> +where + H: Hasher, + B: 'a + Backend, + T: 'a + ChangesTrieStorage, + N: crate::changes_trie::BlockNumber, +{ + fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> { + self.extensions.as_mut().and_then(|exts| exts.get_mut(type_id)) } - } #[cfg(test)] mod tests { + use super::*; use hex_literal::hex; use codec::Encode; - use primitives::{Blake2Hasher}; - use primitives::storage::well_known_keys::EXTRINSIC_INDEX; - use crate::backend::InMemory; - use crate::changes_trie::{Configuration as ChangesTrieConfiguration, - InMemoryStorage as InMemoryChangesTrieStorage}; - use crate::overlayed_changes::OverlayedValue; - use super::*; + use primitives::{Blake2Hasher, storage::well_known_keys::EXTRINSIC_INDEX}; + use crate::{ + changes_trie::{ + Configuration as ChangesTrieConfiguration, + InMemoryStorage as InMemoryChangesTrieStorage, + }, backend::InMemory, overlayed_changes::OverlayedValue, + }; type TestBackend = InMemory; type TestChangesTrieStorage = InMemoryChangesTrieStorage; - type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend, TestChangesTrieStorage, crate::NeverOffchainExt>; + type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend, TestChangesTrieStorage>; fn prepare_overlay_with_changes() -> OverlayedChanges { OverlayedChanges { @@ -532,7 +565,7 @@ mod tests { fn storage_changes_root_is_none_when_storage_is_not_provided() { let mut overlay = prepare_overlay_with_changes(); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, None, None, None); + let mut ext = TestExt::new(&mut overlay, &backend, None, None); assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), None); } @@ -542,7 +575,7 @@ mod tests { overlay.changes_trie_config = None; let storage = TestChangesTrieStorage::with_blocks(vec![(100, Default::default())]); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None); + let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None); assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), None); } @@ -551,7 +584,7 @@ mod tests { let mut overlay = prepare_overlay_with_changes(); let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None); + let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None); assert_eq!( ext.storage_changes_root(Default::default()).unwrap(), Some(hex!("bb0c2ef6e1d36d5490f9766cfcc7dfe2a6ca804504c3bb206053890d6dd02376").into()), @@ -564,7 +597,7 @@ mod tests { overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None; let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None); + let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None); assert_eq!( ext.storage_changes_root(Default::default()).unwrap(), Some(hex!("96f5aae4690e7302737b6f9b7f8567d5bbb9eac1c315f80101235a92d9ec27f4").into()), diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index 5220b4b31db81982cbd92a7c8fee25c02d9eb8bc..1da9cfb4e7dbe51567713874d3cee28ab53b737d 100644 --- a/core/state-machine/src/lib.rs +++ b/core/state-machine/src/lib.rs @@ -18,18 +18,16 @@ #![warn(missing_docs)] -use std::{ - fmt, result, collections::HashMap, - marker::PhantomData, panic::UnwindSafe, -}; +use std::{fmt, result, collections::HashMap, panic::UnwindSafe, marker::PhantomData}; use log::{warn, trace}; use hash_db::Hasher; use codec::{Decode, Encode}; use primitives::{ - storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain::{self, NeverOffchainExt}, - traits::{BareCryptoStorePtr, CodeExecutor}, - hexdisplay::HexDisplay, + storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain::OffchainExt, + traits::{KeystoreExt, CodeExecutor}, hexdisplay::HexDisplay, hash::H256, }; +use overlayed_changes::OverlayedChangeSet; +use externalities::Extensions; pub mod backend; mod changes_trie; @@ -42,9 +40,7 @@ mod proving_backend; mod trie_backend; mod trie_backend_essence; -use overlayed_changes::OverlayedChangeSet; -pub use trie::{TrieMut, DBValue, MemoryDB}; -pub use trie::trie_types::{Layout, TrieDBMut}; +pub use trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB}; pub use testing::TestExternalities; pub use basic::BasicExternalities; pub use ext::Ext; @@ -63,8 +59,8 @@ pub use changes_trie::{ }; pub use overlayed_changes::OverlayedChanges; pub use proving_backend::{ - create_proof_check_backend, create_proof_check_backend_storage, - Recorder as ProofRecorder, ProvingBackend, + create_proof_check_backend, create_proof_check_backend_storage, merge_storage_proofs, + Recorder as ProofRecorder, ProvingBackend, StorageProof, }; pub use trie_backend_essence::{TrieBackendStorage, Storage}; pub use trie_backend::TrieBackend; @@ -167,48 +163,54 @@ fn always_untrusted_wasm() -> ExecutionManager { - backend: B, - changes_trie_storage: Option<&'a T>, - offchain_ext: Option<&'a mut O>, - overlay: &'a mut OverlayedChanges, +pub struct StateMachine<'a, B, H, N, T, Exec> where H: Hasher, B: Backend { + backend: &'a B, exec: &'a Exec, method: &'a str, call_data: &'a [u8], - keystore: Option, - _hasher: PhantomData<(H, N)>, + overlay: &'a mut OverlayedChanges, + extensions: Extensions, + changes_trie_storage: Option<&'a T>, + _marker: PhantomData<(H, N)>, } -impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where - H: Hasher, - Exec: CodeExecutor, +impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where + H: Hasher, + Exec: CodeExecutor, B: Backend, T: ChangesTrieStorage, - O: offchain::Externalities, - H::Out: Ord + 'static, N: crate::changes_trie::BlockNumber, { /// Creates new substrate state machine. pub fn new( - backend: B, + backend: &'a B, changes_trie_storage: Option<&'a T>, - offchain_ext: Option<&'a mut O>, + offchain_ext: Option, overlay: &'a mut OverlayedChanges, exec: &'a Exec, method: &'a str, call_data: &'a [u8], - keystore: Option, + keystore: Option, ) -> Self { + let mut extensions = Extensions::new(); + + if let Some(keystore) = keystore { + extensions.register(keystore); + } + + if let Some(offchain) = offchain_ext { + extensions.register(offchain); + } + Self { backend, - changes_trie_storage, - offchain_ext, - overlay, exec, method, call_data, - keystore, - _hasher: PhantomData, + extensions, + overlay, + changes_trie_storage, + _marker: PhantomData, } } @@ -220,10 +222,10 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, 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. - pub fn execute( - &mut self, - strategy: ExecutionStrategy, - ) -> Result<(Vec, (B::Transaction, H::Out), Option>), Box> { + pub fn execute(&mut self, strategy: ExecutionStrategy) -> Result< + (Vec, (B::Transaction, H::Out), Option>), + Box, + > { // We are not giving a native call and thus we are sure that the result can never be a native // value. self.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -252,38 +254,44 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, { - let mut externalities = ext::Ext::new( + let mut ext = Ext::new( self.overlay, - &self.backend, - self.changes_trie_storage, - self.offchain_ext.as_mut().map(|x| &mut **x), - self.keystore.clone(), + self.backend, + self.changes_trie_storage.clone(), + Some(&mut self.extensions), ); - let id = externalities.id; - trace!(target: "state-trace", "{:04x}: Call {} at {:?}. Input={:?}", + + let id = ext.id; + trace!( + target: "state-trace", "{:04x}: Call {} at {:?}. Input={:?}", id, self.method, self.backend, HexDisplay::from(&self.call_data), ); + let (result, was_native) = self.exec.call( - &mut externalities, + &mut ext, self.method, self.call_data, use_native, native_call, ); + let (storage_delta, changes_delta) = if compute_tx { - let (storage_delta, changes_delta) = externalities.transaction(); + let (storage_delta, changes_delta) = ext.transaction(); (Some(storage_delta), changes_delta) } else { (None, None) }; - trace!(target: "state-trace", "{:04x}: Return. Native={:?}, Result={:?}", + + trace!( + target: "state-trace", "{:04x}: Return. Native={:?}, Result={:?}", id, was_native, result, ); + (result, was_native, storage_delta, changes_delta) } @@ -293,12 +301,16 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where mut native_call: Option, orig_prospective: OverlayedChangeSet, on_consensus_failure: Handler, - ) -> (CallResult, Option<(B::Transaction, H::Out)>, Option>) where + ) -> ( + CallResult, + Option<(B::Transaction, H::Out)>, + Option>, + ) where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, Handler: FnOnce( CallResult, - CallResult + CallResult, ) -> CallResult { let (result, was_native, storage_delta, changes_delta) = self.execute_aux( @@ -317,7 +329,8 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where if (result.is_ok() && wasm_result.is_ok() && result.as_ref().ok() == wasm_result.as_ref().ok()) - || result.is_err() && wasm_result.is_err() { + || result.is_err() && wasm_result.is_err() + { (result, storage_delta, changes_delta) } else { (on_consensus_failure(wasm_result, result), wasm_storage_delta, wasm_changes_delta) @@ -332,7 +345,11 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where compute_tx: bool, mut native_call: Option, orig_prospective: OverlayedChangeSet, - ) -> (CallResult, Option<(B::Transaction, H::Out)>, Option>) where + ) -> ( + CallResult, + Option<(B::Transaction, H::Out)>, + Option>, + ) where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, { @@ -431,7 +448,7 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where }; if result.is_ok() { - init_overlay(self.overlay, true, &self.backend)?; + init_overlay(self.overlay, true, self.backend)?; } result.map_err(|e| Box::new(e) as _) @@ -445,13 +462,12 @@ pub fn prove_execution( exec: &Exec, method: &str, call_data: &[u8], - keystore: Option, -) -> Result<(Vec, Vec>), Box> + keystore: Option, +) -> Result<(Vec, StorageProof), Box> where B: Backend, - H: Hasher, - Exec: CodeExecutor, - H::Out: Ord + 'static, + H: Hasher, + Exec: CodeExecutor, { let trie_backend = backend.as_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; @@ -473,17 +489,16 @@ pub fn prove_execution_on_trie_backend( exec: &Exec, method: &str, call_data: &[u8], - keystore: Option, -) -> Result<(Vec, Vec>), Box> + keystore: Option, +) -> Result<(Vec, StorageProof), Box> where S: trie_backend_essence::TrieBackendStorage, - H: Hasher, - Exec: CodeExecutor, - H::Out: Ord + 'static, + H: Hasher, + Exec: CodeExecutor, { let proving_backend = proving_backend::ProvingBackend::new(trie_backend); - let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, _, Exec>::new( - proving_backend, None, NeverOffchainExt::new(), overlay, exec, method, call_data, keystore, + let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new( + &proving_backend, None, None, overlay, exec, method, call_data, keystore, ); let (result, _, _) = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -498,16 +513,16 @@ where /// Check execution proof, generated by `prove_execution` call. pub fn execution_proof_check( root: H::Out, - proof: Vec>, + proof: StorageProof, overlay: &mut OverlayedChanges, exec: &Exec, method: &str, call_data: &[u8], - keystore: Option, + keystore: Option, ) -> Result, Box> where - H: Hasher, - Exec: CodeExecutor, + H: Hasher, + Exec: CodeExecutor, H::Out: Ord + 'static, { let trie_backend = create_proof_check_backend::(root.into(), proof)?; @@ -521,15 +536,14 @@ pub fn execution_proof_check_on_trie_backend( exec: &Exec, method: &str, call_data: &[u8], - keystore: Option, + keystore: Option, ) -> Result, Box> where - H: Hasher, - Exec: CodeExecutor, - H::Out: Ord + 'static, + H: Hasher, + Exec: CodeExecutor, { - let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, _, Exec>::new( - trie_backend, None, NeverOffchainExt::new(), overlay, exec, method, call_data, keystore, + let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage, Exec>::new( + trie_backend, None, None, overlay, exec, method, call_data, keystore, ); sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -543,10 +557,10 @@ where pub fn prove_read( mut backend: B, keys: I, -) -> Result>, Box> +) -> Result> where B: Backend, - H: Hasher, + H: Hasher, H::Out: Ord, I: IntoIterator, I::Item: AsRef<[u8]>, @@ -563,7 +577,7 @@ pub fn prove_child_read( mut backend: B, storage_key: &[u8], keys: I, -) -> Result>, Box> +) -> Result> where B: Backend, H: Hasher, @@ -580,7 +594,7 @@ where pub fn prove_read_on_trie_backend( trie_backend: &TrieBackend, keys: I, -) -> Result>, Box> +) -> Result> where S: trie_backend_essence::TrieBackendStorage, H: Hasher, @@ -602,7 +616,7 @@ pub fn prove_child_read_on_trie_backend( trie_backend: &TrieBackend, storage_key: &[u8], keys: I, -) -> Result>, Box> +) -> Result> where S: trie_backend_essence::TrieBackendStorage, H: Hasher, @@ -622,7 +636,7 @@ where /// Check storage read proof, generated by `prove_read` call. pub fn read_proof_check( root: H::Out, - proof: Vec>, + proof: StorageProof, keys: I, ) -> Result, Option>>, Box> where @@ -643,7 +657,7 @@ where /// Check child storage read proof, generated by `prove_child_read` call. pub fn read_child_proof_check( root: H::Out, - proof: Vec>, + proof: StorageProof, storage_key: &[u8], keys: I, ) -> Result, Option>>, Box> @@ -741,7 +755,7 @@ mod tests { InMemoryStorage as InMemoryChangesTrieStorage, Configuration as ChangesTrieConfig, }; - use primitives::{Blake2Hasher, map, traits::Externalities, child_storage_key::ChildStorageKey}; + use primitives::{Blake2Hasher, map, traits::Externalities, storage::ChildStorageKey}; struct DummyCodeExecutor { change_changes_trie_config: bool, @@ -750,10 +764,14 @@ mod tests { fallback_succeeds: bool, } - impl CodeExecutor for DummyCodeExecutor { + impl CodeExecutor for DummyCodeExecutor { type Error = u8; - fn call, R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result>( + fn call< + E: Externalities, + R: Encode + Decode + PartialEq, + NC: FnOnce() -> result::Result, + >( &self, ext: &mut E, _method: &str, @@ -800,9 +818,9 @@ mod tests { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut state_machine = StateMachine::new( - backend, + &backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), + None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: false, @@ -829,9 +847,9 @@ mod tests { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut state_machine = StateMachine::new( - backend, + &backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), + None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: false, @@ -855,9 +873,9 @@ mod tests { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut state_machine = StateMachine::new( - backend, + &backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), + None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: false, @@ -948,7 +966,6 @@ mod tests { &mut overlay, backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), None, ); ext.clear_prefix(b"ab"); @@ -979,7 +996,6 @@ mod tests { &mut overlay, backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), None, ); @@ -1067,9 +1083,9 @@ mod tests { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut state_machine = StateMachine::new( - backend, + &backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), + None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: true, @@ -1092,9 +1108,9 @@ mod tests { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); let mut state_machine = StateMachine::new( - backend, + &backend, Some(&changes_trie_storage), - NeverOffchainExt::new(), + None, &mut overlayed_changes, &DummyCodeExecutor { change_changes_trie_config: true, diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index a4952ddf73790088feb9ccb3f854cbbc5f8995d3..53a66dc49ee05e9f25025a241728343e43dca62f 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -420,7 +420,6 @@ mod tests { &mut overlay, &backend, Some(&changes_trie_storage), - crate::NeverOffchainExt::new(), None, ); const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa"); @@ -432,9 +431,12 @@ mod tests { fn changes_trie_configuration_is_saved() { let mut overlay = OverlayedChanges::default(); assert!(overlay.changes_trie_config.is_none()); - assert_eq!(overlay.set_changes_trie_config(ChangesTrieConfig { - digest_interval: 4, digest_levels: 1, - }), true); + assert_eq!( + overlay.set_changes_trie_config( + ChangesTrieConfig { digest_interval: 4, digest_levels: 1, }, + ), + true, + ); assert!(overlay.changes_trie_config.is_some()); } diff --git a/core/state-machine/src/proving_backend.rs b/core/state-machine/src/proving_backend.rs index 3908f62eaae0a44928e25d8097e45b1bd6a88e57..14f17a3a48c475715de15eb60d48d3565a9a76c1 100644 --- a/core/state-machine/src/proving_backend.rs +++ b/core/state-machine/src/proving_backend.rs @@ -16,7 +16,8 @@ //! Proving state machine backend. -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, collections::HashSet, rc::Rc}; +use codec::{Decode, Encode}; use log::debug; use hash_db::{Hasher, HashDB, EMPTY_PREFIX}; use trie::{ @@ -29,6 +30,82 @@ use crate::trie_backend::TrieBackend; use crate::trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage}; use crate::{Error, ExecutionError, Backend}; +/// A proof that some set of key-value pairs are included in the storage trie. The proof contains +/// the storage values so that the partial storage backend can be reconstructed by a verifier that +/// does not already have access to the key-value pairs. +/// +/// The proof consists of the set of serialized nodes in the storage trie accessed when looking up +/// the keys covered by the proof. Verifying the proof requires constructing the partial trie from +/// the serialized nodes and performing the key lookups. +#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] +pub struct StorageProof { + trie_nodes: Vec>, +} + +impl StorageProof { + /// Constructs a storage proof from a subset of encoded trie nodes in a storage backend. + pub fn new(trie_nodes: Vec>) -> Self { + StorageProof { trie_nodes } + } + + /// Returns a new empty proof. + /// + /// An empty proof is capable of only proving trivial statements (ie. that an empty set of + /// key-value pairs exist in storage). + pub fn empty() -> Self { + StorageProof { + trie_nodes: Vec::new(), + } + } + + /// Returns whether this is an empty proof. + pub fn is_empty(&self) -> bool { + self.trie_nodes.is_empty() + } + + /// Create an iterator over trie nodes constructed from the proof. The nodes are not guaranteed + /// to be traversed in any particular order. + pub fn iter_nodes(self) -> StorageProofNodeIterator { + StorageProofNodeIterator::new(self) + } +} + +/// An iterator over trie nodes constructed from a storage proof. The nodes are not guaranteed to +/// be traversed in any particular order. +pub struct StorageProofNodeIterator { + inner: > as IntoIterator>::IntoIter, +} + +impl StorageProofNodeIterator { + fn new(proof: StorageProof) -> Self { + StorageProofNodeIterator { + inner: proof.trie_nodes.into_iter(), + } + } +} + +impl Iterator for StorageProofNodeIterator { + type Item = Vec; + + fn next(&mut self) -> Option { + self.inner.next() + } +} + +/// Merges multiple storage proofs covering potentially different sets of keys into one proof +/// covering all keys. The merged proof output may be smaller than the aggregate size of the input +/// proofs due to deduplication of trie nodes. +pub fn merge_storage_proofs(proofs: I) -> StorageProof + where I: IntoIterator +{ + let trie_nodes = proofs.into_iter() + .flat_map(|proof| proof.iter_nodes()) + .collect::>() + .into_iter() + .collect(); + StorageProof { trie_nodes } +} + /// Patricia trie-based backend essence which also tracks all touched storage trie values. /// These can be sent to remote node and used as a proof of execution. pub struct ProvingBackendEssence<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> { @@ -129,13 +206,14 @@ impl<'a, S: 'a + TrieBackendStorage, H: 'a + Hasher> ProvingBackend<'a, S, H> } /// Consume the backend, extracting the gathered proof in lexicographical order by value. - pub fn extract_proof(&self) -> Vec> { - self.proof_recorder + pub fn extract_proof(&self) -> StorageProof { + let trie_nodes = self.proof_recorder .borrow_mut() .drain() .into_iter() - .map(|n| n.data.to_vec()) - .collect() + .map(|record| record.data) + .collect(); + StorageProof::new(trie_nodes) } } @@ -217,7 +295,7 @@ impl<'a, S, H> Backend for ProvingBackend<'a, S, H> /// Create proof check backend. pub fn create_proof_check_backend( root: H::Out, - proof: Vec> + proof: StorageProof, ) -> Result, H>, Box> where H: Hasher, @@ -233,13 +311,13 @@ where /// Create in-memory storage of proof check backend. pub fn create_proof_check_backend_storage( - proof: Vec> + proof: StorageProof, ) -> MemoryDB where H: Hasher, { let mut db = MemoryDB::default(); - for item in proof { + for item in proof.iter_nodes() { db.insert(EMPTY_PREFIX, &item); } db @@ -250,7 +328,7 @@ mod tests { use crate::backend::{InMemory}; use crate::trie_backend::tests::test_trie; use super::*; - use primitives::{Blake2Hasher, child_storage_key::ChildStorageKey}; + use primitives::{Blake2Hasher, storage::ChildStorageKey}; fn test_proving<'a>( trie_backend: &'a TrieBackend,Blake2Hasher>, @@ -275,7 +353,11 @@ mod tests { #[test] fn proof_is_invalid_when_does_not_contains_root() { use primitives::H256; - assert!(create_proof_check_backend::(H256::from_low_u64_be(1), vec![]).is_err()); + let result = create_proof_check_backend::( + H256::from_low_u64_be(1), + StorageProof::empty() + ); + assert!(result.is_err()); } #[test] diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index 160f7d2a47ccb8d9aae15a35756129e4e6069692..16ff62020b59401f81f282659ef0ed867a11f635 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -16,35 +16,47 @@ //! Test implementation for Externalities. -use std::collections::{HashMap}; +use std::{collections::HashMap, any::{Any, TypeId}}; use hash_db::Hasher; use crate::{ backend::{InMemory, Backend}, OverlayedChanges, changes_trie::{ - build_changes_trie, InMemoryStorage as ChangesTrieInMemoryStorage, + InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, }, + ext::Ext, }; use primitives::{ - storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key}, - traits::{BareCryptoStorePtr, Externalities}, offchain, child_storage_key::ChildStorageKey, + storage::{ + well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key} + }, + hash::H256, Blake2Hasher, }; use codec::Encode; - -const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; +use externalities::{Extensions, Extension}; type StorageTuple = (HashMap, Vec>, HashMap, HashMap, Vec>>); /// Simple HashMap-based Externalities impl. -pub struct TestExternalities { +pub struct TestExternalities=Blake2Hasher, N: ChangesTrieBlockNumber=u64> { overlay: OverlayedChanges, backend: InMemory, changes_trie_storage: ChangesTrieInMemoryStorage, - offchain: Option>, - keystore: Option, + extensions: Extensions, } -impl TestExternalities { +impl, N: ChangesTrieBlockNumber> TestExternalities { + + /// Get externalities implementation. + pub fn ext(&mut self) -> Ext, ChangesTrieInMemoryStorage> { + Ext::new( + &mut self.overlay, + &self.backend, + Some(&self.changes_trie_storage), + Some(&mut self.extensions), + ) + } + /// Create a new instance of `TestExternalities` with storage. pub fn new(storage: StorageTuple) -> Self { Self::new_with_code(&[], storage) @@ -75,8 +87,7 @@ impl TestExternalities { overlay, changes_trie_storage: ChangesTrieInMemoryStorage::new(), backend: backend.into(), - offchain: None, - keystore: None, + extensions: Default::default(), } } @@ -85,14 +96,9 @@ impl TestExternalities { self.backend = self.backend.update(vec![(None, k, Some(v))]); } - /// Set offchain externaltiies. - pub fn set_offchain_externalities(&mut self, offchain: impl offchain::Externalities + 'static) { - self.offchain = Some(Box::new(offchain)); - } - - /// Set keystore. - pub fn set_keystore(&mut self, keystore: BareCryptoStorePtr) { - self.keystore = Some(keystore); + /// Registers the given extension for this instance. + pub fn register_extension(&mut self, ext: E) { + self.extensions.register(ext); } /// Get mutable reference to changes trie storage. @@ -116,15 +122,23 @@ impl TestExternalities { self.backend.update(top.chain(children).collect()) } + + /// Execute the given closure while `self` is set as externalities. + /// + /// Returns the result of the given closure. + pub fn execute_with(&mut self, execute: impl FnOnce() -> R) -> R { + let mut ext = self.ext(); + externalities::set_and_run_with_externalities(&mut ext, execute) + } } -impl std::fmt::Debug for TestExternalities { +impl, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, self.backend.pairs()) } } -impl PartialEq for TestExternalities { +impl, N: ChangesTrieBlockNumber> PartialEq for TestExternalities { /// This doesn't test if they are in the same state, only if they contains the /// same data at this state fn eq(&self, other: &TestExternalities) -> bool { @@ -132,173 +146,35 @@ impl PartialEq for TestExternalities } } -impl Default for TestExternalities { +impl, N: ChangesTrieBlockNumber> Default for TestExternalities { fn default() -> Self { Self::new(Default::default()) } } -impl From for TestExternalities { +impl, N: ChangesTrieBlockNumber> From for TestExternalities { fn from(storage: StorageTuple) -> Self { Self::new(storage) } } -impl Externalities for TestExternalities where - H: Hasher, +impl externalities::ExtensionStore for TestExternalities where + H: Hasher, N: ChangesTrieBlockNumber, - H::Out: Ord + 'static, { - fn storage(&self, key: &[u8]) -> Option> { - self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| - self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)) - } - - fn original_storage(&self, key: &[u8]) -> Option> { - self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL) - } - - fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { - self.overlay - .child_storage(storage_key.as_ref(), key) - .map(|x| x.map(|x| x.to_vec())) - .unwrap_or_else(|| self.backend - .child_storage(storage_key.as_ref(), key) - .expect(EXT_NOT_ALLOWED_TO_FAIL) - ) - } - - fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option> { - self.backend - .child_storage(storage_key.as_ref(), key) - .map(|x| x.map(|x| x.to_vec())) - .expect(EXT_NOT_ALLOWED_TO_FAIL) - } - - fn place_storage(&mut self, key: Vec, maybe_value: Option>) { - if is_child_storage_key(&key) { - panic!("Refuse to directly set child storage key"); - } - - self.overlay.set_storage(key, maybe_value); - } - - fn place_child_storage( - &mut self, - storage_key: ChildStorageKey, - key: Vec, - value: Option> - ) { - self.overlay.set_child_storage(storage_key.into_owned(), key, value); - } - - fn kill_child_storage(&mut self, storage_key: ChildStorageKey) { - let backend = &self.backend; - let overlay = &mut self.overlay; - - overlay.clear_child_storage(storage_key.as_ref()); - backend.for_keys_in_child_storage(storage_key.as_ref(), |key| { - overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None); - }); - } - - fn clear_prefix(&mut self, prefix: &[u8]) { - if is_child_storage_key(prefix) { - panic!("Refuse to directly clear prefix that is part of child storage key"); - } - - self.overlay.clear_prefix(prefix); - - let backend = &self.backend; - let overlay = &mut self.overlay; - backend.for_keys_with_prefix(prefix, |key| { - overlay.set_storage(key.to_vec(), None); - }); - } - - fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) { - - self.overlay.clear_child_prefix(storage_key.as_ref(), prefix); - - let backend = &self.backend; - let overlay = &mut self.overlay; - backend.for_child_keys_with_prefix(storage_key.as_ref(), prefix, |key| { - overlay.set_child_storage(storage_key.as_ref().to_vec(), key.to_vec(), None); - }); - } - - fn chain_id(&self) -> u64 { 42 } - - fn storage_root(&mut self) -> H::Out { - - let child_storage_keys = - self.overlay.prospective.children.keys() - .chain(self.overlay.committed.children.keys()); - - let child_delta_iter = child_storage_keys.map(|storage_key| - (storage_key.clone(), self.overlay.committed.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone()))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.value.clone())))))); - - - // compute and memoize - let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) - .chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))); - self.backend.full_storage_root(delta, child_delta_iter).0 - - } - - fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec { - let storage_key = storage_key.as_ref(); - - let (root, is_empty, _) = { - let delta = self.overlay.committed.children.get(storage_key) - .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.clone().into_iter().map(|(k, v)| (k, v.value)))); - - self.backend.child_storage_root(storage_key, delta) - }; - if is_empty { - self.overlay.set_storage(storage_key.into(), None); - } else { - self.overlay.set_storage(storage_key.into(), Some(root.clone())); - } - root - } - - fn storage_changes_root(&mut self, parent: H::Out) -> Result, ()> { - Ok(build_changes_trie::<_, _, H, N>( - &self.backend, - Some(&self.changes_trie_storage), - &self.overlay, - parent, - )?.map(|(_, root, _)| root)) - } - - fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { - self.offchain - .as_mut() - .map(|x| &mut **x as _) - } - - fn keystore(&self) -> Option { - self.keystore.clone() + fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> { + self.extensions.get_mut(type_id) } } #[cfg(test)] mod tests { use super::*; - use primitives::{Blake2Hasher, H256}; + use primitives::traits::Externalities; use hex_literal::hex; #[test] fn commit_should_work() { let mut ext = TestExternalities::::default(); + let mut ext = ext.ext(); ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); @@ -309,6 +185,7 @@ mod tests { #[test] fn set_and_retrieve_code() { let mut ext = TestExternalities::::default(); + let mut ext = ext.ext(); let code = vec![1, 2, 3]; ext.set_storage(CODE.to_vec(), code.clone()); diff --git a/core/state-machine/src/trie_backend.rs b/core/state-machine/src/trie_backend.rs index ce5773c0b7956ce0d979be1afa9b3da8a7edafdf..432ccf3e75f0e47319a30c12daaf3c1a03c7747f 100644 --- a/core/state-machine/src/trie_backend.rs +++ b/core/state-machine/src/trie_backend.rs @@ -168,7 +168,7 @@ impl, H: Hasher> Backend for TrieBackend where let mut write_overlay = S::Overlay::default(); let mut root = match self.storage(storage_key) { - Ok(value) => value.unwrap_or(default_child_trie_root::>(storage_key)), + Ok(value) => value.unwrap_or(default_root.clone()), Err(e) => { warn!(target: "trie", "Failed to read child storage root: {}", e); default_root.clone() diff --git a/core/telemetry/src/worker/node.rs b/core/telemetry/src/worker/node.rs index 11b1f2a81e6996da38c9636f563bbf809922b7a2..0f606e40638024c82c9c2cc96784d4faf3217580 100644 --- a/core/telemetry/src/worker/node.rs +++ b/core/telemetry/src/worker/node.rs @@ -58,6 +58,8 @@ struct NodeSocketConnected { pending: VecDeque, /// If true, we need to flush the sink. need_flush: bool, + /// A timeout for the socket to write data. + timeout: Option, } /// Event that can happen with this node. @@ -66,7 +68,16 @@ pub enum NodeEvent { /// We are now connected to this node. Connected, /// We are now disconnected from this node. - Disconnected(TSinkErr), + Disconnected(ConnectionError), +} + +/// Reason for disconnecting from a node. +#[derive(Debug)] +pub enum ConnectionError { + /// The connection timed-out. + Timeout, + /// The sink errored. + Sink(TSinkErr), } impl Node { @@ -116,10 +127,12 @@ where TTrans: Clone + Unpin, TTrans::Dial: Unpin, let mut socket = mem::replace(&mut self.socket, NodeSocket::Poisoned); self.socket = loop { match socket { - NodeSocket::Connected(mut conn) => + NodeSocket::Connected(mut conn) => { match NodeSocketConnected::poll(Pin::new(&mut conn), cx, &self.addr) { - Poll::Ready(Ok(v)) => match v {} - Poll::Pending => break NodeSocket::Connected(conn), + Poll::Ready(Ok(v)) => match v {}, + Poll::Pending => { + break NodeSocket::Connected(conn) + }, Poll::Ready(Err(err)) => { warn!(target: "telemetry", "Disconnected from {}: {:?}", self.addr, err); let timeout = gen_rand_reconnect_delay(); @@ -127,10 +140,16 @@ where TTrans: Clone + Unpin, TTrans::Dial: Unpin, return Poll::Ready(NodeEvent::Disconnected(err)) } } + } NodeSocket::Dialing(mut s) => match Future::poll(Pin::new(&mut s), cx) { Poll::Ready(Ok(sink)) => { debug!(target: "telemetry", "Connected to {}", self.addr); - let conn = NodeSocketConnected { sink, pending: VecDeque::new(), need_flush: false }; + let conn = NodeSocketConnected { + sink, + pending: VecDeque::new(), + need_flush: false, + timeout: None, + }; self.socket = NodeSocket::Connected(conn); return Poll::Ready(NodeEvent::Connected) }, @@ -189,18 +208,15 @@ where TTrans::Output: Sink fn poll( mut self: Pin<&mut Self>, cx: &mut Context, - my_addr: &Multiaddr - ) -> Poll> { - loop { - if let Some(item) = self.pending.pop_front() { - if let Poll::Pending = Sink::poll_ready(Pin::new(&mut self.sink), cx) { - self.pending.push_front(item); - return Poll::Pending - } + my_addr: &Multiaddr, + ) -> Poll>> { + while let Some(item) = self.pending.pop_front() { + if let Poll::Ready(_) = Sink::poll_ready(Pin::new(&mut self.sink), cx) { let item_len = item.len(); if let Err(err) = Sink::start_send(Pin::new(&mut self.sink), item) { - return Poll::Ready(Err(err)) + self.timeout = None; + return Poll::Ready(Err(ConnectionError::Sink(err))) } trace!( target: "telemetry", "Successfully sent {:?} bytes message to {}", @@ -208,28 +224,59 @@ where TTrans::Output: Sink ); self.need_flush = true; - } else if self.need_flush { - match Sink::poll_flush(Pin::new(&mut self.sink), cx) { - Poll::Pending => return Poll::Pending, - Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), - Poll::Ready(Ok(())) => self.need_flush = false, + } else { + self.pending.push_front(item); + if self.timeout.is_none() { + self.timeout = Some(Delay::new(Duration::from_secs(10))); } + break; + } + } - } else { - match Stream::poll_next(Pin::new(&mut self.sink), cx) { - Poll::Ready(Some(Ok(_))) => { - // We poll the telemetry `Stream` because the underlying implementation relies on - // this in order to answer PINGs. - // We don't do anything with incoming messages, however. - }, - Poll::Ready(Some(Err(err))) => { - return Poll::Ready(Err(err)) - }, - Poll::Pending | Poll::Ready(None) => break, + if self.need_flush { + match Sink::poll_flush(Pin::new(&mut self.sink), cx) { + Poll::Pending => { + if self.timeout.is_none() { + self.timeout = Some(Delay::new(Duration::from_secs(10))); + } + }, + Poll::Ready(Err(err)) => { + self.timeout = None; + return Poll::Ready(Err(ConnectionError::Sink(err))) + }, + Poll::Ready(Ok(())) => { + self.timeout = None; + self.need_flush = false; + }, + } + } + + if let Some(timeout) = self.timeout.as_mut() { + match Future::poll(Pin::new(timeout), cx) { + Poll::Pending => {}, + Poll::Ready(Err(err)) => { + self.timeout = None; + warn!(target: "telemetry", "Connection timeout error for {} {:?}", my_addr, err); + } + Poll::Ready(Ok(_)) => { + self.timeout = None; + return Poll::Ready(Err(ConnectionError::Timeout)) } } } + match Stream::poll_next(Pin::new(&mut self.sink), cx) { + Poll::Ready(Some(Ok(_))) => { + // We poll the telemetry `Stream` because the underlying implementation relies on + // this in order to answer PINGs. + // We don't do anything with incoming messages, however. + }, + Poll::Ready(Some(Err(err))) => { + return Poll::Ready(Err(ConnectionError::Sink(err))) + }, + Poll::Pending | Poll::Ready(None) => {}, + } + Poll::Pending } } diff --git a/core/test-client/src/lib.rs b/core/test-client/src/lib.rs index a2baf11be2e8483381b11affd3b4be404e395da4..dbe4431456a7494385a3525086586968cf510163 100644 --- a/core/test-client/src/lib.rs +++ b/core/test-client/src/lib.rs @@ -24,7 +24,7 @@ pub use client::{ExecutionStrategies, blockchain, backend, self}; pub use client_db::{Backend, self}; pub use client_ext::ClientExt; pub use consensus; -pub use executor::{NativeExecutor, self}; +pub use executor::{NativeExecutor, WasmExecutionMethod, self}; pub use keyring::{ AccountKeyring, ed25519::Keyring as Ed25519Keyring, @@ -198,7 +198,7 @@ impl TestClientBuilder } impl TestClientBuilder< - client::LocalCallExecutor>, + client::LocalCallExecutor>, Backend, G, > { @@ -209,18 +209,20 @@ impl TestClientBuilder< ) -> ( client::Client< Backend, - client::LocalCallExecutor>, + client::LocalCallExecutor>, Block, RuntimeApi >, client::LongestChain, ) where - I: Into>>, + I: Into>>, E: executor::NativeExecutionDispatch, Backend: client::backend::Backend, Block: BlockT::Out>, { - let executor = executor.into().unwrap_or_else(|| executor::NativeExecutor::new(None)); + let executor = executor.into().unwrap_or_else(|| + NativeExecutor::new(WasmExecutionMethod::Interpreted, None) + ); let executor = LocalCallExecutor::new(self.backend.clone(), executor, self.keystore.take()); self.build_with_executor(executor) diff --git a/core/test-runtime/Cargo.toml b/core/test-runtime/Cargo.toml index 313db0fd070f14f676b62395ba564bac04fe05f1..4e7c3f8bca450f035706ad465fef44442b62f7ea 100644 --- a/core/test-runtime/Cargo.toml +++ b/core/test-runtime/Cargo.toml @@ -31,6 +31,7 @@ cfg-if = "0.1.10" srml-babe = { path = "../../srml/babe", default-features = false } srml-timestamp = { path = "../../srml/timestamp", default-features = false } srml-system = { path = "../../srml/system", default-features = false } +srml-system-rpc-runtime-api = { path = "../../srml/system/rpc/runtime-api", default-features = false } [dev-dependencies] substrate-executor = { path = "../executor" } @@ -38,7 +39,7 @@ substrate-test-runtime-client = { path = "./client" } state_machine = { package = "substrate-state-machine", path = "../state-machine" } [build-dependencies] -wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../utils/wasm-builder-runner" } +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.4", path = "../utils/wasm-builder-runner" } [features] default = [ @@ -68,6 +69,7 @@ std = [ "srml-babe/std", "srml-timestamp/std", "srml-system/std", + "srml-system-rpc-runtime-api/std", "app-crypto/std", "session/std", ] diff --git a/core/test-runtime/build.rs b/core/test-runtime/build.rs index 2e2d1fc93ec7ec2013ec25f79a498ce030295cff..200cd6d42c2d7705c01ccd4b6b9e26528535163d 100644 --- a/core/test-runtime/build.rs +++ b/core/test-runtime/build.rs @@ -21,7 +21,7 @@ fn main() { "wasm_binary.rs", WasmBuilderSource::CratesOrPath { path: "../utils/wasm-builder", - version: "1.0.7", + version: "1.0.8", }, // 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`) diff --git a/core/test-runtime/client/src/lib.rs b/core/test-runtime/client/src/lib.rs index 722aa0b2b472601b6003bc3bf805ef1324acfecd..affbae62c22e77d4b66acb8d8f3efc94aa7bcd01 100644 --- a/core/test-runtime/client/src/lib.rs +++ b/core/test-runtime/client/src/lib.rs @@ -39,7 +39,7 @@ pub mod prelude { // Client structs pub use super::{ TestClient, TestClientBuilder, Backend, LightBackend, - Executor, LightExecutor, LocalExecutor, NativeExecutor, + Executor, LightExecutor, LocalExecutor, NativeExecutor, WasmExecutionMethod, }; // Keyring pub use super::{AccountKeyring, Sr25519Keyring}; @@ -261,7 +261,7 @@ pub fn new_light() -> ( let storage = client_db::light::LightStorage::new_test(); let blockchain = Arc::new(client::light::blockchain::Blockchain::new(storage)); let backend = Arc::new(LightBackend::new(blockchain.clone())); - let executor = NativeExecutor::new(None); + let executor = NativeExecutor::new(WasmExecutionMethod::Interpreted, None); let local_call_executor = client::LocalCallExecutor::new(backend.clone(), executor, None); let call_executor = LightExecutor::new( backend.clone(), diff --git a/core/test-runtime/src/genesismap.rs b/core/test-runtime/src/genesismap.rs index a3abf235ff1ca9132132046940d959d60ac95729..a02a1df855c351f908083340865e6e45036e9dc7 100644 --- a/core/test-runtime/src/genesismap.rs +++ b/core/test-runtime/src/genesismap.rs @@ -18,7 +18,7 @@ use std::collections::HashMap; use runtime_io::{blake2_256, twox_128}; -use super::{AuthorityId, AccountId, WASM_BINARY}; +use super::{AuthorityId, AccountId, WASM_BINARY, system}; use codec::{Encode, KeyedVec, Joiner}; use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys}; use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; @@ -77,10 +77,16 @@ impl GenesisConfig { map.insert(well_known_keys::CHANGES_TRIE_CONFIG.to_vec(), changes_trie_config.encode()); } map.insert(twox_128(&b"sys:auth"[..])[..].to_vec(), self.authorities.encode()); - // Finally, add the extra storage entries. + // Add the extra storage entries. map.extend(self.extra_storage.clone().into_iter()); - (map, self.child_extra_storage.clone()) + // Assimilate the system genesis config. + let mut storage = (map, self.child_extra_storage.clone()); + let mut config = system::GenesisConfig::default(); + config.authorities = self.authorities.clone(); + config.assimilate_storage(&mut storage); + + storage } } diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index ff9826acaedb862a4f8e65e4e617e054675b34bd..7fac0e702e6cada1b968b5acbf48f8d267af9396 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -25,14 +25,7 @@ pub mod system; use rstd::{prelude::*, marker::PhantomData}; use codec::{Encode, Decode, Input, Error}; -use primitives::{ - Blake2Hasher, - OpaqueMetadata, - testing::{ - ED25519, - SR25519, - } -}; +use primitives::{Blake2Hasher, OpaqueMetadata, RuntimeDebug}; use app_crypto::{ed25519, sr25519, RuntimeAppPublic}; pub use app_crypto; use trie_db::{TrieMut, Trie}; @@ -93,8 +86,7 @@ pub fn native_version() -> NativeVersion { } /// Calls in transactions. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] pub struct Transfer { pub from: AccountId, pub to: AccountId, @@ -113,8 +105,7 @@ impl Transfer { } /// Extrinsic for test-runtime. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] pub enum Extrinsic { AuthoritiesChange(Vec), Transfer(Transfer, AccountSignature), @@ -353,8 +344,7 @@ impl_outer_origin!{ pub enum Origin for Runtime where system = srml_system {} } -#[derive(Clone, Encode, Decode, Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] pub struct Event; impl From for Event { @@ -382,7 +372,6 @@ impl srml_system::Trait for Runtime { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; @@ -405,6 +394,10 @@ parameter_types! { impl srml_babe::Trait for Runtime { type EpochDuration = EpochDuration; type ExpectedBlockTime = ExpectedBlockTime; + // there is no actual runtime in this test-runtime, so testing crates + // are manually adding the digests. normally in this situation you'd use + // srml_babe::SameAuthoritiesForever. + type EpochChangeTrigger = srml_babe::ExternalTrigger; } /// Adds one to the given input and returns the final result. @@ -453,9 +446,7 @@ fn code_using_trie() -> u64 { impl_opaque_keys! { pub struct SessionKeys { - #[id(ED25519)] pub ed25519: ed25519::AppPublic, - #[id(SR25519)] pub sr25519: sr25519::AppPublic, } } @@ -644,6 +635,12 @@ cfg_if! { SessionKeys::generate(None) } } + + impl srml_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(_account: AccountId) -> Index { + 0 + } + } } } else { impl_runtime_apis! { @@ -854,6 +851,12 @@ cfg_if! { SessionKeys::generate(None) } } + + impl srml_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(_account: AccountId) -> Index { + 0 + } + } } } } diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index e61e72f3a0e85cb1fa3bdbf343757ddee566c87f..f1c75122a01f4d836d6490c207d6fea4c4dae0b2 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -18,14 +18,15 @@ //! and depositing logs. use rstd::prelude::*; -use runtime_io::{storage_root, storage_changes_root, twox_128, blake2_256}; +use runtime_io::{storage_root, storage_changes_root, blake2_256}; use runtime_support::storage::{self, StorageValue, StorageMap}; -use runtime_support::storage_items; +use runtime_support::{decl_storage, decl_module}; use sr_primitives::{ traits::{Hash as HashT, BlakeTwo256, Header as _}, generic, ApplyError, ApplyResult, transaction_validity::{TransactionValidity, ValidTransaction, InvalidTransaction}, }; use codec::{KeyedVec, Encode}; +use srml_system::Trait; use crate::{ AccountId, BlockNumber, Extrinsic, Transfer, H256 as Hash, Block, Header, Digest, AuthorityId }; @@ -34,14 +35,20 @@ use primitives::storage::well_known_keys; const NONCE_OF: &[u8] = b"nonce:"; const BALANCE_OF: &[u8] = b"balance:"; -storage_items! { - ExtrinsicData: b"sys:xtd" => required map [ u32 => Vec ]; - // The current block number being processed. Set by `execute_block`. - Number: b"sys:num" => BlockNumber; - ParentHash: b"sys:pha" => required Hash; - NewAuthorities: b"sys:new_auth" => Vec; - StorageDigest: b"sys:digest" => Digest; - Authorities get(authorities): b"sys:auth" => default Vec; +decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +decl_storage! { + trait Store for Module as TestRuntime { + ExtrinsicData: map u32 => Vec; + // The current block number being processed. Set by `execute_block`. + Number get(fn number): Option; + ParentHash get(fn parent_hash): Hash; + NewAuthorities get(fn new_authorities): Option>; + StorageDigest get(fn storage_digest): Option; + Authorities get(fn authorities) config(): Vec; + } } pub fn balance_of_key(who: AccountId) -> Vec { @@ -70,6 +77,10 @@ pub fn initialize_block(header: &Header) { } } +pub fn authorities() -> Vec { + Authorities::get() +} + pub fn get_block_number() -> Option { Number::get() } @@ -170,22 +181,14 @@ pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity { return InvalidTransaction::Future.into(); } - let hash = |from: &AccountId, nonce: u64| { - twox_128(&nonce.to_keyed_vec(&from.encode())).to_vec() - }; + let encode = |from: &AccountId, nonce: u64| (from, nonce).encode(); let requires = if tx.nonce != expected_nonce && tx.nonce > 0 { - let mut deps = Vec::new(); - deps.push(hash(&tx.from, tx.nonce - 1)); - deps + vec![encode(&tx.from, tx.nonce - 1)] } else { - Vec::new() + vec![] }; - let provides = { - let mut p = Vec::new(); - p.push(hash(&tx.from, tx.nonce)); - p - }; + let provides = vec![encode(&tx.from, tx.nonce)]; Ok(ValidTransaction { priority: tx.amount, @@ -319,28 +322,46 @@ fn info_expect_equal_hash(given: &Hash, expected: &Hash) { mod tests { use super::*; - use runtime_io::{with_externalities, TestExternalities}; + use runtime_io::TestExternalities; use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring}; use crate::{Header, Transfer, WASM_BINARY}; - use primitives::{Blake2Hasher, map}; - use substrate_executor::WasmExecutor; + use primitives::{NeverNativeValue, map, traits::CodeExecutor}; + use substrate_executor::{NativeExecutor, WasmExecutionMethod, native_executor_instance}; + use runtime_io::twox_128; + + // Declare an instance of the native executor dispatch for the test runtime. + native_executor_instance!( + NativeDispatch, + crate::api::dispatch, + crate::native_version + ); + + fn executor() -> NativeExecutor { + NativeExecutor::new(WasmExecutionMethod::Interpreted, None) + } - fn new_test_ext() -> TestExternalities { + fn new_test_ext() -> TestExternalities { let authorities = vec![ Sr25519Keyring::Alice.to_raw_public(), Sr25519Keyring::Bob.to_raw_public(), Sr25519Keyring::Charlie.to_raw_public() ]; - TestExternalities::new((map![ - twox_128(b"latest").to_vec() => vec![69u8; 32], - twox_128(b"sys:auth").to_vec() => authorities.encode(), - blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => { - vec![111u8, 0, 0, 0, 0, 0, 0, 0] - } - ], map![])) + TestExternalities::new_with_code( + WASM_BINARY, + ( + map![ + twox_128(b"latest").to_vec() => vec![69u8; 32], + twox_128(b"sys:auth").to_vec() => authorities.encode(), + blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => { + vec![111u8, 0, 0, 0, 0, 0, 0, 0] + } + ], + map![], + ) + ) } - fn block_import_works(block_executor: F) where F: Fn(Block, &mut TestExternalities) { + fn block_import_works(block_executor: F) where F: Fn(Block, &mut TestExternalities) { let h = Header { parent_hash: [69u8; 32].into(), number: 1, @@ -353,28 +374,33 @@ mod tests { extrinsics: vec![], }; - with_externalities(&mut new_test_ext(), || polish_block(&mut b)); + new_test_ext().execute_with(|| polish_block(&mut b)); block_executor(b, &mut new_test_ext()); } #[test] fn block_import_works_native() { - block_import_works(|b, ext| { - with_externalities(ext, || { - execute_block(b); - }); - }); + block_import_works(|b, ext| ext.execute_with(|| execute_block(b))); } #[test] fn block_import_works_wasm() { block_import_works(|b, ext| { - WasmExecutor::new().call(ext, 8, &WASM_BINARY, "Core_execute_block", &b.encode()).unwrap(); + let mut ext = ext.ext(); + executor().call::<_, NeverNativeValue, fn() -> _>( + &mut ext, + "Core_execute_block", + &b.encode(), + false, + None, + ).0.unwrap(); }) } - fn block_import_with_transaction_works(block_executor: F) where F: Fn(Block, &mut TestExternalities) { + fn block_import_with_transaction_works(block_executor: F) + where F: Fn(Block, &mut TestExternalities) + { let mut b1 = Block { header: Header { parent_hash: [69u8; 32].into(), @@ -394,7 +420,7 @@ mod tests { }; let mut dummy_ext = new_test_ext(); - with_externalities(&mut dummy_ext, || polish_block(&mut b1)); + dummy_ext.execute_with(|| polish_block(&mut b1)); let mut b2 = Block { header: Header { @@ -420,26 +446,26 @@ mod tests { ], }; - with_externalities(&mut dummy_ext, || polish_block(&mut b2)); + dummy_ext.execute_with(|| polish_block(&mut b2)); drop(dummy_ext); let mut t = new_test_ext(); - with_externalities(&mut t, || { + t.execute_with(|| { assert_eq!(balance_of(AccountKeyring::Alice.into()), 111); assert_eq!(balance_of(AccountKeyring::Bob.into()), 0); }); block_executor(b1, &mut t); - with_externalities(&mut t, || { + t.execute_with(|| { assert_eq!(balance_of(AccountKeyring::Alice.into()), 42); assert_eq!(balance_of(AccountKeyring::Bob.into()), 69); }); block_executor(b2, &mut t); - with_externalities(&mut t, || { + t.execute_with(|| { assert_eq!(balance_of(AccountKeyring::Alice.into()), 0); assert_eq!(balance_of(AccountKeyring::Bob.into()), 42); assert_eq!(balance_of(AccountKeyring::Charlie.into()), 69); @@ -448,17 +474,20 @@ mod tests { #[test] fn block_import_with_transaction_works_native() { - block_import_with_transaction_works(|b, ext| { - with_externalities(ext, || { - execute_block(b); - }); - }); + block_import_with_transaction_works(|b, ext| ext.execute_with(|| execute_block(b))); } #[test] fn block_import_with_transaction_works_wasm() { block_import_with_transaction_works(|b, ext| { - WasmExecutor::new().call(ext, 8, &WASM_BINARY, "Core_execute_block", &b.encode()).unwrap(); + let mut ext = ext.ext(); + executor().call::<_, NeverNativeValue, fn() -> _>( + &mut ext, + "Core_execute_block", + &b.encode(), + false, + None, + ).0.unwrap(); }) } } diff --git a/core/transaction-pool/graph/Cargo.toml b/core/transaction-pool/graph/Cargo.toml index d99291476ea91af25ce4f02be4fb9e6ee114ea2d..4b628079cd3cef6915f041e679955f66c0da7db9 100644 --- a/core/transaction-pool/graph/Cargo.toml +++ b/core/transaction-pool/graph/Cargo.toml @@ -15,6 +15,11 @@ sr-primitives = { path = "../../sr-primitives" } [dev-dependencies] assert_matches = "1.3.0" -env_logger = "0.6.2" +env_logger = "0.7.0" codec = { package = "parity-scale-codec", version = "1.0.0" } test_runtime = { package = "substrate-test-runtime", path = "../../test-runtime" } +criterion = "0.3" + +[[bench]] +name = "basics" +harness = false diff --git a/core/transaction-pool/graph/benches/basics.rs b/core/transaction-pool/graph/benches/basics.rs new file mode 100644 index 0000000000000000000000000000000000000000..dcd725ce465f42c6e90f1c70315642ab9d9c326d --- /dev/null +++ b/core/transaction-pool/graph/benches/basics.rs @@ -0,0 +1,165 @@ +// Copyright 2018-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 . + +use criterion::{criterion_group, criterion_main, Criterion}; + +use futures::executor::block_on; +use substrate_transaction_graph::*; +use sr_primitives::transaction_validity::{ValidTransaction, InvalidTransaction}; +use codec::Encode; +use test_runtime::{Block, Extrinsic, Transfer, H256, AccountId}; +use sr_primitives::{ + generic::BlockId, + transaction_validity::{TransactionValidity, TransactionTag as Tag}, +}; +use primitives::blake2_256; + +#[derive(Clone, Debug, Default)] +struct TestApi { + nonce_dependant: bool, +} + +impl TestApi { + fn new_dependant() -> Self { + TestApi { nonce_dependant: true } + } +} + +fn to_tag(nonce: u64, from: AccountId) -> Tag { + let mut data = [0u8; 40]; + data[..8].copy_from_slice(&nonce.to_le_bytes()[..]); + data[8..].copy_from_slice(&from.0[..]); + data.to_vec() +} + +impl ChainApi for TestApi { + type Block = Block; + type Hash = H256; + type Error = error::Error; + type ValidationFuture = futures::future::Ready>; + + fn validate_transaction( + &self, + at: &BlockId, + uxt: ExtrinsicFor, + ) -> Self::ValidationFuture { + let nonce = uxt.transfer().nonce; + let from = uxt.transfer().from.clone(); + + match self.block_id_to_number(at) { + Ok(Some(num)) if num > 5 => { + return futures::future::ready( + Ok(Err(InvalidTransaction::Stale.into())) + ) + }, + _ => {}, + } + + futures::future::ready( + Ok(Ok(ValidTransaction { + priority: 4, + requires: if nonce > 1 && self.nonce_dependant { + vec![to_tag(nonce-1, from.clone())] + } else { vec![] }, + provides: vec![to_tag(nonce, from)], + longevity: 10, + propagate: true, + })) + ) + } + + fn block_id_to_number( + &self, + at: &BlockId, + ) -> Result>, Self::Error> { + Ok(match at { + BlockId::Number(num) => Some(*num), + BlockId::Hash(_) => None, + }) + } + + fn block_id_to_hash( + &self, + at: &BlockId, + ) -> Result>, Self::Error> { + Ok(match at { + BlockId::Number(num) => Some(H256::from_low_u64_be(*num)).into(), + BlockId::Hash(_) => None, + }) + } + + fn hash_and_length(&self, uxt: &ExtrinsicFor) -> (Self::Hash, usize) { + let encoded = uxt.encode(); + (blake2_256(&encoded).into(), encoded.len()) + } +} + +fn uxt(transfer: Transfer) -> Extrinsic { + Extrinsic::Transfer(transfer, Default::default()) +} + +fn bench_configured(pool: Pool, number: u64) { + let mut futures = Vec::new(); + let mut tags = Vec::new(); + + for nonce in 1..=number { + let xt = 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, + }); + + tags.push(to_tag(nonce, AccountId::from_h256(H256::from_low_u64_be(1)))); + futures.push(pool.submit_one(&BlockId::Number(1), xt)); + } + + 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); + + // Prune all transactions. + let block_num = 6; + block_on(pool.prune_tags( + &BlockId::Number(block_num), + tags, + vec![], + )).expect("Prune failed"); + + // pool is empty + assert_eq!(pool.status().ready, 0); + assert_eq!(pool.status().future, 0); +} + +fn benchmark_main(c: &mut Criterion) { + + c.bench_function("sequential 50 tx", |b| { + b.iter(|| { + bench_configured(Pool::new(Default::default(), TestApi::new_dependant()), 50); + }); + }); + + c.bench_function("random 100 tx", |b| { + b.iter(|| { + bench_configured(Pool::new(Default::default(), TestApi::default()), 100); + }); + }); +} + +criterion_group!(benches, benchmark_main); +criterion_main!(benches); diff --git a/core/transaction-pool/graph/src/pool.rs b/core/transaction-pool/graph/src/pool.rs index 53b2a62cbee89922f2b86c5fdbd24942c1c6e986..dcb54f710f175ed88942f89b323810bb1a20a7d1 100644 --- a/core/transaction-pool/graph/src/pool.rs +++ b/core/transaction-pool/graph/src/pool.rs @@ -66,7 +66,7 @@ pub trait ChainApi: Send + Sync { /// Error type. type Error: From + error::IntoPoolError; /// Validate transaction future. - type ValidationFuture: Future> + Send; + type ValidationFuture: Future> + Send + Unpin; /// Verify extrinsic at given block. fn validate_transaction( @@ -240,6 +240,7 @@ impl Pool { tags: impl IntoIterator, known_imported_hashes: impl IntoIterator> + Clone, ) -> impl Future> { + log::trace!(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, @@ -257,6 +258,7 @@ impl Pool { 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(); @@ -475,7 +477,6 @@ mod tests { Pool::new(Default::default(), TestApi::default()) } - #[test] fn should_validate_and_import_transaction() { // given @@ -909,3 +910,4 @@ mod tests { } } } + diff --git a/core/transaction-pool/graph/src/ready.rs b/core/transaction-pool/graph/src/ready.rs index 85bb4dd783c42292b07dfb7292bbd709de081960..3698bf447eeaaecf0a6c2ad3212b9f7091b3e871 100644 --- a/core/transaction-pool/graph/src/ready.rs +++ b/core/transaction-pool/graph/src/ready.rs @@ -338,7 +338,20 @@ impl ReadyTransactions { } } - debug!(target: "txpool", "[{:?}] Pruned.", tx.hash); + // we also need to remove all other tags that this transaction provides, + // but since all the hard work is done, we only clear the provided_tag -> hash + // mapping. + let current_tag = &tag; + for tag in &tx.provides { + let removed = self.provided_tags.remove(tag); + assert_eq!( + removed.as_ref(), + if current_tag == tag { None } else { Some(&tx.hash) }, + "The pool contains exactly one transaction providing given tag; the removed transaction + claims to provide that tag, so it has to be mapped to it's hash; qed" + ); + } + removed.push(tx); } } diff --git a/core/transaction-pool/src/tests.rs b/core/transaction-pool/src/tests.rs index d1ad27dd260f038cb2b33088cca1348921b02d24..60a9e0562fce30ce678ee0cf52ee685608482484 100644 --- a/core/transaction-pool/src/tests.rs +++ b/core/transaction-pool/src/tests.rs @@ -27,11 +27,15 @@ use sr_primitives::{ transaction_validity::{TransactionValidity, ValidTransaction}, }; -struct TestApi; +struct TestApi { + pub modifier: Box, +} impl TestApi { fn default() -> Self { - TestApi + TestApi { + modifier: Box::new(|_| {}), + } } } @@ -54,14 +58,18 @@ impl txpool::ChainApi for TestApi { }; 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(ValidTransaction { - priority: 1, - requires, - provides, - longevity: 64, - propagate: true, - }) + Ok(validity) )) } @@ -181,3 +189,34 @@ fn should_ban_invalid_transactions() { // 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/core/trie/src/node_header.rs b/core/trie/src/node_header.rs index 50d3d87250de6002c5df232bb44cc7db6c250a96..616273e574d6007106a495965d384bb0238c20fd 100644 --- a/core/trie/src/node_header.rs +++ b/core/trie/src/node_header.rs @@ -22,7 +22,7 @@ use rstd::iter::once; /// A node header #[derive(Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(primitives::RuntimeDebug)] pub(crate) enum NodeHeader { Null, Branch(bool, usize), diff --git a/core/utils/fork-tree/src/lib.rs b/core/utils/fork-tree/src/lib.rs index 42999187558fad4c4942c1036d98f0f9cc44cf04..f192ee54785b18c402e321500a93f17f6aa9aba1 100644 --- a/core/utils/fork-tree/src/lib.rs +++ b/core/utils/fork-tree/src/lib.rs @@ -86,55 +86,46 @@ impl ForkTree where N: Ord + Clone, V: Clone, { - /// Prune all nodes that are not descendents of `hash` according to - /// `is_descendent_of`. The given function `is_descendent_of` should return - /// `true` if the second hash (target) is a descendent of the first hash - /// (base). After pruning the tree it should have one or zero roots. The - /// number and order of calls to `is_descendent_of` is unspecified and - /// subject to change. - pub fn prune( + /// Prune the tree, removing all non-canonical nodes. We find the node in the + /// tree that is the deepest ancestor of the given hash and that passes the + /// given predicate. If such a node exists, we re-root the tree to this + /// node. Otherwise the tree remains unchanged. The given function + /// `is_descendent_of` should return `true` if the second hash (target) is a + /// descendent of the first hash (base). + pub fn prune( &mut self, hash: &H, - number: N, - is_descendent_of: &F + number: &N, + is_descendent_of: &F, + predicate: &P, ) -> Result<(), Error> where E: std::error::Error, - F: Fn(&H, &H) -> Result + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, { - let mut new_root = None; - for node in self.node_iter() { - // if the node has a lower number than the one being finalized then - // we only keep if it has no children and the finalized block is a - // descendent of this node - if node.number < number { - if !node.children.is_empty() || !is_descendent_of(&node.hash, hash)? { - continue; - } - } - - // if the node has the same number as the finalized block then it - // must have the same hash - if node.number == number && node.hash != *hash { - continue; - } + let new_root = self.find_node_where( + hash, + number, + is_descendent_of, + predicate, + )?; - // if the node has a higher number then we keep it if it is a - // descendent of the finalized block - if node.number > number && !is_descendent_of(hash, &node.hash)? { - continue; - } + if let Some(root) = new_root { + let mut root = root.clone(); - new_root = Some(node); - break; - } + // we found the deepest ancestor of the finalized block, so we prune + // out any children that don't include the finalized block. + let children = std::mem::replace(&mut root.children, Vec::new()); + root.children = children.into_iter().filter(|node| { + node.number == *number && node.hash == *hash || + node.number < *number && is_descendent_of(&node.hash, hash).unwrap_or(false) + }).take(1).collect(); - if let Some(root) = new_root { - self.roots = vec![root.clone()]; + self.roots = vec![root]; } Ok(()) } - } impl ForkTree where @@ -1203,18 +1194,36 @@ mod test { tree.prune( &"C", - 3, + &3, &is_descendent_of, + &|_| true, + ).unwrap(); + + assert_eq!( + tree.roots.iter().map(|node| node.hash).collect::>(), + vec!["B"], + ); + + assert_eq!( + tree.iter().map(|(hash, _, _)| *hash).collect::>(), + vec!["B", "C", "D", "E"], + ); + + tree.prune( + &"E", + &5, + &is_descendent_of, + &|_| true, ).unwrap(); assert_eq!( tree.roots.iter().map(|node| node.hash).collect::>(), - vec!["C"], + vec!["D"], ); assert_eq!( tree.iter().map(|(hash, _, _)| *hash).collect::>(), - vec!["C", "D", "E"], + vec!["D", "E"], ); } diff --git a/core/utils/wasm-builder-runner/Cargo.toml b/core/utils/wasm-builder-runner/Cargo.toml index 71cdbd283505d1d29bc763fb430aad1fc3d155e8..ab8a539054820445d744711a99192b3adf6f524f 100644 --- a/core/utils/wasm-builder-runner/Cargo.toml +++ b/core/utils/wasm-builder-runner/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-wasm-builder-runner" -version = "1.0.3" +version = "1.0.4" authors = ["Parity Technologies "] description = "Runner for substrate-wasm-builder" edition = "2018" diff --git a/core/utils/wasm-builder-runner/src/lib.rs b/core/utils/wasm-builder-runner/src/lib.rs index 1fee4a4fd7ccd6b83fc530ce17e579d79ccda484..1739c5eff211e7b5aaa92237a31f80b1a8b32b16 100644 --- a/core/utils/wasm-builder-runner/src/lib.rs +++ b/core/utils/wasm-builder-runner/src/lib.rs @@ -227,6 +227,11 @@ fn run_project(project_folder: &Path) { cmd.arg("--release"); } + // Unset the `CARGO_TARGET_DIR` to prevent a cargo deadlock (cargo locks a target dir exclusive). + // The runner project is created in `CARGO_TARGET_DIR` and executing it will create a sub target + // directory inside of `CARGO_TARGET_DIR`. + cmd.env_remove("CARGO_TARGET_DIR"); + if !cmd.status().map(|s| s.success()).unwrap_or(false) { // Don't spam the output with backtraces when a build failed! process::exit(1); @@ -255,7 +260,7 @@ fn check_provide_dummy_wasm_binary() -> bool { fn provide_dummy_wasm_binary(file_path: &Path) { fs::write( file_path, - "pub const WASM_BINARY: &[u8] = &[]; pub const WASM_BINARY_BLOATY: &[u8] = &[];" + "pub const WASM_BINARY: &[u8] = &[]; pub const WASM_BINARY_BLOATY: &[u8] = &[];", ).expect("Writing dummy WASM binary should not fail"); } diff --git a/core/utils/wasm-builder/Cargo.toml b/core/utils/wasm-builder/Cargo.toml index bbcfe2ff5288180ec58d4dc53fa060e88dffe79d..e9d38f2bd23f6168d9aacb86808e4f5d8243c3ab 100644 --- a/core/utils/wasm-builder/Cargo.toml +++ b/core/utils/wasm-builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-wasm-builder" -version = "1.0.7" +version = "1.0.8" authors = ["Parity Technologies "] description = "Utility for building WASM binaries" edition = "2018" @@ -15,3 +15,4 @@ tempfile = "3.1.0" toml = "0.5.3" walkdir = "2.2.9" fs2 = "0.4.3" +wasm-gc-api = "0.1.11" diff --git a/core/utils/wasm-builder/README.md b/core/utils/wasm-builder/README.md index 0f3d933a2f774671d1cfa44d4a918a80b0bed725..b15d2ebfab2a0dbe124593ceb97f9c9bf8a574ca 100644 --- a/core/utils/wasm-builder/README.md +++ b/core/utils/wasm-builder/README.md @@ -57,7 +57,6 @@ be `NODE_RUNTIME`. WASM builder requires the following prerequisities for building the WASM binary: - rust nightly + `wasm32-unknown-unknown` toolchain -- wasm-gc License: GPL-3.0 diff --git a/core/utils/wasm-builder/src/lib.rs b/core/utils/wasm-builder/src/lib.rs index 6f7687f446bf175dcd75c0b49413400e69fe4117..93e1700792ab4619262b49da384cb4db0e1e9785 100644 --- a/core/utils/wasm-builder/src/lib.rs +++ b/core/utils/wasm-builder/src/lib.rs @@ -75,7 +75,6 @@ //! WASM builder requires the following prerequisities for building the WASM binary: //! //! - rust nightly + `wasm32-unknown-unknown` toolchain -//! - wasm-gc //! use std::{env, fs, path::PathBuf, process::{Command, Stdio, self}}; @@ -206,9 +205,7 @@ impl CargoCommand { } fn args(&mut self, args: &[&str]) -> &mut Self { - for arg in args { - self.arg(arg); - } + args.into_iter().for_each(|a| { self.arg(a); }); self } @@ -228,12 +225,17 @@ impl CargoCommand { /// Check if the supplied cargo command is a nightly version fn is_nightly(&self) -> bool { - self.command() - .arg("--version") - .output() - .map_err(|_| ()) - .and_then(|o| String::from_utf8(o.stdout).map_err(|_| ())) - .unwrap_or_default() - .contains("-nightly") + // `RUSTC_BOOTSTRAP` tells a stable compiler to behave like a nightly. So, when this env + // variable is set, we can assume that whatever rust compiler we have, it is a nightly compiler. + // For "more" information, see: + // https://github.com/rust-lang/rust/blob/fa0f7d0080d8e7e9eb20aa9cbf8013f96c81287f/src/libsyntax/feature_gate/check.rs#L891 + env::var("RUSTC_BOOTSTRAP").is_ok() || + self.command() + .arg("--version") + .output() + .map_err(|_| ()) + .and_then(|o| String::from_utf8(o.stdout).map_err(|_| ())) + .unwrap_or_default() + .contains("-nightly") } } diff --git a/core/utils/wasm-builder/src/prerequisites.rs b/core/utils/wasm-builder/src/prerequisites.rs index eeac6df33e3d6b5d2b083d2fa2a46c6824402626..3a7f8387dc8b8c09de41b0d8cbdc2ab449765c6d 100644 --- a/core/utils/wasm-builder/src/prerequisites.rs +++ b/core/utils/wasm-builder/src/prerequisites.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::{process::{Command, Stdio}, fs}; +use std::fs; use tempfile::tempdir; @@ -23,25 +23,15 @@ use tempfile::tempdir; /// # Returns /// Returns `None` if everything was found and `Some(ERR_MSG)` if something could not be found. pub fn check() -> Option<&'static str> { - if !check_nightly_installed() { + if !check_nightly_installed(){ return Some("Rust nightly not installed, please install it!") } - if Command::new("wasm-gc") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .map(|s| !s.success()).unwrap_or(true) - { - return Some("`wasm-gc` not installed, please install it!") - } - check_wasm_toolchain_installed() } fn check_nightly_installed() -> bool { - let command = crate::get_nightly_cargo(); - command.is_nightly() + crate::get_nightly_cargo().is_nightly() } fn check_wasm_toolchain_installed() -> Option<&'static str> { diff --git a/core/utils/wasm-builder/src/wasm_project.rs b/core/utils/wasm-builder/src/wasm_project.rs index afe6faa07051c10f715b98c54f833c81bbc7f64d..1651642ee0a7b53a5c7cdd624e448357b0b123d7 100644 --- a/core/utils/wasm-builder/src/wasm_project.rs +++ b/core/utils/wasm-builder/src/wasm_project.rs @@ -16,7 +16,7 @@ use crate::write_file_if_changed; -use std::{fs, path::{Path, PathBuf}, borrow::ToOwned, process::{Command, self}, env}; +use std::{fs, path::{Path, PathBuf}, borrow::ToOwned, process, env}; use toml::value::Table; @@ -352,15 +352,8 @@ fn compact_wasm_file( .join(format!("{}.wasm", wasm_binary)); let wasm_compact_file = project.join(format!("{}.compact.wasm", wasm_binary)); - let res = Command::new("wasm-gc") - .arg(&wasm_file) - .arg(&wasm_compact_file) - .status() - .map(|s| s.success()); - - if !res.unwrap_or(false) { - panic!("Failed to compact generated WASM binary."); - } + wasm_gc::garbage_collect_file(&wasm_file, &wasm_compact_file) + .expect("Failed to compact generated WASM binary."); (WasmBinary(wasm_compact_file), WasmBinaryBloaty(wasm_file)) } diff --git a/core/wasm-interface/src/lib.rs b/core/wasm-interface/src/lib.rs index b3cbde556eea0dbfe6c985c089706d9469a9d1e0..83689ddc819a8b5c18652cf102af8b85d69ab309 100644 --- a/core/wasm-interface/src/lib.rs +++ b/core/wasm-interface/src/lib.rs @@ -49,6 +49,18 @@ pub enum Value { F64(u64), } +impl Value { + /// Returns the type of this value. + pub fn value_type(&self) -> ValueType { + match self { + Value::I32(_) => ValueType::I32, + Value::I64(_) => ValueType::I64, + Value::F32(_) => ValueType::F32, + Value::F64(_) => ValueType::F64, + } + } +} + /// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate. mod private { pub trait Sealed {} @@ -180,7 +192,7 @@ pub trait Function { fn execute( &self, context: &mut dyn FunctionContext, - args: &mut dyn Iterator, + args: &mut dyn Iterator, ) -> Result>; } @@ -212,7 +224,7 @@ pub type MemoryId = u32; pub trait Sandbox { /// Get sandbox memory from the `memory_id` instance at `offset` into the given buffer. fn memory_get( - &self, + &mut self, memory_id: MemoryId, offset: WordSize, buf_ptr: Pointer, diff --git a/node-template/Cargo.toml b/node-template/Cargo.toml index 2aa305486ce216619b756d93c5c80fa7de6c8647..ba8c4caaf856260974c835a33d6aa64ad7c873ec 100644 --- a/node-template/Cargo.toml +++ b/node-template/Cargo.toml @@ -27,13 +27,14 @@ substrate-service = { path = "../core/service" } inherents = { package = "substrate-inherents", path = "../core/inherents" } transaction-pool = { package = "substrate-transaction-pool", path = "../core/transaction-pool" } network = { package = "substrate-network", path = "../core/network" } -babe = { package = "substrate-consensus-babe", path = "../core/consensus/babe" } -babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../core/consensus/babe/primitives" } +aura = { package = "substrate-consensus-aura", path = "../core/consensus/aura" } +aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../core/consensus/aura/primitives" } grandpa = { package = "substrate-finality-grandpa", path = "../core/finality-grandpa" } grandpa-primitives = { package = "substrate-finality-grandpa-primitives", path = "../core/finality-grandpa/primitives" } substrate-client = { path = "../core/client" } basic-authorship = { package = "substrate-basic-authorship", path = "../core/basic-authorship" } -node-template-runtime = { path = "runtime" } +runtime = { package = "node-template-runtime", path = "runtime" } +sr-primitives = { path = "../core/sr-primitives" } [build-dependencies] vergen = "3.0.4" diff --git a/node-template/README.md b/node-template/README.md index 5a59652c1b352c1f2dbf64cbe360d64b1ee8c714..c411dbeef5bcccf9e05d64ddee2405cb5237c678 100644 --- a/node-template/README.md +++ b/node-template/README.md @@ -10,7 +10,7 @@ Install Rust: curl https://sh.rustup.rs -sSf | sh ``` -Install required tools: +Initialize your Wasm Build environment: ```bash ./scripts/init.sh @@ -19,17 +19,23 @@ Install required tools: Build Wasm and native code: ```bash -cargo build +cargo build --release ``` ## Run ### Single node development chain -You can start a development chain with: +Purge any existing developer chain state: ```bash -cargo run -- --dev +./target/release/node-template purge-chain --dev +``` + +Start a development chain with: + +```bash +./target/release/node-template --dev ``` Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`. diff --git a/node-template/runtime/Cargo.toml b/node-template/runtime/Cargo.toml index 18948d503cdbe51c62924a932bb7b4777c30141e..ff2e3eb2b1676ad3642c854360c929245a1a999c 100644 --- a/node-template/runtime/Cargo.toml +++ b/node-template/runtime/Cargo.toml @@ -15,20 +15,22 @@ support = { package = "srml-support", path = "../../srml/support", default_featu primitives = { package = "substrate-primitives", path = "../../core/primitives", default_features = false } substrate-session = { path = "../../core/session", default-features = false } balances = { package = "srml-balances", path = "../../srml/balances", default_features = false } -babe = { package = "srml-babe", path = "../../srml/babe", default-features = false } -babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives", default-features = false } +aura = { package = "srml-aura", path = "../../srml/aura", default_features = false } +aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default_features = false } +grandpa = { package = "srml-grandpa", path = "../../srml/grandpa", default_features = false } executive = { package = "srml-executive", path = "../../srml/executive", default_features = false } indices = { package = "srml-indices", path = "../../srml/indices", default_features = false } -grandpa = { package = "srml-grandpa", path = "../../srml/grandpa", default-features = false } +randomness-collective-flip = { package = "srml-randomness-collective-flip", path = "../../srml/randomness-collective-flip", default_features = false } system = { package = "srml-system", path = "../../srml/system", default_features = false } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default_features = false } sudo = { package = "srml-sudo", path = "../../srml/sudo", default_features = false } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment", default_features = false } sr-primitives = { path = "../../core/sr-primitives", default_features = false } client = { package = "substrate-client", path = "../../core/client", default_features = false } offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false } [build-dependencies] -wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2" } +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.4" } [features] default = ["std"] @@ -39,16 +41,18 @@ std = [ "runtime-io/std", "support/std", "balances/std", - "babe/std", - "babe-primitives/std", + "aura/std", + "aura-primitives/std", + "grandpa/std", "executive/std", "indices/std", - "grandpa/std", "primitives/std", "sr-primitives/std", + "randomness-collective-flip/std", "system/std", "timestamp/std", "sudo/std", + "transaction-payment/std", "version/std", "serde", "safe-mix/std", diff --git a/node-template/runtime/build.rs b/node-template/runtime/build.rs index ddbeefa11273dd572f97c9fdd4e027ed7f96ecc0..c5e798c6efa90a4fdb10e8ffc005afad7066d897 100644 --- a/node-template/runtime/build.rs +++ b/node-template/runtime/build.rs @@ -19,7 +19,7 @@ use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSourc fn main() { build_current_project_with_rustflags( "wasm_binary.rs", - WasmBuilderSource::Crates("1.0.7"), + WasmBuilderSource::Crates("1.0.8"), // This instructs LLD to export __heap_base as a global variable, which is used by the // external memory allocator. "-Clink-arg=--export=__heap_base", diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 6b2b335d68f3c9ab192b0bc33e9c2238712f2c4b..c0cd2cf33770cebf96060cb6f5afc035164a2b54 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -9,20 +9,22 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use rstd::prelude::*; -use primitives::{OpaqueMetadata, crypto::key_types}; +use primitives::OpaqueMetadata; use sr_primitives::{ ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, - impl_opaque_keys, AnySignature + impl_opaque_keys, MultiSignature +}; +use sr_primitives::traits::{ + NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, IdentifyAccount }; -use sr_primitives::traits::{NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto}; use sr_primitives::weights::Weight; -use babe::{AuthorityId as BabeId}; -use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; -use grandpa::fg_primitives; use client::{ block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, runtime_api as client_api, impl_runtime_apis }; +use aura_primitives::sr25519::AuthorityId as AuraId; +use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; +use grandpa::fg_primitives; use version::RuntimeVersion; #[cfg(feature = "std")] use version::NativeVersion; @@ -33,17 +35,17 @@ pub use sr_primitives::BuildStorage; pub use timestamp::Call as TimestampCall; pub use balances::Call as BalancesCall; pub use sr_primitives::{Permill, Perbill}; -pub use support::{StorageValue, construct_runtime, parameter_types}; +pub use support::{StorageValue, construct_runtime, parameter_types, traits::Randomness}; /// An index to a block. pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = AnySignature; +pub type Signature = MultiSignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. -pub type AccountId = ::Signer; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; /// The type for looking up accounts. We don't expect more than 4 billion of them, but you /// never know... @@ -80,14 +82,10 @@ pub mod opaque { /// Opaque block identifier type. pub type BlockId = generic::BlockId; - pub type SessionHandlers = (Grandpa, Babe); - impl_opaque_keys! { pub struct SessionKeys { - #[id(key_types::GRANDPA)] - pub grandpa: GrandpaId, - #[id(key_types::BABE)] - pub babe: BabeId, + pub aura: Aura, + pub grandpa: Grandpa, } } } @@ -96,40 +94,21 @@ pub mod opaque { pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node-template"), impl_name: create_runtime_str!("node-template"), - authoring_version: 3, - spec_version: 4, - impl_version: 4, + authoring_version: 1, + spec_version: 1, + impl_version: 1, apis: RUNTIME_API_VERSIONS, }; -/// Constants for Babe. - -/// Since BABE is probabilistic this is the average expected block time that -/// we are targetting. Blocks will be produced at a minimum duration defined -/// by `SLOT_DURATION`, but some slots will not be allocated to any -/// authority and hence no block will be produced. We expect to have this -/// block time on average following the defined slot duration and the value -/// of `c` configured for BABE (where `1 - c` represents the probability of -/// a slot being empty). -/// This value is only used indirectly to define the unit constants below -/// that are expressed in blocks. The rest of the code should use -/// `SLOT_DURATION` instead (like the timestamp module for calculating the -/// minimum period). -/// pub const MILLISECS_PER_BLOCK: u64 = 6000; pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; -pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; - // These time units are defined in number of blocks. pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; -// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. -pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); - /// The version infromation used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { @@ -166,29 +145,22 @@ impl system::Trait for Runtime { type Header = generic::Header; /// The ubiquitous event type. type Event = Event; - /// Update weight (to fee) multiplier per-block. - type WeightMultiplierUpdate = (); /// The ubiquitous origin type. type Origin = Origin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; - /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + /// Maximum weight of each block. type MaximumBlockWeight = MaximumBlockWeight; /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. type MaximumBlockLength = MaximumBlockLength; /// Portion of the block weight that is available to all normal transactions. type AvailableBlockRatio = AvailableBlockRatio; + /// Version of the runtime. type Version = Version; } -parameter_types! { - pub const EpochDuration: u64 = EPOCH_DURATION_IN_BLOCKS as u64; - pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; -} - -impl babe::Trait for Runtime { - type EpochDuration = EpochDuration; - type ExpectedBlockTime = ExpectedBlockTime; +impl aura::Trait for Runtime { + type AuthorityId = AuraId; } impl grandpa::Trait for Runtime { @@ -198,7 +170,7 @@ impl grandpa::Trait for Runtime { impl indices::Trait for Runtime { /// The type for recording indexing into the account enumeration. If this ever overflows, there /// will be problems! - type AccountIndex = u32; + 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. @@ -214,7 +186,7 @@ parameter_types! { impl timestamp::Trait for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; - type OnTimestampSet = Babe; + type OnTimestampSet = Aura; type MinimumPeriod = MinimumPeriod; } @@ -222,8 +194,6 @@ parameter_types! { pub const ExistentialDeposit: u128 = 500; pub const TransferFee: u128 = 0; pub const CreationFee: u128 = 0; - pub const TransactionBaseFee: u128 = 0; - pub const TransactionByteFee: u128 = 1; } impl balances::Trait for Runtime { @@ -235,15 +205,25 @@ impl balances::Trait for Runtime { type OnNewAccount = Indices; /// The ubiquitous event type. type Event = Event; - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 0; + pub const TransactionByteFee: Balance = 1; +} + +impl transaction_payment::Trait for Runtime { + type Currency = balances::Module; + type OnTransactionPayment = (); type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); } impl sudo::Trait for Runtime { @@ -264,13 +244,15 @@ construct_runtime!( { System: system::{Module, Call, Storage, Config, Event}, Timestamp: timestamp::{Module, Call, Storage, Inherent}, - Babe: babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, + Aura: aura::{Module, Config, Inherent(Timestamp)}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, Indices: indices::{default, Config}, Balances: balances::{default, Error}, + TransactionPayment: transaction_payment::{Module, Storage}, Sudo: sudo, // Used for the module template in `./template.rs` TemplateModule: template::{Module, Call, Storage, Event}, + RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage}, } ); @@ -291,7 +273,7 @@ pub type SignedExtra = ( system::CheckEra, system::CheckNonce, system::CheckWeight, - balances::TakeFees + transaction_payment::ChargeTransactionPayment ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; @@ -339,7 +321,7 @@ impl_runtime_apis! { } fn random_seed() -> ::Hash { - System::random_seed() + RandomnessCollectiveFlip::random_seed() } } @@ -355,27 +337,13 @@ impl_runtime_apis! { } } - impl fg_primitives::GrandpaApi for Runtime { - fn grandpa_authorities() -> Vec<(GrandpaId, GrandpaWeight)> { - Grandpa::grandpa_authorities() + impl aura_primitives::AuraApi for Runtime { + fn slot_duration() -> u64 { + Aura::slot_duration() } - } - impl babe_primitives::BabeApi for Runtime { - fn configuration() -> babe_primitives::BabeConfiguration { - // The choice of `c` parameter (where `1 - c` represents the - // probability of a slot being empty), is done in accordance to the - // slot duration and expected target block time, for safely - // resisting network delays of maximum two seconds. - // - babe_primitives::BabeConfiguration { - slot_duration: Babe::slot_duration(), - epoch_length: EpochDuration::get(), - c: PRIMARY_PROBABILITY, - genesis_authorities: Babe::authorities(), - randomness: Babe::randomness(), - secondary_slots: true, - } + fn authorities() -> Vec { + Aura::authorities() } } @@ -385,4 +353,10 @@ impl_runtime_apis! { opaque::SessionKeys::generate(seed) } } + + impl fg_primitives::GrandpaApi for Runtime { + fn grandpa_authorities() -> Vec<(GrandpaId, GrandpaWeight)> { + Grandpa::grandpa_authorities() + } + } } diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index 5a4225e63a29e83b0f68eac1825b828c0127d364..db7bd24b0e419268610612ae03275164cec7a9ff 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -24,8 +24,8 @@ decl_storage! { trait Store for Module as TemplateModule { // Just a dummy storage item. // Here we are declaring a StorageValue, `Something` as a Option - // `get(something)` is the default getter which returns either the stored `u32` or `None` if nothing stored - Something get(something): Option; + // `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored + Something get(fn something): Option; } } @@ -69,12 +69,11 @@ decl_event!( mod tests { use super::*; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; + use primitives::H256; use support::{impl_outer_origin, assert_ok, parameter_types}; - use sr_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header}; - use sr_primitives::weights::Weight; - use sr_primitives::Perbill; + use sr_primitives::{ + traits::{BlakeTwo256, IdentityLookup}, testing::Header, weights::Weight, Perbill, + }; impl_outer_origin! { pub enum Origin for Test {} @@ -101,7 +100,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -116,13 +114,13 @@ mod tests { // This function basically just builds a genesis storage key/value store according to // our desired mockup. - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { system::GenesisConfig::default().build_storage::().unwrap().into() } #[test] fn it_works_for_default_value() { - with_externalities(&mut new_test_ext(), || { + 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)); diff --git a/node-template/scripts/init.sh b/node-template/scripts/init.sh index cf5ecf97926fea7a5e8fd2a91df96853f90e8ee7..1405a41ef333e6af863080d83f854d3edb5fb4fa 100755 --- a/node-template/scripts/init.sh +++ b/node-template/scripts/init.sh @@ -10,7 +10,3 @@ if [ -z $CI_PROJECT_NAME ] ; then fi rustup target add wasm32-unknown-unknown --toolchain nightly - -# Install wasm-gc. It's useful for stripping slimming down wasm binaries. -command -v wasm-gc || \ - cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force diff --git a/node-template/src/chain_spec.rs b/node-template/src/chain_spec.rs index 9fdc6ee2ca6b3510eabc2ba89a7e4a8b2a7cfa18..8d43e67304c795b510777e3de154d75513f16eb1 100644 --- a/node-template/src/chain_spec.rs +++ b/node-template/src/chain_spec.rs @@ -1,11 +1,12 @@ -use primitives::{Pair, Public}; -use node_template_runtime::{ - AccountId, BabeConfig, BalancesConfig, GenesisConfig, GrandpaConfig, - SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, +use primitives::{Pair, Public, sr25519}; +use runtime::{ + AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, + SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, Signature }; -use babe_primitives::{AuthorityId as BabeId}; +use aura_primitives::sr25519::{AuthorityId as AuraId}; use grandpa_primitives::{AuthorityId as GrandpaId}; use substrate_service; +use sr_primitives::traits::{Verify, IdentifyAccount}; // Note this is the URL for the telemetry server //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; @@ -31,13 +32,20 @@ pub fn get_from_seed(seed: &str) -> ::Pu .public() } -/// Helper function to generate stash, controller and session key from seed -pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, GrandpaId, BabeId) { +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId where + AccountPublic: From<::Public> +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to generate an authority key for Aura +pub fn get_authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { ( - get_from_seed::(&format!("{}//stash", seed)), - get_from_seed::(seed), - get_from_seed::(seed), - get_from_seed::(seed), + get_from_seed::(s), + get_from_seed::(s), ) } @@ -51,12 +59,12 @@ impl Alternative { || testnet_genesis(vec![ get_authority_keys_from_seed("Alice"), ], - get_from_seed::("Alice"), + get_account_id_from_seed::("Alice"), vec![ - get_from_seed::("Alice"), - get_from_seed::("Bob"), - get_from_seed::("Alice//stash"), - get_from_seed::("Bob//stash"), + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), ], true), vec![], @@ -71,21 +79,21 @@ impl Alternative { || testnet_genesis(vec![ get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob"), - ], - get_from_seed::("Alice"), + ], + get_account_id_from_seed::("Alice"), vec![ - get_from_seed::("Alice"), - get_from_seed::("Bob"), - get_from_seed::("Charlie"), - get_from_seed::("Dave"), - get_from_seed::("Eve"), - get_from_seed::("Ferdie"), - get_from_seed::("Alice//stash"), - get_from_seed::("Bob//stash"), - get_from_seed::("Charlie//stash"), - get_from_seed::("Dave//stash"), - get_from_seed::("Eve//stash"), - get_from_seed::("Ferdie//stash"), + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), ], true), vec![], @@ -106,8 +114,8 @@ impl Alternative { } } -fn testnet_genesis(initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId)>, - root_key: AccountId, +fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>, + root_key: AccountId, endowed_accounts: Vec, _enable_println: bool) -> GenesisConfig { GenesisConfig { @@ -125,11 +133,11 @@ fn testnet_genesis(initial_authorities: Vec<(AccountId, AccountId, GrandpaId, Ba sudo: Some(SudoConfig { key: root_key, }), - babe: Some(BabeConfig { - authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), + aura: Some(AuraConfig { + authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), }), grandpa: Some(GrandpaConfig { - authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), + authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), }), } } diff --git a/node-template/src/cli.rs b/node-template/src/cli.rs index 6a0b0dd706c2dbade16e7828562f2da62cc6d22c..ec463a236b2e0f361c44589f77b6537c38ece459 100644 --- a/node-template/src/cli.rs +++ b/node-template/src/cli.rs @@ -3,8 +3,9 @@ use futures::{future, Future, sync::oneshot}; use std::cell::RefCell; use tokio::runtime::Runtime; pub use substrate_cli::{VersionInfo, IntoExit, error}; -use substrate_cli::{informant, parse_and_prepare, ParseAndPrepare, NoCustom}; +use substrate_cli::{display_role, informant, parse_and_prepare, ParseAndPrepare, NoCustom}; use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration}; +use aura_primitives::sr25519::{AuthorityPair as AuraPair}; use crate::chain_spec; use log::info; @@ -23,22 +24,22 @@ pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> info!(" by {}, 2017, 2018", version.author); info!("Chain specification: {}", config.chain_spec.name()); info!("Node name: {}", config.name); - info!("Roles: {:?}", config.roles); + info!("Roles: {}", display_role(&config)); let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; match config.roles { ServiceRoles::LIGHT => run_until_exit( runtime, - service::new_light(config).map_err(|e| format!("{:?}", e))?, + service::new_light(config)?, exit ), _ => run_until_exit( runtime, - service::new_full(config).map_err(|e| format!("{:?}", e))?, + service::new_full(config)?, exit ), - }.map_err(|e| format!("{:?}", e)) + } }), - ParseAndPrepare::BuildSpec(cmd) => cmd.run(load_spec), + 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<_>| diff --git a/node-template/src/main.rs b/node-template/src/main.rs index 024efcc7db5413eef320efa722ef3c02c5f9b42a..1f286a2237548dfc84a9c9ccce7c9f78a3eb38d4 100644 --- a/node-template/src/main.rs +++ b/node-template/src/main.rs @@ -10,7 +10,7 @@ mod cli; pub use substrate_cli::{VersionInfo, IntoExit, error}; -fn main() { +fn main() -> Result<(), cli::error::Error> { let version = VersionInfo { name: "Substrate Node", commit: env!("VERGEN_SHA_SHORT"), @@ -21,8 +21,5 @@ fn main() { support_url: "support.anonymous.an", }; - if let Err(e) = cli::run(::std::env::args(), cli::Exit, version) { - eprintln!("Fatal error: {}\n\n{:?}", e, e); - std::process::exit(1) - } + cli::run(std::env::args(), cli::Exit, version) } diff --git a/node-template/src/service.rs b/node-template/src/service.rs index 24b22082c5de7faac972bb09d56b1afa30357385..398795325fd04075ce025122209f6d6f8b3b060b 100644 --- a/node-template/src/service.rs +++ b/node-template/src/service.rs @@ -3,22 +3,21 @@ use std::sync::Arc; use std::time::Duration; use substrate_client::LongestChain; -use babe; -use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; -use futures::prelude::*; -use node_template_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi}; +use runtime::{self, GenesisConfig, opaque::Block, RuntimeApi}; use substrate_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder}; use transaction_pool::{self, txpool::{Pool as TransactionPool}}; use inherents::InherentDataProviders; -use network::construct_simple_protocol; +use network::{construct_simple_protocol}; use substrate_executor::native_executor_instance; pub use substrate_executor::NativeExecutor; +use aura_primitives::sr25519::{AuthorityPair as AuraPair}; +use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; // Our native executor instance. native_executor_instance!( pub Executor, - node_template_runtime::api::dispatch, - node_template_runtime::native_version, + runtime::api::dispatch, + runtime::native_version, ); construct_simple_protocol! { @@ -36,7 +35,7 @@ macro_rules! new_full_start { let inherent_data_providers = inherents::InherentDataProviders::new(); let builder = substrate_service::ServiceBuilder::new_full::< - node_template_runtime::opaque::Block, node_template_runtime::RuntimeApi, crate::service::Executor + runtime::opaque::Block, runtime::RuntimeApi, crate::service::Executor >($config)? .with_select_chain(|_config, backend| { Ok(substrate_client::LongestChain::new(backend.clone())) @@ -44,33 +43,26 @@ macro_rules! new_full_start { .with_transaction_pool(|config, client| Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::FullChainApi::new(client))) )? - .with_import_queue(|_config, client, mut select_chain, _transaction_pool| { + .with_import_queue(|_config, client, mut select_chain, transaction_pool| { let select_chain = select_chain.take() .ok_or_else(|| substrate_service::Error::SelectChainRequired)?; + let (grandpa_block_import, grandpa_link) = - grandpa::block_import::<_, _, _, node_template_runtime::RuntimeApi, _, _>( + grandpa::block_import::<_, _, _, runtime::RuntimeApi, _, _>( client.clone(), &*client, select_chain )?; - let justification_import = grandpa_block_import.clone(); - let (babe_block_import, babe_link) = babe::block_import( - babe::Config::get_or_compute(&*client)?, - grandpa_block_import, - client.clone(), - client.clone(), - )?; - - let import_queue = babe::import_queue( - babe_link.clone(), - babe_block_import.clone(), - Some(Box::new(justification_import)), + let import_queue = aura::import_queue::<_, _, AuraPair, _>( + aura::SlotDuration::get_or_compute(&*client)?, + Box::new(grandpa_block_import.clone()), + Some(Box::new(grandpa_block_import.clone())), None, - client.clone(), client, inherent_data_providers.clone(), + Some(transaction_pool), )?; - import_setup = Some((babe_block_import, grandpa_link, babe_link)); + import_setup = Some((grandpa_block_import, grandpa_link)); Ok(import_queue) })?; @@ -83,25 +75,29 @@ macro_rules! new_full_start { pub fn new_full(config: Configuration) -> Result { - let is_authority = config.roles.is_authority(); + let force_authoring = config.force_authoring; let name = config.name.clone(); let disable_grandpa = config.disable_grandpa; - let force_authoring = config.force_authoring; + + // sentry nodes announce themselves as authorities to the network + // and should run the same protocols authorities do, but it should + // never actively participate in any consensus process. + let participates_in_consensus = is_authority && !config.sentry_mode; let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config); + let (block_import, grandpa_link) = + import_setup.take() + .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); + let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))? .with_finality_proof_provider(|client, backend| Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _) )? .build()?; - let (block_import, grandpa_link, babe_link) = - import_setup.take() - .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); - - if is_authority { + if participates_in_consensus { let proposer = basic_authorship::ProposerFactory { client: service.client(), transaction_pool: service.transaction_pool(), @@ -111,43 +107,50 @@ pub fn new_full(config: Configuration( + aura::SlotDuration::get_or_compute(&*client)?, client, select_chain, - env: proposer, block_import, - sync_oracle: service.network(), - inherent_data_providers: inherent_data_providers.clone(), + proposer, + service.network(), + inherent_data_providers.clone(), force_authoring, - babe_link, - }; - - let babe = babe::start_babe(babe_config)?; - let select = babe.select(service.on_exit()).then(|_| Ok(())); + service.keystore(), + )?; - // the BABE authoring task is considered infallible, i.e. if it + // the AURA authoring task is considered essential, i.e. if it // fails we take down the service with it. - service.spawn_essential_task(select); + service.spawn_essential_task(aura); } + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = if participates_in_consensus { + Some(service.keystore()) + } else { + None + }; + let grandpa_config = grandpa::Config { // FIXME #1578 make this available through chainspec gossip_duration: Duration::from_millis(333), justification_period: 512, name: Some(name), - keystore: Some(service.keystore()), + observer_enabled: true, + keystore, + is_authority, }; match (is_authority, disable_grandpa) { (false, false) => { // start the lightweight GRANDPA observer - service.spawn_task(Box::new(grandpa::run_grandpa_observer( + service.spawn_task(grandpa::run_grandpa_observer( grandpa_config, grandpa_link, service.network(), service.on_exit(), - )?)); + )?); }, (true, false) => { // start the full GRANDPA voter @@ -158,6 +161,7 @@ pub fn new_full(config: Configuration(config: Configuration( client.clone(), backend, Arc::new(fetch_checker), client.clone() )?; - let finality_proof_import = grandpa_block_import.clone(); let finality_proof_request_builder = finality_proof_import.create_finality_proof_request_builder(); - let (babe_block_import, babe_link) = babe::block_import( - babe::Config::get_or_compute(&*client)?, - grandpa_block_import, - client.clone(), - client.clone(), - )?; - - let import_queue = babe::import_queue( - babe_link.clone(), - babe_block_import, + let import_queue = aura::import_queue::<_, _, AuraPair, ()>( + aura::SlotDuration::get_or_compute(&*client)?, + Box::new(grandpa_block_import), None, Some(Box::new(finality_proof_import)), - client.clone(), client, inherent_data_providers.clone(), + None, )?; Ok((import_queue, finality_proof_request_builder)) diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index ca3ec92630789cfa8e965778f7a45f40691db655..5e55d5c2085773ffc9ceae2108fa50346241b561 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -5,14 +5,26 @@ authors = ["Parity Technologies "] description = "Substrate node implementation in Rust." build = "build.rs" edition = "2018" +default-run = "substrate" + +[badges] +travis-ci = { repository = "paritytech/substrate", branch = "master" } +maintenance = { status = "actively-developed" } +is-it-maintained-issue-resolution = { repository = "paritytech/substrate" } +is-it-maintained-open-issues = { repository = "paritytech/substrate" } + +[[bin]] +name = "substrate" +path = "bin/main.rs" +required-features = ["cli"] + +[lib] +crate-type = ["cdylib", "rlib"] [dependencies] log = "0.4.8" -tokio = "0.1.22" futures = "0.1.29" -exit-future = "0.1.4" jsonrpc-core = "13.2.0" -cli = { package = "substrate-cli", path = "../../core/cli" } codec = { package = "parity-scale-codec", version = "1.0.0" } sr-io = { path = "../../core/sr-io" } client = { package = "substrate-client", path = "../../core/client" } @@ -24,7 +36,7 @@ node-primitives = { path = "../primitives" } hex-literal = "0.2.1" substrate-rpc = { package = "substrate-rpc", path = "../../core/rpc" } substrate-basic-authorship = { path = "../../core/basic-authorship" } -substrate-service = { path = "../../core/service" } +substrate-service = { path = "../../core/service", default-features = false } chain-spec = { package = "substrate-chain-spec", path = "../../core/chain-spec" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } network = { package = "substrate-network", path = "../../core/network" } @@ -35,8 +47,7 @@ grandpa_primitives = { package = "substrate-finality-grandpa-primitives", path = sr-primitives = { path = "../../core/sr-primitives" } node-executor = { path = "../executor" } substrate-telemetry = { package = "substrate-telemetry", path = "../../core/telemetry" } -structopt = "0.2.0" -transaction-factory = { path = "../../test-utils/transaction-factory" } +structopt = "0.3.3" keyring = { package = "substrate-keyring", path = "../../core/keyring" } indices = { package = "srml-indices", path = "../../srml/indices" } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default-features = false } @@ -45,14 +56,31 @@ finality_tracker = { package = "srml-finality-tracker", path = "../../srml/final contracts = { package = "srml-contracts", path = "../../srml/contracts" } system = { package = "srml-system", path = "../../srml/system" } balances = { package = "srml-balances", path = "../../srml/balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment" } support = { package = "srml-support", path = "../../srml/support", default-features = false } im_online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } -sr-authority-discovery = { package = "srml-authority-discovery", path = "../../srml/authority-discovery", default-features = false } -authority-discovery = { package = "substrate-authority-discovery", path = "../../core/authority-discovery"} serde = { version = "1.0.101", features = [ "derive" ] } -client_db = { package = "substrate-client-db", path = "../../core/client/db", features = ["kvdb-rocksdb"] } +client_db = { package = "substrate-client-db", path = "../../core/client/db", default-features = false } offchain = { package = "substrate-offchain", path = "../../core/offchain" } +# CLI-specific dependencies +tokio = { version = "0.1.22", optional = true } +exit-future = { version = "0.1.4", optional = true } +substrate-cli = { path = "../../core/cli", optional = true } +transaction-factory = { path = "../../test-utils/transaction-factory", optional = true } +ctrlc = { version = "3.1.3", features = ["termination"], optional = true } + +# WASM-specific dependencies +libp2p = { version = "0.12.0", default-features = false, optional = true } +clear_on_drop = { version = "0.2.3", features = ["no_cc"], optional = true } # Imported just for the `no_cc` feature +console_error_panic_hook = { version = "0.1.1", optional = true } +console_log = { version = "0.1.2", optional = true } +js-sys = { version = "0.3.22", optional = true } +wasm-bindgen = { version = "0.2.45", optional = true } +wasm-bindgen-futures = { version = "0.3.22", optional = true } +kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d", optional = true } +rand6 = { package = "rand", version = "0.6", features = ["wasm-bindgen"], optional = true } # Imported just for the `wasm-bindgen` feature + [dev-dependencies] keystore = { package = "substrate-keystore", path = "../../core/keystore" } babe = { package = "substrate-consensus-babe", path = "../../core/consensus/babe", features = ["test-helpers"] } @@ -62,5 +90,35 @@ futures03 = { package = "futures-preview", version = "0.3.0-alpha.19" } tempfile = "3.1.0" [build-dependencies] -cli = { package = "substrate-cli", path = "../../core/cli" } -structopt = "0.2.0" +substrate-cli = { package = "substrate-cli", path = "../../core/cli" } +structopt = "0.3.3" +vergen = "3.0.4" + +[features] +default = ["cli"] +browser = [ + "clear_on_drop", + "console_error_panic_hook", + "console_log", + "js-sys", + "libp2p", + "wasm-bindgen", + "wasm-bindgen-futures", + "kvdb-memorydb", + "rand/wasm-bindgen", + "rand6" +] +cli = [ + "substrate-cli", + "transaction-factory", + "tokio", + "exit-future", + "ctrlc", + "substrate-service/rocksdb" +] +wasmtime = [ + "cli", + "node-executor/wasmtime", + "substrate-cli/wasmtime", + "substrate-service/wasmtime", +] diff --git a/node/src/main.rs b/node/cli/bin/main.rs similarity index 89% rename from node/src/main.rs rename to node/cli/bin/main.rs index ca4a6b4c601de658896d0d9dbc43f006c2462ca7..e4415a2a89e664d21248d189381133a107c6b484 100644 --- a/node/src/main.rs +++ b/node/cli/bin/main.rs @@ -18,15 +18,15 @@ #![warn(missing_docs)] -use cli::VersionInfo; use futures::sync::oneshot; use futures::{future, Future}; +use substrate_cli::VersionInfo; use std::cell::RefCell; // handles ctrl-c struct Exit; -impl cli::IntoExit for Exit { +impl substrate_cli::IntoExit for Exit { type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; fn into_exit(self) -> Self::Exit { // can't use signal directly here because CtrlC takes only `Fn`. @@ -43,7 +43,7 @@ impl cli::IntoExit for Exit { } } -fn main() { +fn main() -> Result<(), substrate_cli::error::Error> { let version = VersionInfo { name: "Substrate Node", commit: env!("VERGEN_SHA_SHORT"), @@ -54,8 +54,5 @@ fn main() { support_url: "https://github.com/paritytech/substrate/issues/new", }; - if let Err(e) = cli::run(::std::env::args(), Exit, version) { - eprintln!("Fatal error: {}\n\n{:?}", e, e); - std::process::exit(1) - } + node_cli::run(std::env::args(), Exit, version) } diff --git a/node/cli/browser-demo/.gitignore b/node/cli/browser-demo/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0c6117d9fb83be5d944c757c10508e44b4cf2b30 --- /dev/null +++ b/node/cli/browser-demo/.gitignore @@ -0,0 +1 @@ +pkg \ No newline at end of file diff --git a/node/cli/browser-demo/README.md b/node/cli/browser-demo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4faebcbc76df4b416dd3274635c99e23b8fbd5bd --- /dev/null +++ b/node/cli/browser-demo/README.md @@ -0,0 +1,10 @@ +# How to run this demo + +```sh +cargo install wasm-pack # If necessary + +# From the `node/cli` directory (parent from this README) +wasm-pack build --target web --out-dir ./demo/pkg --no-typescript --release -- --no-default-features --features "browser" + +xdg-open index.html +``` diff --git a/node/cli/browser-demo/build.sh b/node/cli/browser-demo/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..c16100794ad2e3f5ac315d4acaf684485cfac6db --- /dev/null +++ b/node/cli/browser-demo/build.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh +wasm-pack build --target web --out-dir ./browser-demo/pkg --no-typescript --release ./.. -- --no-default-features --features "browser" +python -m SimpleHTTPServer 8000 diff --git a/node/cli/browser-demo/favicon.png b/node/cli/browser-demo/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4548ce34dfa220f612080820cfde778a39cb8f Binary files /dev/null and b/node/cli/browser-demo/favicon.png differ diff --git a/node/cli/browser-demo/index.html b/node/cli/browser-demo/index.html new file mode 100644 index 0000000000000000000000000000000000000000..cf107e64568f25c28f891c15577047fd2995a303 --- /dev/null +++ b/node/cli/browser-demo/index.html @@ -0,0 +1,39 @@ + + + + + Substrate node + + + + + diff --git a/node/cli/browser-demo/ws.js b/node/cli/browser-demo/ws.js new file mode 100644 index 0000000000000000000000000000000000000000..fa7a499a8a7a3046e72495b90c03827f3000c839 --- /dev/null +++ b/node/cli/browser-demo/ws.js @@ -0,0 +1,148 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +export default () => { + return { + dial: dial, + listen_on: (addr) => { + let err = new Error("Listening on WebSockets is not possible from within a browser"); + err.name = "NotSupportedError"; + throw err; + }, + }; +} + +/// Turns a string multiaddress into a WebSockets string URL. +// TODO: support dns addresses as well +const multiaddr_to_ws = (addr) => { + let parsed = addr.match(/^\/(ip4|ip6|dns4|dns6)\/(.*?)\/tcp\/(.*?)\/(ws|wss|x-parity-ws\/(.*)|x-parity-wss\/(.*))$/); + let proto = 'wss'; + if (parsed[4] == 'ws' || parsed[4] == 'x-parity-ws') { + proto = 'ws'; + } + let url = decodeURIComponent(parsed[5] || parsed[6] || ''); + if (parsed != null) { + if (parsed[1] == 'ip6') { + return proto + "://[" + parsed[2] + "]:" + parsed[3] + url; + } else { + return proto + "://" + parsed[2] + ":" + parsed[3] + url; + } + } + + let err = new Error("Address not supported: " + addr); + err.name = "NotSupportedError"; + throw err; +} + +// Attempt to dial a multiaddress. +const dial = (addr) => { + let ws = new WebSocket(multiaddr_to_ws(addr)); + let reader = read_queue(); + + return new Promise((resolve, reject) => { + // TODO: handle ws.onerror properly after dialing has happened + ws.onerror = (ev) => reject(ev); + ws.onmessage = (ev) => reader.inject_blob(ev.data); + ws.onclose = () => reader.inject_eof(); + ws.onopen = () => resolve({ + read: (function*() { while(ws.readyState == 1) { yield reader.next(); } })(), + write: (data) => { + if (ws.readyState == 1) { + ws.send(data); + return promise_when_ws_finished(ws); + } else { + return Promise.reject("WebSocket is closed"); + } + }, + shutdown: () => {}, + close: () => ws.close() + }); + }); +} + +// Takes a WebSocket object and returns a Promise that resolves when bufferedAmount is 0. +const promise_when_ws_finished = (ws) => { + if (ws.bufferedAmount == 0) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + setTimeout(function check() { + if (ws.bufferedAmount == 0) { + resolve(); + } else { + setTimeout(check, 100); + } + }, 2); + }) +} + +// Creates a queue reading system. +const read_queue = () => { + // State of the queue. + let state = { + // Array of promises resolving to `ArrayBuffer`s, that haven't been transmitted back with + // `next` yet. + queue: new Array(), + // If `resolve` isn't null, it is a "resolve" function of a promise that has already been + // returned by `next`. It should be called with some data. + resolve: null, + }; + + return { + // Inserts a new Blob in the queue. + inject_blob: (blob) => { + if (state.resolve != null) { + var resolve = state.resolve; + state.resolve = null; + + var reader = new FileReader(); + reader.addEventListener("loadend", () => resolve(reader.result)); + reader.readAsArrayBuffer(blob); + } else { + state.queue.push(new Promise((resolve, reject) => { + var reader = new FileReader(); + reader.addEventListener("loadend", () => resolve(reader.result)); + reader.readAsArrayBuffer(blob); + })); + } + }, + + // Inserts an EOF message in the queue. + inject_eof: () => { + if (state.resolve != null) { + var resolve = state.resolve; + state.resolve = null; + resolve(null); + } else { + state.queue.push(Promise.resolve(null)); + } + }, + + // Returns a Promise that yields the next entry as an ArrayBuffer. + next: () => { + if (state.queue.length != 0) { + return state.queue.shift(0); + } else { + if (state.resolve !== null) + throw "Internal error: already have a pending promise"; + return new Promise((resolve, reject) => { + state.resolve = resolve; + }); + } + } + }; +}; diff --git a/node/cli/build.rs b/node/cli/build.rs index e7a7b271f15034498ad00dbf17827a96a7a0a127..8ef644eed5d94cb5e4ee0dd34068c039f6d451d9 100644 --- a/node/cli/build.rs +++ b/node/cli/build.rs @@ -14,14 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use cli::{NoCustom, CoreParams}; - use std::{fs, env, path::Path}; - use structopt::{StructOpt, clap::Shell}; +use substrate_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"); + println!("cargo:rerun-if-changed=.git/HEAD"); } /// Build shell completion scripts for all known shells diff --git a/node/cli/src/browser.rs b/node/cli/src/browser.rs new file mode 100644 index 0000000000000000000000000000000000000000..702d67b55afa5dfd2d15e443c1b4e509d7c163c2 --- /dev/null +++ b/node/cli/src/browser.rs @@ -0,0 +1,159 @@ +// 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 . + +use crate::ChainSpec; +use futures::{prelude::*, sync::oneshot, sync::mpsc}; +use libp2p::wasm_ext; +use log::{debug, info}; +use std::sync::Arc; +use substrate_service::{AbstractService, RpcSession, Roles as ServiceRoles, Configuration, config::DatabaseConfig}; +use wasm_bindgen::prelude::*; + +/// Starts the client. +/// +/// You must pass a libp2p transport that supports . +#[wasm_bindgen] +pub fn start_client(wasm_ext: wasm_ext::ffi::Transport) -> Result { + start_inner(wasm_ext) + .map_err(|err| JsValue::from_str(&err.to_string())) +} + +fn start_inner(wasm_ext: wasm_ext::ffi::Transport) -> Result> { + console_error_panic_hook::set_once(); + console_log::init_with_level(log::Level::Info); + + // Build the configuration to pass to the service. + let config = { + let wasm_ext = wasm_ext::ExtTransport::new(wasm_ext); + let chain_spec = ChainSpec::FlamingFir.load().map_err(|e| format!("{:?}", e))?; + let mut config = Configuration::<(), _, _>::default_with_spec_and_base_path(chain_spec, None); + config.network.transport = network::config::TransportConfig::Normal { + wasm_external_transport: Some(wasm_ext.clone()), + enable_mdns: false, + }; + config.telemetry_external_transport = Some(wasm_ext); + config.roles = ServiceRoles::LIGHT; + config.name = "Browser node".to_string(); + config.database = { + let db = Arc::new(kvdb_memorydb::create(10)); + DatabaseConfig::Custom(db) + }; + config + }; + + info!("Substrate browser node"); + 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: {:?}", config.roles); + + // Create the service. This is the most heavy initialization step. + let mut service = crate::service::new_light(config).map_err(|e| format!("{:?}", e))?; + + // We now dispatch a background task responsible for processing the service. + // + // The main action performed by the code below consists in polling the service with + // `service.poll()`. + // The rest consists in handling RPC requests. + let (rpc_send_tx, mut rpc_send_rx) = mpsc::unbounded::(); + wasm_bindgen_futures::spawn_local(futures::future::poll_fn(move || { + loop { + match rpc_send_rx.poll() { + Ok(Async::Ready(Some(message))) => { + let fut = service.rpc_query(&message.session, &message.rpc_json); + let _ = message.send_back.send(Box::new(fut)); + }, + Ok(Async::NotReady) => break, + Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), + } + } + + loop { + match service.poll().map_err(|_| ())? { + Async::Ready(()) => return Ok(Async::Ready(())), + Async::NotReady => break + } + } + + Ok(Async::NotReady) + })); + + Ok(Client { + rpc_send_tx, + }) +} + +/// A running client. +#[wasm_bindgen] +pub struct Client { + rpc_send_tx: mpsc::UnboundedSender, +} + +struct RpcMessage { + rpc_json: String, + session: RpcSession, + send_back: oneshot::Sender, Error = ()>>>, +} + +#[wasm_bindgen] +impl Client { + /// Allows starting an RPC request. Returns a `Promise` containing the result of that request. + #[wasm_bindgen(js_name = "rpcSend")] + pub fn rpc_send(&mut self, rpc: &str) -> js_sys::Promise { + let rpc_session = RpcSession::new(mpsc::channel(1).0); + let (tx, rx) = oneshot::channel(); + let _ = self.rpc_send_tx.unbounded_send(RpcMessage { + rpc_json: rpc.to_owned(), + session: rpc_session, + send_back: tx, + }); + let fut = rx + .map_err(|_| ()) + .and_then(|fut| fut) + .map(|s| JsValue::from_str(&s.unwrap_or(String::new()))) + .map_err(|_| JsValue::NULL); + wasm_bindgen_futures::future_to_promise(fut) + } + + /// Subscribes to an RPC pubsub endpoint. + #[wasm_bindgen(js_name = "rpcSubscribe")] + pub fn rpc_subscribe(&mut self, rpc: &str, callback: js_sys::Function) { + let (tx, rx) = mpsc::channel(4); + let rpc_session = RpcSession::new(tx); + let (fut_tx, fut_rx) = oneshot::channel(); + let _ = self.rpc_send_tx.unbounded_send(RpcMessage { + rpc_json: rpc.to_owned(), + session: rpc_session.clone(), + send_back: fut_tx, + }); + let fut_rx = fut_rx + .map_err(|_| ()) + .and_then(|fut| fut); + wasm_bindgen_futures::spawn_local(fut_rx.then(|_| Ok(()))); + wasm_bindgen_futures::spawn_local(rx.for_each(move |s| { + match callback.call1(&callback, &JsValue::from_str(&s)) { + Ok(_) => Ok(()), + Err(_) => Err(()), + } + }).then(move |v| { + // We need to keep `rpc_session` alive. + debug!("RPC subscription has ended"); + drop(rpc_session); + v + })); + } +} diff --git a/node/cli/src/chain_spec.rs b/node/cli/src/chain_spec.rs index 905e867436e593e68759fbb4f35d015f3fa4f9e8..04fb41c211009b4856dac0c3241593f5b8e26a3f 100644 --- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -17,26 +17,28 @@ //! Substrate chain configurations. use chain_spec::ChainSpecExtension; -use primitives::{Pair, Public, crypto::UncheckedInto}; +use primitives::{Pair, Public, crypto::UncheckedInto, sr25519}; use serde::{Serialize, Deserialize}; use node_runtime::{ - AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, - ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, - StakingConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, + BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, GrandpaConfig, + ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, + SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, }; use node_runtime::Block; -use node_runtime::constants::{time::*, currency::*}; +use node_runtime::constants::currency::*; use substrate_service; use hex_literal::hex; use substrate_telemetry::TelemetryEndpoints; use grandpa_primitives::{AuthorityId as GrandpaId}; use babe_primitives::{AuthorityId as BabeId}; use im_online::sr25519::{AuthorityId as ImOnlineId}; -use sr_primitives::Perbill; +use sr_primitives::{Perbill, traits::{Verify, IdentifyAccount}}; -pub use node_primitives::{AccountId, Balance}; +pub use node_primitives::{AccountId, Balance, Signature}; pub use node_runtime::GenesisConfig; +type AccountPublic = ::Signer; + const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; /// Node `ChainSpec` extensions. @@ -72,9 +74,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)> = vec![( // 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy - hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].unchecked_into(), + hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].into(), // 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq - hex!["781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"].unchecked_into(), + hex!["781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"].into(), // 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC hex!["9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332"].unchecked_into(), // 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8 @@ -83,9 +85,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), ),( // 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2 - hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].unchecked_into(), + hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].into(), // 5Gc4vr42hH1uDZc93Nayk5G7i687bAQdHHc9unLuyeawHipF - hex!["c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"].unchecked_into(), + hex!["c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"].into(), // 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE hex!["7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f"].unchecked_into(), // 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ @@ -94,9 +96,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), ),( // 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp - hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].unchecked_into(), + hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].into(), // 5FeD54vGVNpFX3PndHPXJ2MDakc462vBCD5mgtWRnWYCpZU9 - hex!["9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"].unchecked_into(), + hex!["9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"].into(), // 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d hex!["5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440"].unchecked_into(), // 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH @@ -105,9 +107,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), ),( // 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9 - hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].unchecked_into(), + hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].into(), // 5EPQdAQ39WQNLCRjWsCk5jErsCitHiY5ZmjfWzzbXDoAoYbn - hex!["66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"].unchecked_into(), + hex!["66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"].into(), // 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4 hex!["3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef"].unchecked_into(), // 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x @@ -117,83 +119,19 @@ fn staging_testnet_config_genesis() -> GenesisConfig { )]; // generated with secret: subkey inspect "$secret"/fir - let endowed_accounts: Vec = vec![ + let root_key: AccountId = hex![ // 5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo - hex!["9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809"].unchecked_into(), - ]; + "9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809" + ].into(); - const ENDOWMENT: Balance = 10_000_000 * DOLLARS; - const STASH: Balance = 100 * DOLLARS; + let endowed_accounts: Vec = vec![root_key.clone()]; - GenesisConfig { - system: Some(SystemConfig { - code: WASM_BINARY.to_vec(), - changes_trie_config: Default::default(), - }), - balances: Some(BalancesConfig { - balances: endowed_accounts.iter().cloned() - .map(|k| (k, ENDOWMENT)) - .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) - .collect(), - vesting: vec![], - }), - indices: Some(IndicesConfig { - ids: endowed_accounts.iter().cloned() - .chain(initial_authorities.iter().map(|x| x.0.clone())) - .collect::>(), - }), - session: Some(SessionConfig { - keys: initial_authorities.iter().map(|x| { - (x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone())) - }).collect::>(), - }), - staking: Some(StakingConfig { - current_era: 0, - validator_count: 7, - minimum_validator_count: 4, - stakers: initial_authorities.iter().map(|x| { - (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator) - }).collect(), - invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), - slash_reward_fraction: Perbill::from_percent(10), - .. Default::default() - }), - democracy: Some(DemocracyConfig::default()), - collective_Instance1: Some(CouncilConfig { - members: vec![], - phantom: Default::default(), - }), - collective_Instance2: Some(TechnicalCommitteeConfig { - members: vec![], - phantom: Default::default(), - }), - elections: Some(ElectionsConfig { - members: vec![], - presentation_duration: 1 * DAYS, - term_duration: 28 * DAYS, - desired_seats: 0, - }), - contracts: Some(ContractsConfig { - current_schedule: Default::default(), - gas_price: 1 * MILLICENTS, - }), - sudo: Some(SudoConfig { - key: endowed_accounts[0].clone(), - }), - babe: Some(BabeConfig { - authorities: vec![], - }), - im_online: Some(ImOnlineConfig { - keys: vec![], - }), - authority_discovery: Some(AuthorityDiscoveryConfig{ - keys: vec![], - }), - grandpa: Some(GrandpaConfig { - authorities: vec![], - }), - membership_Instance1: Some(Default::default()), - } + testnet_genesis( + initial_authorities, + root_key, + Some(endowed_accounts), + false, + ) } /// Staging testnet config. @@ -218,12 +156,18 @@ pub fn get_from_seed(seed: &str) -> ::Pu .public() } +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId where + AccountPublic: From<::Public> +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} /// Helper function to generate stash, controller and session key from seed pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId) { ( - get_from_seed::(&format!("{}//stash", seed)), - get_from_seed::(seed), + get_account_id_from_seed::(&format!("{}//stash", seed)), + get_account_id_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), @@ -239,38 +183,41 @@ pub fn testnet_genesis( ) -> GenesisConfig { let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(|| { vec![ - get_from_seed::("Alice"), - get_from_seed::("Bob"), - get_from_seed::("Charlie"), - get_from_seed::("Dave"), - get_from_seed::("Eve"), - get_from_seed::("Ferdie"), - get_from_seed::("Alice//stash"), - get_from_seed::("Bob//stash"), - get_from_seed::("Charlie//stash"), - get_from_seed::("Dave//stash"), - get_from_seed::("Eve//stash"), - get_from_seed::("Ferdie//stash"), + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), ] }); const ENDOWMENT: Balance = 10_000_000 * DOLLARS; const STASH: Balance = 100 * DOLLARS; - let desired_seats = (endowed_accounts.len() / 2 - initial_authorities.len()) as u32; - GenesisConfig { system: Some(SystemConfig { code: WASM_BINARY.to_vec(), changes_trie_config: Default::default(), }), - indices: Some(IndicesConfig { - ids: endowed_accounts.clone(), - }), balances: Some(BalancesConfig { - balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), + balances: endowed_accounts.iter().cloned() + .map(|k| (k, ENDOWMENT)) + .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) + .collect(), vesting: vec![], }), + indices: Some(IndicesConfig { + ids: endowed_accounts.iter().cloned() + .chain(initial_authorities.iter().map(|x| x.0.clone())) + .collect::>(), + }), session: Some(SessionConfig { keys: initial_authorities.iter().map(|x| { (x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone())) @@ -278,8 +225,8 @@ pub fn testnet_genesis( }), staking: Some(StakingConfig { current_era: 0, - minimum_validator_count: 1, - validator_count: 2, + validator_count: initial_authorities.len() as u32 * 2, + minimum_validator_count: initial_authorities.len() as u32, stakers: initial_authorities.iter().map(|x| { (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator) }).collect(), @@ -296,14 +243,6 @@ pub fn testnet_genesis( members: vec![], phantom: Default::default(), }), - elections: Some(ElectionsConfig { - members: endowed_accounts.iter() - .filter(|&endowed| initial_authorities.iter().find(|&(_, controller, ..)| controller == endowed).is_none()) - .map(|a| (a.clone(), 1000000)).collect(), - presentation_duration: 10, - term_duration: 1000000, - desired_seats: desired_seats, - }), contracts: Some(ContractsConfig { current_schedule: contracts::Schedule { enable_println, // this should only be enabled on development chains @@ -320,13 +259,11 @@ pub fn testnet_genesis( im_online: Some(ImOnlineConfig { keys: vec![], }), - authority_discovery: Some(AuthorityDiscoveryConfig{ - keys: vec![], - }), grandpa: Some(GrandpaConfig { authorities: vec![], }), membership_Instance1: Some(Default::default()), + treasury: Some(Default::default()), } } @@ -335,7 +272,7 @@ fn development_config_genesis() -> GenesisConfig { vec![ get_authority_keys_from_seed("Alice"), ], - get_from_seed::("Alice"), + get_account_id_from_seed::("Alice"), None, true, ) @@ -361,7 +298,7 @@ fn local_testnet_genesis() -> GenesisConfig { get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob"), ], - get_from_seed::("Alice"), + get_account_id_from_seed::("Alice"), None, false, ) @@ -393,7 +330,7 @@ pub(crate) mod tests { vec![ get_authority_keys_from_seed("Alice"), ], - get_from_seed::("Alice"), + get_account_id_from_seed::("Alice"), None, false, ) diff --git a/node/cli/src/cli.rs b/node/cli/src/cli.rs new file mode 100644 index 0000000000000000000000000000000000000000..4bddb50b4bcac95d6c9084cbd398dcdb5d037318 --- /dev/null +++ b/node/cli/src/cli.rs @@ -0,0 +1,210 @@ +// Copyright 2018-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 . + +pub use substrate_cli::error; +use tokio::prelude::Future; +use tokio::runtime::{Builder as RuntimeBuilder, Runtime}; +pub use substrate_cli::{VersionInfo, IntoExit, NoCustom, SharedParams, ExecutionStrategyParam}; +use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration}; +use log::info; +use structopt::{StructOpt, clap::App}; +use substrate_cli::{display_role, parse_and_prepare, AugmentClap, GetLogFilter, ParseAndPrepare}; +use crate::{service, ChainSpec, load_spec}; +use crate::factory_impl::FactoryState; +use transaction_factory::RuntimeAdapter; +use client::ExecutionStrategies; + +/// Custom subcommands. +#[derive(Clone, Debug, StructOpt)] +pub enum CustomSubcommands { + /// The custom factory subcommmand for manufacturing transactions. + #[structopt( + name = "factory", + about = "Manufactures num transactions from Alice to random accounts. \ + Only supported for development or local testnet." + )] + Factory(FactoryCmd), +} + +impl GetLogFilter for CustomSubcommands { + fn get_log_filter(&self) -> Option { + None + } +} + +/// 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: transaction_factory::Mode, + + /// 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, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + /// The means of execution used when calling into the runtime while importing blocks. + #[structopt( + long = "execution", + value_name = "STRATEGY", + possible_values = &ExecutionStrategyParam::variants(), + case_insensitive = true, + default_value = "NativeElseWasm" + )] + pub execution: ExecutionStrategyParam, +} + +impl AugmentClap for FactoryCmd { + fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + FactoryCmd::augment_clap(app) + } +} + +/// Parse command line arguments into service configuration. +pub fn run(args: I, exit: E, version: substrate_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, 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().name_prefix("main-tokio-").build() + .map_err(|e| format!("{:?}", e))?; + 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::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<_, _> = substrate_cli::create_config_with_db_path( + load_spec, + &cli_args.shared_params, + &version, + )?; + config.execution_strategies = ExecutionStrategies { + importing: cli_args.execution.into(), + block_construction: cli_args.execution.into(), + other: cli_args.execution.into(), + ..Default::default() + }; + + 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; + 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) = exit_future::signal(); + + let informant = substrate_cli::informant::build(&service); + runtime.executor().spawn(exit.until(informant).map(|_| ())); + + // 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 service_res = { + let exit = e.into_exit().map_err(|_| error::Error::Other("Exit future failed.".into())); + let service = service.map_err(|err| error::Error::Service(err)); + let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err); + runtime.block_on(select) + }; + + exit_send.fire(); + + // TODO [andre]: timeout this future #1318 + let _ = runtime.shutdown_on_idle().wait(); + + service_res +} diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index 827b8e308c8f18dc5a688a7c4a1c1542e148184b..48fb7b237f1e67b5a6d0613dda0c4ff362fcecac 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -25,16 +25,21 @@ use codec::{Encode, Decode}; use keyring::sr25519::Keyring; use node_runtime::{ Call, CheckedExtrinsic, UncheckedExtrinsic, SignedExtra, BalancesCall, ExistentialDeposit, - MinimumPeriod, + MinimumPeriod }; +use node_primitives::Signature; use primitives::{sr25519, crypto::Pair}; -use sr_primitives::{generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension}}; +use sr_primitives::{ + generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension, Verify, IdentifyAccount} +}; use transaction_factory::RuntimeAdapter; use transaction_factory::modes::Mode; use inherents::InherentData; use timestamp; use finality_tracker; +type AccountPublic = ::Signer; + pub struct FactoryState { block_no: N, @@ -56,7 +61,7 @@ impl FactoryState { system::CheckEra::from(Era::mortal(256, phase)), system::CheckNonce::from(index), system::CheckWeight::new(), - balances::TakeFees::from(0), + transaction_payment::ChargeTransactionPayment::from(0), Default::default(), ) } @@ -167,7 +172,7 @@ impl RuntimeAdapter for FactoryState { } fn master_account_id() -> Self::AccountId { - Keyring::Alice.pair().public() + Keyring::Alice.to_account_id() } fn master_account_secret() -> Self::Secret { @@ -177,7 +182,7 @@ 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)); - pair.public().into() + AccountPublic::from(pair.public()).into_account() } /// Generates a random `Secret` from `seed`. diff --git a/node/cli/src/lib.rs b/node/cli/src/lib.rs index a4deed37c1e37931751b605c7f1a26a568e6ec91..78660ae92e3c5d00d066dddf82e4eb5e2044b036 100644 --- a/node/cli/src/lib.rs +++ b/node/cli/src/lib.rs @@ -15,26 +15,35 @@ // along with Substrate. If not, see . //! Substrate CLI library. +//! +//! This package has two Cargo features: +//! +//! - `cli` (default): exposes functions that parse command-line options, then start and run the +//! node as a CLI application. +//! +//! - `browser`: exposes the content of the `browser` module, which consists of exported symbols +//! that are meant to be passed through the `wasm-bindgen` utility and called from JavaScript. +//! Despite its name the produced WASM can theoretically also be used from NodeJS, although this +//! hasn't been tested. #![warn(missing_docs)] #![warn(unused_extern_crates)] -pub use cli::error; pub mod chain_spec; + #[macro_use] mod service; +#[cfg(feature = "browser")] +mod browser; +#[cfg(feature = "cli")] +mod cli; +#[cfg(feature = "cli")] mod factory_impl; -use tokio::prelude::Future; -use tokio::runtime::{Builder as RuntimeBuilder, Runtime}; -pub use cli::{VersionInfo, IntoExit, NoCustom, SharedParams, ExecutionStrategyParam}; -use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration}; -use log::info; -use structopt::{StructOpt, clap::App}; -use cli::{AugmentClap, GetLogFilter, parse_and_prepare, ParseAndPrepare}; -use crate::factory_impl::FactoryState; -use transaction_factory::RuntimeAdapter; -use client::ExecutionStrategies; +#[cfg(feature = "browser")] +pub use browser::*; +#[cfg(feature = "cli")] +pub use cli::*; /// The chain specification option. #[derive(Clone, Debug, PartialEq)] @@ -49,80 +58,6 @@ pub enum ChainSpec { StagingTestnet, } -/// Custom subcommands. -#[derive(Clone, Debug, StructOpt)] -pub enum CustomSubcommands { - /// The custom factory subcommmand for manufacturing transactions. - #[structopt( - name = "factory", - about = "Manufactures num transactions from Alice to random accounts. \ - Only supported for development or local testnet." - )] - Factory(FactoryCmd), -} - -impl GetLogFilter for CustomSubcommands { - fn get_log_filter(&self) -> Option { - None - } -} - -/// 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: transaction_factory::Mode, - - /// 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, - - #[allow(missing_docs)] - #[structopt(flatten)] - pub shared_params: SharedParams, - - /// The means of execution used when calling into the runtime while importing blocks. - #[structopt( - long = "execution", - value_name = "STRATEGY", - raw( - possible_values = "&ExecutionStrategyParam::variants()", - case_insensitive = "true", - default_value = r#""NativeElseWasm""# - ) - )] - pub execution: ExecutionStrategyParam, -} - -impl AugmentClap for FactoryCmd { - fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { - FactoryCmd::augment_clap(app) - } -} - /// Get a chain config from a spec setting. impl ChainSpec { pub(crate) fn load(self) -> Result { @@ -151,113 +86,3 @@ fn load_spec(id: &str) -> Result, String> { None => None, }) } - -/// Parse command line arguments into service configuration. -pub fn run(args: I, exit: E, version: 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, 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: {:?}", config.roles); - let runtime = RuntimeBuilder::new().name_prefix("main-tokio-").build() - .map_err(|e| format!("{:?}", e))?; - match config.roles { - ServiceRoles::LIGHT => run_until_exit( - runtime, - service::new_light(config).map_err(|e| format!("{:?}", e))?, - exit - ), - _ => run_until_exit( - runtime, - service::new_full(config).map_err(|e| format!("{:?}", e))?, - exit - ), - }.map_err(|e| format!("{:?}", e)) - }), - 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::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<_, _> = cli::create_config_with_db_path( - load_spec, - &cli_args.shared_params, - &version, - )?; - config.execution_strategies = ExecutionStrategies { - importing: cli_args.execution.into(), - block_construction: cli_args.execution.into(), - other: cli_args.execution.into(), - ..Default::default() - }; - - 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; - 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) = exit_future::signal(); - - let informant = cli::informant::build(&service); - runtime.executor().spawn(exit.until(informant).map(|_| ())); - - // 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 service_res = { - let exit = e.into_exit().map_err(|_| error::Error::Other("Exit future failed.".into())); - let service = service.map_err(|err| error::Error::Service(err)); - let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err); - runtime.block_on(select) - }; - - exit_send.fire(); - - // TODO [andre]: timeout this future #1318 - let _ = runtime.shutdown_on_idle().wait(); - - service_res -} diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index 7012a3d6ce0ad3e98ae54f6d8b786a865a05ce0f..09056591143ec668e1e8c652af03d7e94c827be8 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -33,7 +33,7 @@ use transaction_pool::{self, txpool::{Pool as TransactionPool}}; use inherents::InherentDataProviders; use network::construct_simple_protocol; -use substrate_service::{NewService, NetworkStatus}; +use substrate_service::{Service, NetworkStatus}; use client::{Client, LocalCallExecutor}; use client_db::Backend; use sr_primitives::traits::Block as BlockT; @@ -95,7 +95,7 @@ macro_rules! new_full_start { import_setup = Some((block_import, grandpa_link, babe_link)); Ok(import_queue) })? - .with_rpc_extensions(|client, pool| -> RpcExtension { + .with_rpc_extensions(|client, pool, _backend| -> RpcExtension { node_rpc::create(client, pool) })?; @@ -124,14 +124,19 @@ macro_rules! new_full { $config.disable_grandpa ); + // sentry nodes announce themselves as authorities to the network + // and should run the same protocols authorities do, but it should + // never actively participate in any consensus process. + let participates_in_consensus = is_authority && !$config.sentry_mode; + let (builder, mut import_setup, inherent_data_providers) = new_full_start!($config); // Dht event channel from the network to the authority discovery module. Use bounded channel to ensure // back-pressure. Authority discovery is triggering one event per authority within the current authority set. // This estimates the authority set size to be somewhere below 10 000 thereby setting the channel buffer size to // 10 000. - let (dht_event_tx, dht_event_rx) = - mpsc::channel::(10000); + let (dht_event_tx, _dht_event_rx) = + mpsc::channel::(10_000); let service = builder.with_network_protocol(|_| Ok(crate::service::NodeProtocol::new()))? .with_finality_proof_provider(|client, backend| @@ -145,7 +150,7 @@ macro_rules! new_full { ($with_startup_data)(&block_import, &babe_link); - if is_authority { + if participates_in_consensus { let proposer = substrate_basic_authorship::ProposerFactory { client: service.client(), transaction_pool: service.transaction_pool(), @@ -169,32 +174,35 @@ macro_rules! new_full { let babe = babe::start_babe(babe_config)?; service.spawn_essential_task(babe); - - let authority_discovery = authority_discovery::AuthorityDiscovery::new( - service.client(), - service.network(), - dht_event_rx, - ); - service.spawn_task(authority_discovery); } + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = if participates_in_consensus { + Some(service.keystore()) + } else { + None + }; + let config = grandpa::Config { // FIXME #1578 make this available through chainspec gossip_duration: std::time::Duration::from_millis(333), justification_period: 512, name: Some(name), - keystore: Some(service.keystore()), + observer_enabled: true, + keystore, + is_authority, }; match (is_authority, disable_grandpa) { (false, false) => { // start the lightweight GRANDPA observer - service.spawn_task(Box::new(grandpa::run_grandpa_observer( + service.spawn_task(grandpa::run_grandpa_observer( config, grandpa_link, service.network(), service.on_exit(), - )?)); + )?); }, (true, false) => { // start the full GRANDPA voter @@ -205,8 +213,11 @@ macro_rules! new_full { inherent_data_providers: inherent_data_providers.clone(), on_exit: service.on_exit(), telemetry_on_connect: Some(service.telemetry_on_connect_stream()), + voting_rule: grandpa::VotingRulesBuilder::default().build(), }; - service.spawn_task(Box::new(grandpa::run_grandpa_voter(grandpa_config)?)); + // 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)?); }, (_, true) => { grandpa::setup_disabled_grandpa( @@ -244,7 +255,7 @@ pub type NodeConfiguration = Configuration(config: NodeConfiguration) -> Result< - NewService< + Service< ConcreteBlock, ConcreteClient, LongestChain, @@ -311,7 +322,7 @@ 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| -> RpcExtension { + .with_rpc_extensions(|client, pool, _backend| -> RpcExtension { node_rpc::create(client, pool) })? .build()?; @@ -326,17 +337,15 @@ mod tests { use consensus_common::{ Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy, BlockImport, }; - use node_primitives::{Block, DigestItem}; - use node_runtime::{BalancesCall, Call, UncheckedExtrinsic}; + use node_primitives::{Block, DigestItem, Signature}; + use node_runtime::{BalancesCall, Call, UncheckedExtrinsic, Address}; use node_runtime::constants::{currency::CENTS, time::SLOT_DURATION}; use codec::{Encode, Decode}; - use primitives::{ - crypto::Pair as CryptoPair, - sr25519::Public as AddressPublic, H256, - }; + use primitives::{crypto::Pair as CryptoPair, H256}; use sr_primitives::{ generic::{BlockId, Era, Digest, SignedPayload}, traits::Block as BlockT, + traits::Verify, OpaqueExtrinsic, }; use timestamp; @@ -344,6 +353,9 @@ mod tests { use keyring::AccountKeyring; use substrate_service::{AbstractService, Roles}; use crate::service::new_full; + use sr_primitives::traits::IdentifyAccount; + + type AccountPublic = ::Signer; #[cfg(feature = "rhd")] fn test_sync() { @@ -514,8 +526,8 @@ mod tests { }, |service, _| { let amount = 5 * CENTS; - let to = AddressPublic::from_raw(bob.public().0); - let from = AddressPublic::from_raw(charlie.public().0); + let to: Address = AccountPublic::from(bob.public()).into_account().into(); + let from: Address = AccountPublic::from(charlie.public()).into_account().into(); let genesis_hash = service.client().block_hash(0).unwrap().unwrap(); let best_block_id = BlockId::number(service.client().info().chain.best_number); let version = service.client().runtime_version_at(&best_block_id).unwrap().spec_version; @@ -528,14 +540,14 @@ mod tests { let check_era = system::CheckEra::from(Era::Immortal); let check_nonce = system::CheckNonce::from(index); let check_weight = system::CheckWeight::new(); - let take_fees = balances::TakeFees::from(0); + let payment = transaction_payment::ChargeTransactionPayment::from(0); let extra = ( check_version, check_genesis, check_era, check_nonce, check_weight, - take_fees, + payment, Default::default(), ); let raw_payload = SignedPayload::from_raw( diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml index 63f1c8c0f4f3a9b3101558a029e17be5a19e7907..93f29910edbe05edfa66e4d0772315a7bd234914 100644 --- a/node/executor/Cargo.toml +++ b/node/executor/Cargo.toml @@ -22,6 +22,7 @@ test-client = { package = "substrate-test-client", path = "../../core/test-clien sr-primitives = { path = "../../core/sr-primitives" } runtime_support = { package = "srml-support", path = "../../srml/support" } balances = { package = "srml-balances", path = "../../srml/balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment" } session = { package = "srml-session", path = "../../srml/session" } system = { package = "srml-system", path = "../../srml/system" } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp" } @@ -30,7 +31,15 @@ contracts = { package = "srml-contracts", path = "../../srml/contracts" } grandpa = { package = "srml-grandpa", path = "../../srml/grandpa" } indices = { package = "srml-indices", path = "../../srml/indices" } wabt = "0.9.2" +criterion = "0.3.0" [features] -benchmarks = [] +wasmtime = [ + "substrate-executor/wasmtime", +] stress-test = [] + +[[bench]] +name = "bench" +harness = false + diff --git a/node/executor/benches/bench.rs b/node/executor/benches/bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..e72c28467fa7f6fe624ba3514ebe00ca91a33ebe --- /dev/null +++ b/node/executor/benches/bench.rs @@ -0,0 +1,196 @@ +// Copyright 2018-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 . + +use codec::{Decode, Encode}; +use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use node_executor::Executor; +use node_primitives::{BlockNumber, Hash}; +use node_runtime::{ + Block, BuildStorage, Call, CheckedExtrinsic, GenesisConfig, Header, UncheckedExtrinsic, +}; +use node_runtime::constants::currency::*; +use node_testing::keyring::*; +use primitives::{Blake2Hasher, NativeOrEncoded, NeverNativeValue}; +use primitives::storage::well_known_keys; +use primitives::traits::CodeExecutor; +use runtime_support::Hashable; +use state_machine::TestExternalities as CoreTestExternalities; +use substrate_executor::{NativeExecutor, RuntimeInfo, WasmExecutionMethod, Externalities}; + +criterion_group!(benches, bench_execute_block); +criterion_main!(benches); + +/// The wasm runtime code. +const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; + +const GENESIS_HASH: [u8; 32] = [69u8; 32]; + +const VERSION: u32 = node_runtime::VERSION.spec_version; + +const HEAP_PAGES: u64 = 20; + +type TestExternalities = CoreTestExternalities; + +#[derive(Debug)] +enum ExecutionMethod { + Native, + Wasm(WasmExecutionMethod), +} + +fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { + node_testing::keyring::sign(xt, VERSION, GENESIS_HASH) +} + +fn new_test_ext(genesis_config: &GenesisConfig) -> TestExternalities { + let mut test_ext = TestExternalities::new_with_code( + COMPACT_CODE, + genesis_config.build_storage().unwrap(), + ); + test_ext.ext().place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(HEAP_PAGES.encode())); + test_ext +} + +fn construct_block( + executor: &NativeExecutor, + ext: &mut E, + number: BlockNumber, + parent_hash: Hash, + extrinsics: Vec, +) -> (Vec, Hash) { + use trie::{TrieConfiguration, trie_types::Layout}; + + // sign extrinsics. + 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 header = Header { + parent_hash, + number, + extrinsics_root, + state_root: Default::default(), + digest: Default::default(), + }; + + // execute the block to get the real header. + executor.call::<_, NeverNativeValue, fn() -> _>( + ext, + "Core_initialize_block", + &header.encode(), + true, + None, + ).0.unwrap(); + + for i in extrinsics.iter() { + executor.call::<_, NeverNativeValue, fn() -> _>( + ext, + "BlockBuilder_apply_extrinsic", + &i.encode(), + true, + None, + ).0.unwrap(); + } + + let header = match executor.call::<_, NeverNativeValue, fn() -> _>( + ext, + "BlockBuilder_finalize_block", + &[0u8;0], + true, + None, + ).0.unwrap() { + NativeOrEncoded::Native(_) => unreachable!(), + NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(), + }; + + let hash = header.blake2_256(); + (Block { header, extrinsics }.encode(), hash.into()) +} + + +fn test_blocks(genesis_config: &GenesisConfig, executor: &NativeExecutor) + -> Vec<(Vec, Hash)> +{ + let mut test_ext = new_test_ext(genesis_config); + let mut block1_extrinsics = vec![ + CheckedExtrinsic { + signed: None, + function: Call::Timestamp(timestamp::Call::set(42 * 1000)), + }, + ]; + block1_extrinsics.extend((0..20).map(|i| { + CheckedExtrinsic { + signed: Some((alice(), signed_extra(i, 0))), + function: Call::Balances(balances::Call::transfer(bob().into(), 1 * DOLLARS)), + } + })); + let block1 = construct_block( + executor, + &mut test_ext.ext(), + 1, + GENESIS_HASH.into(), + block1_extrinsics, + ); + + vec![block1] +} + +fn bench_execute_block(c: &mut Criterion) { + c.bench_function_over_inputs( + "execute blocks", + |b, strategy| { + let genesis_config = node_testing::genesis::config(false, Some(COMPACT_CODE)); + let (use_native, wasm_method) = match strategy { + ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted), + ExecutionMethod::Wasm(wasm_method) => (false, *wasm_method), + }; + let executor = NativeExecutor::new(wasm_method, None); + + // Get the runtime version to initialize the runtimes cache. + { + let mut test_ext = new_test_ext(&genesis_config); + executor.runtime_version(&mut test_ext.ext()); + } + + let blocks = test_blocks(&genesis_config, &executor); + + b.iter_batched_ref( + || new_test_ext(&genesis_config), + |test_ext| { + for block in blocks.iter() { + executor.call::<_, NeverNativeValue, fn() -> _>( + &mut test_ext.ext(), + "Core_execute_block", + &block.0, + use_native, + None, + ).0.unwrap(); + } + }, + BatchSize::LargeInput, + ); + }, + vec![ + ExecutionMethod::Native, + ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), + #[cfg(feature = "wasmtime")] + ExecutionMethod::Wasm(WasmExecutionMethod::Compiled), + ], + ); +} diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 864b51d6c75f0c7a6222c98e21240c651e0eea9b..fe43fc8ff0ba4f4b7420969d27247f8e87a9251f 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -17,12 +17,7 @@ //! A `CodeExecutor` specialization which uses natively compiled runtime when the wasm to be //! executed is equivalent to the natively compiled code. -#![cfg_attr(feature = "benchmarks", feature(test))] - -#[cfg(feature = "benchmarks")] extern crate test; - pub use substrate_executor::NativeExecutor; -pub use substrate_executor::RuntimesCache; use substrate_executor::native_executor_instance; // Declare an instance of the native executor named `Executor`. Include the wasm binary as the @@ -35,30 +30,30 @@ native_executor_instance!( #[cfg(test)] mod tests { + use substrate_executor::error::Result; use super::Executor; use {balances, contracts, indices, system, timestamp}; - use runtime_io; use codec::{Encode, Decode, Joiner}; - use runtime_support::{ - Hashable, StorageValue, StorageMap, traits::Currency, - }; + use runtime_support::{Hashable, StorageValue, StorageMap, traits::Currency}; use state_machine::TestExternalities as CoreTestExternalities; use primitives::{ Blake2Hasher, NeverNativeValue, NativeOrEncoded, map, traits::{CodeExecutor, Externalities}, storage::well_known_keys, }; use sr_primitives::{ - assert_eq_error_rate, + Fixed64, traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyResult, - transaction_validity::InvalidTransaction, weights::{WeightMultiplier, GetDispatchInfo}, + transaction_validity::InvalidTransaction, weights::GetDispatchInfo, }; use contracts::ContractAddressFor; + use substrate_executor::{NativeExecutor, WasmExecutionMethod}; use system::{EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, - System, Event, TransferFee, TransactionBaseFee, TransactionByteFee, - constants::currency::*, impls::WeightToFee, + System, TransactionPayment, Event, TransferFee, TransactionBaseFee, TransactionByteFee, + WeightFeeCoefficient, constants::currency::*, }; + use node_runtime::impls::LinearWeightToFee; use node_primitives::{Balance, Hash, BlockNumber}; use node_testing::keyring::*; use wabt; @@ -89,17 +84,15 @@ mod tests { } /// Default transfer fee - fn transfer_fee(extrinsic: &E) -> Balance { + fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { let length_fee = TransactionBaseFee::get() + TransactionByteFee::get() * (extrinsic.encode().len() as Balance); let weight = default_transfer_call().get_dispatch_info().weight; - // NOTE: this is really hard to apply, since the multiplier of each block needs to be fetched - // before the block, while we compute this after the block. - // weight = >::next_weight_multiplier().apply_to(weight); - let weight_fee = ::WeightToFee::convert(weight); - length_fee + weight_fee + TransferFee::get() + let weight_fee = ::WeightToFee::convert(weight); + + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() } fn default_transfer_call() -> balances::Call { @@ -117,14 +110,34 @@ mod tests { Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) } - fn executor() -> ::substrate_executor::NativeExecutor { - substrate_executor::NativeExecutor::new(None) + fn executor() -> NativeExecutor { + NativeExecutor::new(WasmExecutionMethod::Interpreted, None) } - fn set_heap_pages>(ext: &mut E, heap_pages: u64) { + fn set_heap_pages(ext: &mut E, heap_pages: u64) { ext.place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(heap_pages.encode())); } + fn executor_call< + R:Decode + Encode + PartialEq, + NC: FnOnce() -> std::result::Result + std::panic::UnwindSafe + >( + t: &mut TestExternalities, + method: &str, + data: &[u8], + use_native: bool, + native_call: Option, + ) -> (Result>, bool) { + let mut t = t.ext(); + executor().call::<_, R, NC>( + &mut t, + method, + data, + use_native, + native_call, + ) + } + #[test] fn panic_execution_with_foreign_code_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, (map![ @@ -142,7 +155,7 @@ mod tests { } ], map![])); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -150,7 +163,7 @@ mod tests { None, ).0; assert!(r.is_ok()); - let v = executor().call::<_, NeverNativeValue, fn() -> _>( + let v = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), @@ -178,7 +191,7 @@ mod tests { } ], map![])); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -186,7 +199,7 @@ mod tests { None, ).0; assert!(r.is_ok()); - let v = executor().call::<_, NeverNativeValue, fn() -> _>( + let v = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), @@ -210,7 +223,7 @@ mod tests { >::hashed_key_for(0) => vec![0u8; 32] ], map![])); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -218,7 +231,10 @@ mod tests { None, ).0; assert!(r.is_ok()); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + let r = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), @@ -227,8 +243,8 @@ mod tests { ).0; assert!(r.is_ok()); - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -246,7 +262,7 @@ mod tests { >::hashed_key_for(0) => vec![0u8; 32] ], map![])); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -254,7 +270,10 @@ mod tests { None, ).0; assert!(r.is_ok()); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + let r = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), @@ -263,8 +282,8 @@ mod tests { ).0; assert!(r.is_ok()); - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -304,7 +323,7 @@ mod tests { }; // execute the block to get the real header. - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( env, "Core_initialize_block", &header.encode(), @@ -313,7 +332,7 @@ mod tests { ).0.unwrap(); for i in extrinsics.iter() { - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( env, "BlockBuilder_apply_extrinsic", &i.encode(), @@ -322,7 +341,7 @@ mod tests { ).0.unwrap(); } - let header = match executor().call::<_, NeverNativeValue, fn() -> _>( + let header = match executor_call:: _>( env, "BlockBuilder_finalize_block", &[0u8;0], @@ -426,7 +445,10 @@ mod tests { let (block1, block2) = blocks(); - executor().call::<_, NeverNativeValue, fn() -> _>( + let mut alice_last_known_balance: Balance = Default::default(); + let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( &mut t, "Core_execute_block", &block1.0, @@ -434,9 +456,10 @@ mod tests { None, ).0.unwrap(); - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + alice_last_known_balance = Balances::total_balance(&alice()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), @@ -461,7 +484,10 @@ mod tests { ]; assert_eq!(System::events(), events); }); - executor().call::<_, NeverNativeValue, fn() -> _>( + + fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( &mut t, "Core_execute_block", &block2.0, @@ -469,18 +495,14 @@ mod tests { None, ).0.unwrap(); - runtime_io::with_externalities(&mut t, || { - // NOTE: fees differ slightly in tests that execute more than one block due to the - // weight update. Hence, using `assert_eq_error_rate`. - assert_eq_error_rate!( + t.execute_with(|| { + assert_eq!( Balances::total_balance(&alice()), - 32 * DOLLARS - 2 * transfer_fee(&xt()), - 10_000 + alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), ); - assert_eq_error_rate!( + assert_eq!( Balances::total_balance(&bob()), - 179 * DOLLARS - transfer_fee(&xt()), - 10_000 + 179 * DOLLARS - transfer_fee(&xt(), fm), ); let events = vec![ EventRecord { @@ -533,7 +555,10 @@ mod tests { let (block1, block2) = blocks(); - executor().call::<_, NeverNativeValue, fn() -> _>( + let mut alice_last_known_balance: Balance = Default::default(); + let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( &mut t, "Core_execute_block", &block1.0, @@ -541,12 +566,15 @@ mod tests { None, ).0.unwrap(); - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + alice_last_known_balance = Balances::total_balance(&alice()); }); - executor().call::<_, NeverNativeValue, fn() -> _>( + fm = t.execute_with(TransactionPayment::next_fee_multiplier); + + executor_call:: _>( &mut t, "Core_execute_block", &block2.0, @@ -554,16 +582,14 @@ mod tests { None, ).0.unwrap(); - runtime_io::with_externalities(&mut t, || { - assert_eq_error_rate!( + t.execute_with(|| { + assert_eq!( Balances::total_balance(&alice()), - 32 * DOLLARS - 2 * transfer_fee(&xt()), - 10_000 + alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), ); - assert_eq_error_rate!( + assert_eq!( Balances::total_balance(&bob()), - 179 * DOLLARS - 1 * transfer_fee(&xt()), - 10_000 + 179 * DOLLARS - 1 * transfer_fee(&xt(), fm), ); }); } @@ -708,7 +734,7 @@ mod tests { let mut t = new_test_ext(COMPACT_CODE, false); - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &b.0, @@ -716,7 +742,7 @@ mod tests { None, ).0.unwrap(); - runtime_io::with_externalities(&mut t, || { + t.execute_with(|| { // Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed. assert_eq!( &contracts::ContractInfoOf::::get(addr) @@ -732,9 +758,9 @@ mod tests { fn wasm_big_block_import_fails() { let mut t = new_test_ext(COMPACT_CODE, false); - set_heap_pages(&mut t, 4); + set_heap_pages(&mut t.ext(), 4); - let result = executor().call::<_, NeverNativeValue, fn() -> _>( + let result = executor_call:: _>( &mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0, @@ -748,7 +774,7 @@ mod tests { fn native_big_block_import_succeeds() { let mut t = new_test_ext(COMPACT_CODE, false); - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0, @@ -762,7 +788,7 @@ mod tests { let mut t = new_test_ext(COMPACT_CODE, false); assert!( - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0, @@ -785,7 +811,7 @@ mod tests { >::hashed_key_for(0) => vec![0u8; 32] ], map![])); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -793,7 +819,7 @@ mod tests { None, ).0; assert!(r.is_ok()); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), @@ -817,7 +843,7 @@ mod tests { >::hashed_key_for(0) => vec![0u8; 32] ], map![])); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -825,7 +851,8 @@ mod tests { None, ).0; assert!(r.is_ok()); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let r = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), @@ -837,8 +864,8 @@ mod tests { .expect("Extrinsic could be applied") .expect("Extrinsic did not fail"); - runtime_io::with_externalities(&mut t, || { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt())); + t.execute_with(|| { + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -850,7 +877,7 @@ mod tests { let block = Block::decode(&mut &block_data[..]).unwrap(); let mut t = new_test_ext(COMPACT_CODE, true); - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &block.encode(), @@ -858,7 +885,7 @@ mod tests { None, ).0.unwrap(); - assert!(t.storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); + assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); } #[test] @@ -866,7 +893,7 @@ mod tests { let block1 = changes_trie_block(); let mut t = new_test_ext(COMPACT_CODE, true); - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &block1.0, @@ -874,7 +901,7 @@ mod tests { None, ).0.unwrap(); - assert!(t.storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); + assert!(t.ext().storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); } #[test] @@ -891,13 +918,14 @@ mod tests { #[test] - fn weight_multiplier_increases_and_decreases_on_big_weight() { + fn fee_multiplier_increases_and_decreases_on_big_weight() { let mut t = new_test_ext(COMPACT_CODE, false); - let mut prev_multiplier = WeightMultiplier::default(); + // initial fee multiplier must be zero + let mut prev_multiplier = Fixed64::from_parts(0); - runtime_io::with_externalities(&mut t, || { - assert_eq!(System::next_weight_multiplier(), prev_multiplier); + t.execute_with(|| { + assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); }); let mut tt = new_test_ext(COMPACT_CODE, false); @@ -939,7 +967,7 @@ mod tests { println!("++ Block 1 size: {} / Block 2 size {}", block1.0.encode().len(), block2.0.encode().len()); // execute a big block. - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &block1.0, @@ -948,15 +976,15 @@ mod tests { ).0.unwrap(); // weight multiplier is increased for next block. - runtime_io::with_externalities(&mut t, || { - let fm = System::next_weight_multiplier(); + t.execute_with(|| { + let fm = TransactionPayment::next_fee_multiplier(); println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); assert!(fm > prev_multiplier); prev_multiplier = fm; }); // execute a big block. - executor().call::<_, NeverNativeValue, fn() -> _>( + executor_call:: _>( &mut t, "Core_execute_block", &block2.0, @@ -965,8 +993,8 @@ mod tests { ).0.unwrap(); // weight multiplier is increased for next block. - runtime_io::with_externalities(&mut t, || { - let fm = System::next_weight_multiplier(); + t.execute_with(|| { + let fm = TransactionPayment::next_fee_multiplier(); println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); assert!(fm < prev_multiplier); }); @@ -979,7 +1007,7 @@ mod tests { // weight of transfer call as of now: 1_000_000 // if weight of the cheapest weight would be 10^7, this would be 10^9, which is: // - 1 MILLICENTS in substrate node. - // - 1 milldot based on current polkadot runtime. + // - 1 milli-dot based on current polkadot runtime. // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ >::hashed_key_for(alice()) => { @@ -1001,7 +1029,7 @@ mod tests { function: Call::Balances(default_transfer_call()), }); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32)), @@ -1010,7 +1038,7 @@ mod tests { ).0; assert!(r.is_ok()); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt.clone()), @@ -1019,7 +1047,7 @@ mod tests { ).0; assert!(r.is_ok()); - runtime_io::with_externalities(&mut t, || { + t.execute_with(|| { assert_eq!(Balances::total_balance(&bob()), (10 + 69) * DOLLARS); // Components deducted from alice's balances: // - Weight fee @@ -1034,7 +1062,7 @@ mod tests { balance_alice -= length_fee; let weight = default_transfer_call().get_dispatch_info().weight; - let weight_fee = WeightToFee::convert(weight); + let weight_fee = LinearWeightToFee::::convert(weight); // we know that weight to fee multiplier is effect-less in block 1. assert_eq!(weight_fee as Balance, MILLICENTS); @@ -1053,6 +1081,7 @@ mod tests { fn block_weight_capacity_report() { // Just report how many transfer calls you could fit into a block. The number should at least // be a few hundred (250 at the time of writing but can change over time). Runs until panic. + use node_primitives::Index; // execution ext. let mut t = new_test_ext(COMPACT_CODE, false); @@ -1094,7 +1123,7 @@ mod tests { len / 1024 / 1024, ); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_execute_block", &block.0, @@ -1119,6 +1148,7 @@ mod tests { // Just report how big a block can get. Executes until panic. Should be ignored unless if // manually inspected. The number should at least be a few megabytes (5 at the time of // writing but can change over time). + use node_primitives::Index; // execution ext. let mut t = new_test_ext(COMPACT_CODE, false); @@ -1157,7 +1187,7 @@ mod tests { len / 1024 / 1024, ); - let r = executor().call::<_, NeverNativeValue, fn() -> _>( + let r = executor_call:: _>( &mut t, "Core_execute_block", &block.0, @@ -1174,21 +1204,4 @@ mod tests { block_number += 1; } } - - #[cfg(feature = "benchmarks")] - mod benches { - use super::*; - use test::Bencher; - - #[bench] - fn wasm_execute_block(b: &mut Bencher) { - let (block1, block2) = blocks(); - - b.iter(|| { - let mut t = new_test_ext(COMPACT_CODE, false); - WasmExecutor::new().call(&mut t, "Core_execute_block", &block1.0).unwrap(); - WasmExecutor::new().call(&mut t, "Core_execute_block", &block2.0).unwrap(); - }); - } - } } diff --git a/node/primitives/Cargo.toml b/node/primitives/Cargo.toml index 266720b0246a039c0d12bb98cec187bf7375165c..25725449a3a5a17f736a65a1c62953cc6847fc71 100644 --- a/node/primitives/Cargo.toml +++ b/node/primitives/Cargo.toml @@ -5,11 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -client = { package = "substrate-client", path = "../../core/client", default-features = false } -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } -rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } -serde = { version = "1.0.101", optional = true, features = ["derive"] } sr-primitives = { path = "../../core/sr-primitives", default-features = false } [dev-dependencies] @@ -19,10 +15,6 @@ pretty_assertions = "0.6.1" [features] default = ["std"] std = [ - "client/std", - "codec/std", "primitives/std", - "rstd/std", - "serde", "sr-primitives/std", ] diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index 907b78a017eea491c8a942632a4cd7704a859189..b6a5ec05655c5ff90cf4e9180ecb90b52dcd020b 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -20,27 +20,21 @@ #![cfg_attr(not(feature = "std"), no_std)] -use rstd::prelude::*; use sr_primitives::{ - generic, traits::{Verify, BlakeTwo256}, OpaqueExtrinsic, AnySignature + generic, traits::{Verify, BlakeTwo256, IdentifyAccount}, OpaqueExtrinsic, MultiSignature }; -#[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; -use codec::{Encode, Decode}; - /// An index to a block. pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = AnySignature; +pub type Signature = MultiSignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. -pub type AccountId = ::Signer; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -/// The type for looking up accounts. We don't expect more than 4 billion of them, but you -/// never know... +/// The type for looking up accounts. We don't expect more than 4 billion of them. pub type AccountIndex = u32; /// Balance of an account. @@ -65,50 +59,6 @@ pub type DigestItem = generic::DigestItem; /// Header type. pub type Header = generic::Header; /// Block type. -pub type Block = generic::Block; +pub type Block = generic::Block; /// Block ID. pub type BlockId = generic::BlockId; - -/// Opaque, encoded, unchecked extrinsic. -pub type UncheckedExtrinsic = OpaqueExtrinsic; - -/// A result of execution of a contract. -#[derive(Eq, PartialEq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub enum ContractExecResult { - /// The contract returned successfully. - /// - /// There is a status code and, optionally, some data returned by the contract. - Success { - /// Status code returned by the contract. - status: u8, - /// Output data returned by the contract. - /// - /// Can be empty. - data: Vec, - }, - /// The contract execution either trapped or returned an error. - Error, -} - -client::decl_runtime_apis! { - /// The API to query account account nonce (aka index). - pub trait AccountNonceApi { - /// Get current account nonce of given `AccountId`. - fn account_nonce(account: AccountId) -> Index; - } - - /// The API to interact with contracts without using executive. - pub trait ContractsApi { - /// Perform a call from a specified account to a given contract. - /// - /// See the contracts' `call` dispatchable function for more details. - fn call( - origin: AccountId, - dest: AccountId, - value: Balance, - gas_limit: u64, - input_data: Vec, - ) -> ContractExecResult; - } -} diff --git a/node/rpc-client/Cargo.toml b/node/rpc-client/Cargo.toml index 7dca1448862ee1b2ba6bb3008152ee9d8f073990..e377f89359582fb4a86ce28bed569d56e4a56dec 100644 --- a/node/rpc-client/Cargo.toml +++ b/node/rpc-client/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -env_logger = "0.6.2" +env_logger = "0.7.0" futures = "0.1.29" hyper = "0.12.35" jsonrpc-core-client = { version = "13.1.0", features = ["http", "ws"] } diff --git a/node/rpc/Cargo.toml b/node/rpc/Cargo.toml index 6bec6adb6f462b769dbd73357a0fe7aa4cfc2308..5e8b7614892bae55753b64d63159149e97e35bf8 100644 --- a/node/rpc/Cargo.toml +++ b/node/rpc/Cargo.toml @@ -7,21 +7,10 @@ edition = "2018" [dependencies] client = { package = "substrate-client", path = "../../core/client" } jsonrpc-core = "13.2.0" -jsonrpc-core-client = "13.2.0" -jsonrpc-derive = "13.2.0" -jsonrpc-pubsub = "13.2.0" -keyring = { package = "substrate-keyring", path = "../../core/keyring" } -log = "0.4.8" node-primitives = { path = "../primitives" } -codec = { package = "parity-scale-codec", version = "1.0.0" } -serde = { version = "1.0.101", features = ["derive"] } +node-runtime = { path = "../runtime" } sr-primitives = { path = "../../core/sr-primitives" } -substrate-primitives = { path = "../../core/primitives" } -rpc-primitives = { package = "substrate-rpc-primitives", path = "../../core/rpc/primitives" } +srml-contracts-rpc = { path = "../../srml/contracts/rpc/" } +srml-transaction-payment-rpc = { path = "../../srml/transaction-payment/rpc/" } +srml-system-rpc = { path = "../../srml/system/rpc/" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } - -[dev-dependencies] -node-testing = { path = "../testing" } -node-runtime = { path = "../runtime" } -env_logger = "0.6.2" -futures03 = { package = "futures-preview", version = "=0.3.0-alpha.19" } diff --git a/node/rpc/src/contracts.rs b/node/rpc/src/contracts.rs deleted file mode 100644 index 3da2478dab8bf780b0a9591352383f96f3e01187..0000000000000000000000000000000000000000 --- a/node/rpc/src/contracts.rs +++ /dev/null @@ -1,116 +0,0 @@ -// 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 . - -//! Node-specific RPC methods for interaction with contracts. - -use std::sync::Arc; - -use serde::{Serialize, Deserialize}; -use client::blockchain::HeaderBackend; -use jsonrpc_core::{Error, ErrorCode, Result}; -use jsonrpc_derive::rpc; -use node_primitives::{ - AccountId, Balance, Block, BlockId, ContractExecResult, ContractsApi as ContractsRuntimeApi, -}; -use sr_primitives::traits::{ - self, - Block as BlockT, -}; -use rpc_primitives::number; - -/// A struct that encodes RPC parameters required for a call to a smart-contract. -#[derive(Serialize, Deserialize)] -#[serde(rename_all="camelCase")] -#[serde(deny_unknown_fields)] -pub struct CallRequest { - origin: AccountId, - dest: AccountId, - value: Balance, - gas_limit: number::NumberOrHex, - input_data: Vec, -} - -/// Contracts RPC methods. -#[rpc] -pub trait ContractsApi { - /// Executes a call to a contract. - /// - /// This call is performed locally without submitting any transactions. Thus executing this - /// won't change any state. Nonetheless, the calling state-changing contracts is still possible. - /// - /// This method is useful for calling getter-like methods on contracts. - #[rpc(name = "contracts_call")] - fn call( - &self, - call_request: CallRequest, - at: Option, - ) -> Result; -} - -/// An implementation of contract specific RPC methods. -pub struct Contracts { - client: Arc, -} - -impl Contracts { - /// Create new `Contracts` with the given reference to the client. - pub fn new(client: Arc) -> Self { - Contracts { client } - } -} - -impl ContractsApi<::Hash> for Contracts -where - C: Send + Sync + 'static, - C: traits::ProvideRuntimeApi, - C: HeaderBackend, - C::Api: ContractsRuntimeApi, -{ - fn call( - &self, - call_request: CallRequest, - at: Option<::Hash>, - ) -> 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. - self.client.info().best_hash - )); - - let CallRequest { - origin, - dest, - value, - gas_limit, - input_data - } = call_request; - let gas_limit = gas_limit.to_number().map_err(|e| Error { - code: ErrorCode::InvalidParams, - message: e, - data: None, - })?; - - let exec_result = api - .call(&at, origin, dest, value, gas_limit, input_data) - .map_err(|e| Error { - code: ErrorCode::ServerError(crate::constants::RUNTIME_ERROR), - message: "Runtime trapped while executing a contract.".into(), - data: Some(format!("{:?}", e).into()), - })?; - - Ok(exec_result) - } -} diff --git a/node/rpc/src/lib.rs b/node/rpc/src/lib.rs index 43f723f7964bc87317fba57e353ccbbef0ebe088..99321373f8f8c5b81f91514281cf8435cdecc62f 100644 --- a/node/rpc/src/lib.rs +++ b/node/rpc/src/lib.rs @@ -25,46 +25,41 @@ //! The RPCs available in this crate however can make some assumptions //! about how the runtime is constructed and what `SRML` modules //! are part of it. Therefore all node-runtime-specific RPCs can -//! be placed here. +//! be placed here or imported from corresponding `SRML` RPC definitions. #![warn(missing_docs)] use std::sync::Arc; -use node_primitives::{Block, AccountNonceApi, ContractsApi}; +use node_primitives::{Block, AccountId, Index, Balance}; +use node_runtime::UncheckedExtrinsic; use sr_primitives::traits::ProvideRuntimeApi; use transaction_pool::txpool::{ChainApi, Pool}; -pub mod accounts; -pub mod contracts; - -mod constants { - /// A status code indicating an error happened while trying to call into the runtime. - /// - /// This typically means that the runtime trapped. - pub const RUNTIME_ERROR: i64 = 1; -} - /// Instantiate all RPC extensions. pub fn create(client: Arc, pool: Arc>) -> jsonrpc_core::IoHandler where C: ProvideRuntimeApi, C: client::blockchain::HeaderBackend, C: Send + Sync + 'static, - C::Api: AccountNonceApi + ContractsApi, + C::Api: srml_system_rpc::AccountNonceApi, + C::Api: srml_contracts_rpc::ContractsRuntimeApi, + C::Api: srml_transaction_payment_rpc::TransactionPaymentRuntimeApi, P: ChainApi + Sync + Send + 'static, M: jsonrpc_core::Metadata + Default, { - use self::{ - accounts::{Accounts, AccountsApi}, - contracts::{Contracts, ContractsApi}, - }; + use srml_system_rpc::{System, SystemApi}; + use srml_contracts_rpc::{Contracts, ContractsApi}; + use srml_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; let mut io = jsonrpc_core::IoHandler::default(); io.extend_with( - AccountsApi::to_delegate(Accounts::new(client.clone(), pool)) + SystemApi::to_delegate(System::new(client.clone(), pool)) + ); + io.extend_with( + ContractsApi::to_delegate(Contracts::new(client.clone())) ); io.extend_with( - ContractsApi::to_delegate(Contracts::new(client)) + TransactionPaymentApi::to_delegate(TransactionPayment::new(client)) ); io } diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml index 9b794130e7d377a4851b7469cfec276221e1e3f5..0aa2dc551ef3ad6ea79ebdae8950ea251820dc98 100644 --- a/node/runtime/Cargo.toml +++ b/node/runtime/Cargo.toml @@ -12,7 +12,6 @@ rustc-hex = { version = "2.0", optional = true } safe-mix = { version = "1.0", default-features = false } serde = { version = "1.0.101", optional = true } -authority-discovery-primitives = { package = "substrate-authority-discovery-primitives", path = "../../core/authority-discovery/primitives", default-features = false } babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives", default-features = false } client = { package = "substrate-client", path = "../../core/client", default-features = false } node-primitives = { path = "../primitives", default-features = false } @@ -25,38 +24,45 @@ substrate-keyring = { path = "../../core/keyring", optional = true } substrate-session = { path = "../../core/session", default-features = false } version = { package = "sr-version", path = "../../core/sr-version", default-features = false } -authority-discovery = { package = "srml-authority-discovery", path = "../../srml/authority-discovery", default-features = false } authorship = { package = "srml-authorship", path = "../../srml/authorship", default-features = false } babe = { package = "srml-babe", path = "../../srml/babe", default-features = false } balances = { package = "srml-balances", path = "../../srml/balances", default-features = false } collective = { package = "srml-collective", path = "../../srml/collective", default-features = false } contracts = { package = "srml-contracts", path = "../../srml/contracts", default-features = false } +contracts-rpc-runtime-api = { package = "srml-contracts-rpc-runtime-api", path = "../../srml/contracts/rpc/runtime-api/", default-features = false } democracy = { package = "srml-democracy", path = "../../srml/democracy", default-features = false } -elections = { package = "srml-elections", path = "../../srml/elections", default-features = false } +elections-phragmen = { package = "srml-elections-phragmen", path = "../../srml/elections-phragmen", default-features = false } executive = { package = "srml-executive", path = "../../srml/executive", default-features = false } finality-tracker = { package = "srml-finality-tracker", path = "../../srml/finality-tracker", default-features = false } grandpa = { package = "srml-grandpa", path = "../../srml/grandpa", default-features = false } im-online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } indices = { package = "srml-indices", path = "../../srml/indices", default-features = false } membership = { package = "srml-membership", path = "../../srml/membership", default-features = false } +nicks = { package = "srml-nicks", path = "../../srml/nicks", default-features = false } offences = { package = "srml-offences", path = "../../srml/offences", default-features = false } +randomness-collective-flip = { package = "srml-randomness-collective-flip", path = "../../srml/randomness-collective-flip", default-features = false } session = { package = "srml-session", path = "../../srml/session", default-features = false, features = ["historical"] } staking = { package = "srml-staking", path = "../../srml/staking", default-features = false } srml-staking-reward-curve = { path = "../../srml/staking/reward-curve"} sudo = { package = "srml-sudo", path = "../../srml/sudo", default-features = false } support = { package = "srml-support", path = "../../srml/support", default-features = false } system = { package = "srml-system", path = "../../srml/system", default-features = false } +system-rpc-runtime-api = { package = "srml-system-rpc-runtime-api", path = "../../srml/system/rpc/runtime-api/", default-features = false } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default-features = false } treasury = { package = "srml-treasury", path = "../../srml/treasury", default-features = false } +utility = { package = "srml-utility", path = "../../srml/utility", default-features = false } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment", default-features = false } +transaction-payment-rpc-runtime-api = { package = "srml-transaction-payment-rpc-runtime-api", path = "../../srml/transaction-payment/rpc/runtime-api/", default-features = false } [build-dependencies] -wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../../core/utils/wasm-builder-runner" } +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.4", path = "../../core/utils/wasm-builder-runner" } + +[dev-dependencies] +runtime_io = { package = "sr-io", path = "../../core/sr-io" } [features] default = ["std"] std = [ - "authority-discovery-primitives/std", - "authority-discovery/std", "authorship/std", "babe-primitives/std", "babe/std", @@ -65,18 +71,21 @@ std = [ "codec/std", "collective/std", "contracts/std", + "contracts-rpc-runtime-api/std", "democracy/std", - "elections/std", + "elections-phragmen/std", "executive/std", "finality-tracker/std", "grandpa/std", "im-online/std", "indices/std", "membership/std", + "nicks/std", "node-primitives/std", "offchain-primitives/std", "offences/std", "primitives/std", + "randomness-collective-flip/std", "rstd/std", "rustc-hex", "safe-mix/std", @@ -90,7 +99,11 @@ std = [ "sudo/std", "support/std", "system/std", + "system-rpc-runtime-api/std", "timestamp/std", "treasury/std", + "utility/std", + "transaction-payment/std", + "transaction-payment-rpc-runtime-api/std", "version/std", ] diff --git a/node/runtime/build.rs b/node/runtime/build.rs index 7311fb90ce6a700cf9047b1ad9b0671ef1107522..f5c2f98a75c8abbe2d7fa8cb16058bd0d5cea851 100644 --- a/node/runtime/build.rs +++ b/node/runtime/build.rs @@ -21,7 +21,7 @@ fn main() { "wasm_binary.rs", WasmBuilderSource::CratesOrPath { path: "../../core/utils/wasm-builder", - version: "1.0.7", + version: "1.0.8", }, // This instructs LLD to export __heap_base as a global variable, which is used by the // external memory allocator. diff --git a/node/runtime/src/constants.rs b/node/runtime/src/constants.rs index 79d3dbd8eb2bc422ead70cab6df8a38fa45aa866..fba4c7ac79e563dc06a1fa572df726fe07fed91c 100644 --- a/node/runtime/src/constants.rs +++ b/node/runtime/src/constants.rs @@ -66,19 +66,3 @@ pub mod time { pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; } - -// CRITICAL NOTE: The system module maintains two constants: a _maximum_ block weight and a -// _ratio_ of it yielding the portion which is accessible to normal transactions (reserving the rest -// for operational ones). `TARGET_BLOCK_FULLNESS` is entirely independent and the system module is -// not aware of if, nor should it care about it. This constant simply denotes on which ratio of the -// _maximum_ block weight we tweak the fees. It does NOT care about the type of the dispatch. -// -// For the system to be configured in a sane way, `TARGET_BLOCK_FULLNESS` should always be less than -// the ratio that `system` module uses to find normal transaction quota. -/// Fee-related. -pub mod fee { - pub use sr_primitives::Perbill; - - /// The block saturation level. Fees will be updates based on this value. - pub const TARGET_BLOCK_FULLNESS: Perbill = Perbill::from_percent(25); -} diff --git a/node/runtime/src/impls.rs b/node/runtime/src/impls.rs index 2e1fcc8826e035a174d36b1bbac79cae96015c57..2e9bd38c8f822dbf6eb895ac5ceacfc0a8b86e2d 100644 --- a/node/runtime/src/impls.rs +++ b/node/runtime/src/impls.rs @@ -17,12 +17,11 @@ //! Some configurable implementations as associated type for the substrate runtime. use node_primitives::Balance; -use sr_primitives::weights::{Weight, WeightMultiplier}; +use sr_primitives::weights::Weight; use sr_primitives::traits::{Convert, Saturating}; -use sr_primitives::Fixed64; -use support::traits::{OnUnbalanced, Currency}; -use crate::{Balances, Authorship, MaximumBlockWeight, NegativeImbalance}; -use crate::constants::fee::TARGET_BLOCK_FULLNESS; +use sr_primitives::{Fixed64, Perbill}; +use support::traits::{OnUnbalanced, Currency, Get}; +use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance}; pub struct Author; impl OnUnbalanced for Author { @@ -47,48 +46,34 @@ impl Convert for CurrencyToVoteHandler { fn convert(x: u128) -> Balance { x * Self::factor() } } -/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the -/// node's balance type. -/// -/// This should typically create a mapping between the following ranges: -/// - [0, system::MaximumBlockWeight] -/// - [Balance::min, Balance::max] -/// -/// Yet, it can be used for any other sort of change to weight-fee. Some examples being: -/// - Setting it to `0` will essentially disable the weight fee. -/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. -/// -/// By default, substrate node will have a weight range of [0, 1_000_000_000]. -pub struct WeightToFee; -impl Convert for WeightToFee { - fn convert(x: Weight) -> Balance { +/// Convert from weight to balance via a simple coefficient multiplication +/// The associated type C encapsulates a constant in units of balance per weight +pub struct LinearWeightToFee(rstd::marker::PhantomData); + +impl> Convert for LinearWeightToFee { + fn convert(w: Weight) -> Balance { // substrate-node a weight of 10_000 (smallest non-zero weight) to be mapped to 10^7 units of // fees, hence: - Balance::from(x).saturating_mul(1_000) + let coefficient = C::get(); + Balance::from(w).saturating_mul(coefficient) } } -/// A struct that updates the weight multiplier based on the saturation level of the previous block. -/// This should typically be called once per-block. -/// -/// This assumes that weight is a numeric value in the u32 range. +/// Update the given multiplier based on the following formula /// -/// Given `TARGET_BLOCK_FULLNESS = 1/2`, a block saturation greater than 1/2 will cause the system -/// fees to slightly grow and the opposite for block saturations less than 1/2. -/// -/// Formula: -/// diff = (target_weight - current_block_weight) +/// diff = (target_weight - previous_block_weight) /// v = 0.00004 /// next_weight = weight * (1 + (v . diff) + (v . diff)^2 / 2) /// +/// Where `target_weight` must be given as the `Get` implementation of the `T` generic type. /// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees -pub struct WeightMultiplierUpdateHandler; +pub struct TargetedFeeAdjustment(rstd::marker::PhantomData); -impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierUpdateHandler { - fn convert(previous_state: (Weight, WeightMultiplier)) -> WeightMultiplier { - let (block_weight, multiplier) = previous_state; +impl> Convert for TargetedFeeAdjustment { + fn convert(multiplier: Fixed64) -> Fixed64 { + let block_weight = System::all_extrinsics_weight(); let max_weight = MaximumBlockWeight::get(); - let target_weight = (TARGET_BLOCK_FULLNESS * max_weight) as u128; + let target_weight = (T::get() * max_weight) as u128; let block_weight = block_weight as u128; // determines if the first_term is positive @@ -100,8 +85,8 @@ impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierU // 0.00004 = 4/100_000 = 40_000/10^9 let v = Fixed64::from_rational(4, 100_000); - // 0.00004^2 = 16/10^10 ~= 2/10^9. Taking the future /2 into account, then it is just 1 parts - // from a billionth. + // 0.00004^2 = 16/10^10 ~= 2/10^9. Taking the future /2 into account, then it is just 1 + // parts from a billionth. let v_squared_2 = Fixed64::from_rational(1, 1_000_000_000); let first_term = v.saturating_mul(diff); @@ -113,17 +98,17 @@ impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierU // Note: this is merely bounded by how big the multiplier and the inner value can go, // not by any economical reasoning. let excess = first_term.saturating_add(second_term); - multiplier.saturating_add(WeightMultiplier::from_fixed(excess)) + multiplier.saturating_add(excess) } else { - // first_term > second_term + // Proof: first_term > second_term. Safe subtraction. let negative = first_term - second_term; - multiplier.saturating_sub(WeightMultiplier::from_fixed(negative)) + multiplier.saturating_sub(negative) // despite the fact that apply_to saturates weight (final fee cannot go below 0) // it is crucially important to stop here and don't further reduce the weight fee // multiplier. While at -1, it means that the network is so un-congested that all // transactions have no weight fee. We stop here and only increase if the network // became more busy. - .max(WeightMultiplier::from_rational(-1, 1)) + .max(Fixed64::from_rational(-1, 1)) } } } @@ -132,21 +117,20 @@ impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierU mod tests { use super::*; use sr_primitives::weights::Weight; - use sr_primitives::Perbill; + use sr_primitives::assert_eq_error_rate; use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime}; - use crate::constants::currency::*; + use crate::{constants::currency::*, TransactionPayment, TargetBlockFullness}; fn max() -> Weight { MaximumBlockWeight::get() } fn target() -> Weight { - TARGET_BLOCK_FULLNESS * max() + TargetBlockFullness::get() * max() } // poc reference implementation. - #[allow(dead_code)] - fn weight_multiplier_update(block_weight: Weight) -> Perbill { + fn fee_multiplier_update(block_weight: Weight, previous: Fixed64) -> Fixed64 { let block_weight = block_weight as f32; let v: f32 = 0.00004; @@ -157,146 +141,225 @@ mod tests { // Current saturation in terms of weight let s = block_weight; - let fm = 1.0 + (v * (s/m - ss/m)) + (v.powi(2) * (s/m - ss/m).powi(2)) / 2.0; - // return a per-bill-like value. - let fm = if fm >= 1.0 { fm - 1.0 } else { 1.0 - fm }; - Perbill::from_parts((fm * 1_000_000_000_f32) as u32) + let fm = v * (s/m - ss/m) + v.powi(2) * (s/m - ss/m).powi(2) / 2.0; + let addition_fm = Fixed64::from_parts((fm * 1_000_000_000_f32).round() as i64); + previous.saturating_add(addition_fm) + } + + fn feemul(parts: i64) -> Fixed64 { + Fixed64::from_parts(parts) } - fn wm(parts: i64) -> WeightMultiplier { - WeightMultiplier::from_parts(parts) + fn run_with_system_weight(w: Weight, assertions: F) where F: Fn() -> () { + let mut t: runtime_io::TestExternalities = + system::GenesisConfig::default().build_storage::().unwrap().into(); + t.execute_with(|| { + System::set_block_limits(w, 0); + assertions() + }); + } + + #[test] + fn fee_multiplier_update_poc_works() { + let fm = Fixed64::from_rational(0, 1); + let test_set = vec![ + (0, fm.clone()), + (100, fm.clone()), + (target(), fm.clone()), + (max() / 2, fm.clone()), + (max(), fm.clone()), + ]; + test_set.into_iter().for_each(|(w, fm)| { + run_with_system_weight(w, || { + assert_eq_error_rate!( + fee_multiplier_update(w, fm).into_inner(), + TargetedFeeAdjustment::::convert(fm).into_inner(), + 5, + ); + }) + }) } #[test] fn empty_chain_simulation() { // just a few txs per_block. - let block_weight = 1000; - let mut wm = WeightMultiplier::default(); - let mut iterations: u64 = 0; - loop { - let next = WeightMultiplierUpdateHandler::convert((block_weight, wm)); - wm = next; - if wm == WeightMultiplier::from_rational(-1, 1) { break; } - iterations += 1; - } - println!("iteration {}, new wm = {:?}. Weight fee is now zero", iterations, wm); + let block_weight = 0; + run_with_system_weight(block_weight, || { + let mut fm = Fixed64::default(); + let mut iterations: u64 = 0; + loop { + let next = TargetedFeeAdjustment::::convert(fm); + fm = next; + if fm == Fixed64::from_rational(-1, 1) { break; } + iterations += 1; + } + println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm); + assert!(iterations > 50_000, "This assertion is just a warning; Don't panic. \ + Current substrate/polkadot node are configured with a _slow adjusting fee_ \ + mechanism. Hence, it is really unlikely that fees collapse to zero even on an \ + empty chain in less than at least of couple of thousands of empty blocks. But this \ + simulation indicates that fees collapsed to zero after {} almost-empty blocks. \ + Check it", + iterations, + ); + }) } #[test] #[ignore] fn congested_chain_simulation() { // `cargo test congested_chain_simulation -- --nocapture` to get some insight. + // almost full. The entire quota of normal transactions is taken. - let block_weight = AvailableBlockRatio::get() * max(); - let tx_weight = 1000; - let mut wm = WeightMultiplier::default(); - let mut iterations: u64 = 0; - loop { - let next = WeightMultiplierUpdateHandler::convert((block_weight, wm)); - if wm == next { break; } - wm = next; - iterations += 1; - let fee = ::WeightToFee::convert(wm.apply_to(tx_weight)); - println!( - "iteration {}, new wm = {:?}. Fee at this point is: {} millicents, {} cents, {} dollars", - iterations, - wm, - fee / MILLICENTS, - fee / CENTS, - fee / DOLLARS - ); - } + let block_weight = AvailableBlockRatio::get() * max() - 100; + + // Default substrate minimum. + let tx_weight = 10_000; + + run_with_system_weight(block_weight, || { + // initial value configured on module + let mut fm = Fixed64::default(); + assert_eq!(fm, TransactionPayment::next_fee_multiplier()); + + let mut iterations: u64 = 0; + loop { + let next = TargetedFeeAdjustment::::convert(fm); + // if no change, panic. This should never happen in this case. + if fm == next { panic!("The fee should ever increase"); } + fm = next; + iterations += 1; + let fee = ::WeightToFee::convert(tx_weight); + let adjusted_fee = fm.saturated_multiply_accumulate(fee); + println!( + "iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \ + {} cents, {} dollars", + iterations, + fm, + adjusted_fee, + adjusted_fee / MILLICENTS, + adjusted_fee / CENTS, + adjusted_fee / DOLLARS, + ); + } + }); } #[test] fn stateless_weight_mul() { - // Light block. Fee is reduced a little. - assert_eq!( - WeightMultiplierUpdateHandler::convert((target() / 4, WeightMultiplier::default())), - wm(-7500) - ); - // a bit more. Fee is decreased less, meaning that the fee increases as the block grows. - assert_eq!( - WeightMultiplierUpdateHandler::convert((target() / 2, WeightMultiplier::default())), - wm(-5000) - ); - // ideal. Original fee. No changes. - assert_eq!( - WeightMultiplierUpdateHandler::convert((target(), WeightMultiplier::default())), - wm(0) - ); - // // More than ideal. Fee is increased. - assert_eq!( - WeightMultiplierUpdateHandler::convert(((target() * 2), WeightMultiplier::default())), - wm(10000) - ); + run_with_system_weight(target() / 4, || { + // Light block. Fee is reduced a little. + assert_eq!( + TargetedFeeAdjustment::::convert(Fixed64::default()), + feemul(-7500), + ); + }); + run_with_system_weight(target() / 2, || { + // a bit more. Fee is decreased less, meaning that the fee increases as the block grows. + assert_eq!( + TargetedFeeAdjustment::::convert(Fixed64::default()), + feemul(-5000), + ); + + }); + run_with_system_weight(target(), || { + // ideal. Original fee. No changes. + assert_eq!( + TargetedFeeAdjustment::::convert(Fixed64::default()), + feemul(0), + ); + }); + run_with_system_weight(target() * 2, || { + // // More than ideal. Fee is increased. + assert_eq!( + TargetedFeeAdjustment::::convert(Fixed64::default()), + feemul(10000), + ); + }); } #[test] fn stateful_weight_mul_grow_to_infinity() { - assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, WeightMultiplier::default())), - wm(10000) - ); - assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, wm(10000))), - wm(20000) - ); - assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, wm(20000))), - wm(30000) - ); - // ... - assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, wm(1_000_000_000))), - wm(1_000_000_000 + 10000) - ); + run_with_system_weight(target() * 2, || { + assert_eq!( + TargetedFeeAdjustment::::convert(Fixed64::default()), + feemul(10000) + ); + assert_eq!( + TargetedFeeAdjustment::::convert(feemul(10000)), + feemul(20000) + ); + assert_eq!( + TargetedFeeAdjustment::::convert(feemul(20000)), + feemul(30000) + ); + // ... + assert_eq!( + TargetedFeeAdjustment::::convert(feemul(1_000_000_000)), + feemul(1_000_000_000 + 10000) + ); + }); } #[test] fn stateful_weight_mil_collapse_to_minus_one() { - assert_eq!( - WeightMultiplierUpdateHandler::convert((0, WeightMultiplier::default())), - wm(-10000) - ); - assert_eq!( - WeightMultiplierUpdateHandler::convert((0, wm(-10000))), - wm(-20000) - ); - assert_eq!( - WeightMultiplierUpdateHandler::convert((0, wm(-20000))), - wm(-30000) - ); - // ... - assert_eq!( - WeightMultiplierUpdateHandler::convert((0, wm(1_000_000_000 * -1))), - wm(-1_000_000_000) - ); + run_with_system_weight(0, || { + assert_eq!( + TargetedFeeAdjustment::::convert(Fixed64::default()), + feemul(-10000) + ); + assert_eq!( + TargetedFeeAdjustment::::convert(feemul(-10000)), + feemul(-20000) + ); + assert_eq!( + TargetedFeeAdjustment::::convert(feemul(-20000)), + feemul(-30000) + ); + // ... + assert_eq!( + TargetedFeeAdjustment::::convert(feemul(1_000_000_000 * -1)), + feemul(-1_000_000_000) + ); + }) } #[test] fn weight_to_fee_should_not_overflow_on_large_weights() { let kb = 1024 as Weight; let mb = kb * kb; - let max_fm = WeightMultiplier::from_fixed(Fixed64::from_natural(i64::max_value())); - - vec![0, 1, 10, 1000, kb, 10 * kb, 100 * kb, mb, 10 * mb, Weight::max_value() / 2, Weight::max_value()] - .into_iter() - .for_each(|i| { - WeightMultiplierUpdateHandler::convert((i, WeightMultiplier::default())); + let max_fm = Fixed64::from_natural(i64::max_value()); + + // check that for all values it can compute, correctly. + vec![ + 0, + 1, + 10, + 1000, + kb, + 10 * kb, + 100 * kb, + mb, + 10 * mb, + Weight::max_value() / 2, + Weight::max_value() + ].into_iter().for_each(|i| { + run_with_system_weight(i, || { + let next = TargetedFeeAdjustment::::convert(Fixed64::default()); + let truth = fee_multiplier_update(i, Fixed64::default()); + assert_eq_error_rate!(truth.into_inner(), next.into_inner(), 5); }); + }); // Some values that are all above the target and will cause an increase. let t = target(); vec![t + 100, t * 2, t * 4] .into_iter() .for_each(|i| { - let fm = WeightMultiplierUpdateHandler::convert(( - i, - max_fm - )); - // won't grow. The convert saturates everything. - assert_eq!(fm, max_fm); + run_with_system_weight(i, || { + let fm = TargetedFeeAdjustment::::convert(max_fm); + // won't grow. The convert saturates everything. + assert_eq!(fm, max_fm); + }) }); } } diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 9b35950e2642d9f9d0e8d3855340fd174ef48792..5cf6663904142715ac7357f4c68691ae80c3ef7f 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -22,37 +22,34 @@ use rstd::prelude::*; use support::{ - construct_runtime, parameter_types, traits::{SplitTwoWays, Currency} + construct_runtime, parameter_types, traits::{SplitTwoWays, Currency, Randomness} }; use primitives::u32_trait::{_1, _2, _3, _4}; use node_primitives::{ AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, - Moment, Signature, ContractExecResult, + Moment, Signature, }; -use babe_primitives::{AuthorityId as BabeId, AuthoritySignature as BabeSignature}; use grandpa::fg_primitives; use client::{ block_builder::api::{self as block_builder_api, InherentData, CheckInherentsResult}, runtime_api as client_api, impl_runtime_apis }; -use sr_primitives::{ - Permill, Perbill, ApplyResult, impl_opaque_keys, generic, create_runtime_str, key_types -}; +use sr_primitives::{Permill, Perbill, ApplyResult, impl_opaque_keys, generic, create_runtime_str}; use sr_primitives::curve::PiecewiseLinear; use sr_primitives::transaction_validity::TransactionValidity; use sr_primitives::weights::Weight; use sr_primitives::traits::{ self, BlakeTwo256, Block as BlockT, NumberFor, StaticLookup, SaturatedConversion, + OpaqueKeys, }; use version::RuntimeVersion; -use elections::VoteIndex; #[cfg(any(feature = "std", test))] use version::NativeVersion; use primitives::OpaqueMetadata; use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; use im_online::sr25519::{AuthorityId as ImOnlineId}; -use authority_discovery_primitives::{AuthorityId as EncodedAuthorityId, Signature as EncodedSignature}; -use codec::{Encode, Decode}; +use transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; +use contracts_rpc_runtime_api::ContractExecResult; use system::offchain::TransactionSubmitter; #[cfg(any(feature = "std", test))] @@ -65,7 +62,7 @@ pub use staking::StakerStatus; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; -use impls::{CurrencyToVoteHandler, WeightMultiplierUpdateHandler, Author, WeightToFee}; +use impls::{CurrencyToVoteHandler, Author, LinearWeightToFee, TargetedFeeAdjustment}; /// Constant values used within the runtime. pub mod constants; @@ -84,8 +81,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 171, - impl_version: 171, + spec_version: 191, + impl_version: 191, apis: RUNTIME_API_VERSIONS, }; @@ -110,9 +107,9 @@ pub type DealWithFees = SplitTwoWays< parameter_types! { pub const BlockHashCount: BlockNumber = 250; pub const MaximumBlockWeight: Weight = 1_000_000_000; - pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); pub const MaximumBlockLength: u32 = 5 * 1024 * 1024; pub const Version: RuntimeVersion = VERSION; + pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); } impl system::Trait for Runtime { @@ -125,7 +122,6 @@ impl system::Trait for Runtime { type AccountId = AccountId; type Lookup = Indices; type Header = generic::Header; - type WeightMultiplierUpdate = WeightMultiplierUpdateHandler; type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -134,6 +130,11 @@ impl system::Trait for Runtime { type Version = Version; } +impl utility::Trait for Runtime { + type Event = Event; + type Call = Call; +} + parameter_types! { pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS; pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK; @@ -142,6 +143,7 @@ parameter_types! { impl babe::Trait for Runtime { type EpochDuration = EpochDuration; type ExpectedBlockTime = ExpectedBlockTime; + type EpochChangeTrigger = babe::ExternalTrigger; } impl indices::Trait for Runtime { @@ -155,8 +157,6 @@ parameter_types! { pub const ExistentialDeposit: Balance = 1 * DOLLARS; pub const TransferFee: Balance = 1 * CENTS; pub const CreationFee: Balance = 1 * CENTS; - pub const TransactionBaseFee: Balance = 1 * CENTS; - pub const TransactionByteFee: Balance = 10 * MILLICENTS; } impl balances::Trait for Runtime { @@ -164,15 +164,29 @@ impl balances::Trait for Runtime { type OnFreeBalanceZero = ((Staking, Contracts), Session); type OnNewAccount = Indices; type Event = Event; - type TransactionPayment = DealWithFees; type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 1 * CENTS; + pub const TransactionByteFee: Balance = 10 * MILLICENTS; + // setting this to zero will disable the weight fee. + pub const WeightFeeCoefficient: Balance = 1_000; + // for a sane configuration, this should always be less than `AvailableBlockRatio`. + pub const TargetBlockFullness: Perbill = Perbill::from_percent(25); +} + +impl transaction_payment::Trait for Runtime { + type Currency = Balances; + type OnTransactionPayment = DealWithFees; type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; - type WeightToFee = WeightToFee; + type WeightToFee = LinearWeightToFee; + type FeeMultiplierUpdate = TargetedFeeAdjustment; } parameter_types! { @@ -192,34 +206,24 @@ impl authorship::Trait for Runtime { type FindAuthor = session::FindAccountFromAuthorIndex; type UncleGenerations = UncleGenerations; type FilterUncle = (); - type EventHandler = Staking; + type EventHandler = (Staking, ImOnline); } -type SessionHandlers = (Grandpa, Babe, ImOnline, AuthorityDiscovery); - impl_opaque_keys! { pub struct SessionKeys { - #[id(key_types::GRANDPA)] - pub grandpa: GrandpaId, - #[id(key_types::BABE)] - pub babe: BabeId, - #[id(key_types::IM_ONLINE)] - pub im_online: ImOnlineId, + pub grandpa: Grandpa, + pub babe: Babe, + pub im_online: ImOnline, } } -// NOTE: `SessionHandler` and `SessionKeys` are co-dependent: One key will be used for each handler. -// The number and order of items in `SessionHandler` *MUST* be the same number and order of keys in -// `SessionKeys`. -// TODO: Introduce some structure to tie these together to make it a bit less of a footgun. This -// should be easy, since OneSessionHandler trait provides the `Key` as an associated type. #2858 parameter_types! { pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(17); } impl session::Trait for Runtime { type OnSessionEnding = Staking; - type SessionHandler = SessionHandlers; + type SessionHandler = ::KeyTypeIdProviders; type ShouldEndSession = Babe; type Event = Event; type Keys = SessionKeys; @@ -311,33 +315,24 @@ impl collective::Trait for Runtime { parameter_types! { pub const CandidacyBond: Balance = 10 * DOLLARS; pub const VotingBond: Balance = 1 * DOLLARS; - pub const VotingFee: Balance = 2 * DOLLARS; - pub const MinimumVotingLock: Balance = 1 * DOLLARS; - pub const PresentSlashPerVoter: Balance = 1 * CENTS; - pub const CarryCount: u32 = 6; - // one additional vote should go by before an inactive voter can be reaped. - pub const InactiveGracePeriod: VoteIndex = 1; - pub const ElectionsVotingPeriod: BlockNumber = 2 * DAYS; - pub const DecayRatio: u32 = 0; + pub const TermDuration: BlockNumber = 7 * DAYS; + pub const DesiredMembers: u32 = 13; + pub const DesiredRunnersUp: u32 = 7; } -impl elections::Trait for Runtime { +impl elections_phragmen::Trait for Runtime { type Event = Event; type Currency = Balances; - type BadPresentation = (); - type BadReaper = (); - type BadVoterIndex = (); - type LoserCandidate = (); - type ChangeMembers = Council; + type CurrencyToVote = CurrencyToVoteHandler; type CandidacyBond = CandidacyBond; type VotingBond = VotingBond; - type VotingFee = VotingFee; - type MinimumVotingLock = MinimumVotingLock; - type PresentSlashPerVoter = PresentSlashPerVoter; - type CarryCount = CarryCount; - type InactiveGracePeriod = InactiveGracePeriod; - type VotingPeriod = ElectionsVotingPeriod; - type DecayRatio = DecayRatio; + type TermDuration = TermDuration; + type DesiredMembers = DesiredMembers; + type DesiredRunnersUp = DesiredRunnersUp; + type LoserCandidate = (); + type BadReport = (); + type KickedMember = (); + type ChangeMembers = Council; } type TechnicalCollective = collective::Instance2; @@ -369,7 +364,6 @@ impl treasury::Trait for Runtime { type ApproveOrigin = collective::EnsureMembers<_4, AccountId, CouncilCollective>; type RejectOrigin = collective::EnsureMembers<_2, AccountId, CouncilCollective>; type Event = Event; - type MintedForSpending = (); type ProposalRejection = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; @@ -392,12 +386,14 @@ parameter_types! { impl contracts::Trait for Runtime { type Currency = Balances; type Time = Timestamp; + type Randomness = RandomnessCollectiveFlip; type Call = Call; type Event = Event; type DetermineContractAddress = contracts::SimpleAddressDeterminator; type ComputeDispatchFee = contracts::DefaultDispatchFeeComputor; type TrieIdGenerator = contracts::TrieIdFromParentCounter; type GasPayment = (); + type RentPayment = (); type SignedClaimHandicap = contracts::DefaultSignedClaimHandicap; type TombstoneDeposit = TombstoneDeposit; type StorageSizeOffset = contracts::DefaultStorageSizeOffset; @@ -437,10 +433,6 @@ impl offences::Trait for Runtime { type OnOffenceHandler = Staking; } -impl authority_discovery::Trait for Runtime { - type AuthorityId = BabeId; -} - impl grandpa::Trait for Runtime { type Event = Event; } @@ -456,11 +448,29 @@ impl finality_tracker::Trait for Runtime { type ReportLatency = ReportLatency; } +parameter_types! { + pub const ReservationFee: Balance = 1 * DOLLARS; + pub const MinLength: usize = 3; + pub const MaxLength: usize = 16; +} + +impl nicks::Trait for Runtime { + type Event = Event; + type Currency = Balances; + type ReservationFee = ReservationFee; + type Slashed = Treasury; + type ForceOrigin = collective::EnsureMember; + type MinLength = MinLength; + type MaxLength = MaxLength; +} + impl system::offchain::CreateTransaction for Runtime { + type Public = ::Signer; type Signature = Signature; - fn create_transaction>( + fn create_transaction>( call: Call, + public: Self::Public, account: AccountId, index: Index, ) -> Option<(Call, ::SignaturePayload)> { @@ -473,11 +483,11 @@ impl system::offchain::CreateTransaction for Runtim system::CheckEra::::from(generic::Era::mortal(period, current_block)), system::CheckNonce::::from(index), system::CheckWeight::::new(), - balances::TakeFees::::from(tip), + transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), ); let raw_payload = SignedPayload::new(call, extra).ok()?; - let signature = F::sign(account.clone(), &raw_payload)?; + let signature = F::sign(public, &raw_payload)?; let address = Indices::unlookup(account); let (call, extra, _) = raw_payload.deconstruct(); Some((call, (address, signature, extra))) @@ -491,26 +501,29 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic { System: system::{Module, Call, Storage, Config, Event}, + Utility: utility::{Module, Call, Event}, Babe: babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, Timestamp: timestamp::{Module, Call, Storage, Inherent}, Authorship: authorship::{Module, Call, Storage, Inherent}, Indices: indices, Balances: balances::{default, Error}, + TransactionPayment: transaction_payment::{Module, Storage}, Staking: staking::{default, OfflineWorker}, Session: session::{Module, Call, Storage, Event, Config}, Democracy: democracy::{Module, Call, Storage, Config, Event}, Council: collective::::{Module, Call, Storage, Origin, Event, Config}, TechnicalCommittee: collective::::{Module, Call, Storage, Origin, Event, Config}, - Elections: elections::{Module, Call, Storage, Event, Config}, + Elections: elections_phragmen::{Module, Call, Storage, Event}, TechnicalMembership: membership::::{Module, Call, Storage, Event, Config}, FinalityTracker: finality_tracker::{Module, Call, Inherent}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, - Treasury: treasury::{Module, Call, Storage, Event}, + Treasury: treasury::{Module, Call, Storage, Config, Event}, Contracts: contracts, Sudo: sudo, ImOnline: im_online::{Module, Call, Storage, Event, ValidateUnsigned, Config}, - AuthorityDiscovery: authority_discovery::{Module, Call, Config}, Offences: offences::{Module, Call, Storage, Event}, + RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage}, + Nicks: nicks::{Module, Call, Storage, Event}, } ); @@ -531,7 +544,7 @@ pub type SignedExtra = ( system::CheckEra, system::CheckNonce, system::CheckWeight, - balances::TakeFees, + transaction_payment::ChargeTransactionPayment, contracts::CheckBlockGasLimit, ); /// Unchecked extrinsic type as expected by this runtime. @@ -582,7 +595,7 @@ impl_runtime_apis! { } fn random_seed() -> ::Hash { - System::random_seed() + RandomnessCollectiveFlip::random_seed() } } @@ -622,42 +635,13 @@ impl_runtime_apis! { } } - impl authority_discovery_primitives::AuthorityDiscoveryApi for Runtime { - fn authorities() -> Vec { - AuthorityDiscovery::authorities().into_iter() - .map(|id| id.encode()) - .map(EncodedAuthorityId) - .collect() - } - - fn sign(payload: &Vec) -> Option<(EncodedSignature, EncodedAuthorityId)> { - AuthorityDiscovery::sign(payload).map(|(sig, id)| { - (EncodedSignature(sig.encode()), EncodedAuthorityId(id.encode())) - }) - } - - fn verify(payload: &Vec, signature: &EncodedSignature, authority_id: &EncodedAuthorityId) -> bool { - let signature = match BabeSignature::decode(&mut &signature.0[..]) { - Ok(s) => s, - _ => return false, - }; - - let authority_id = match BabeId::decode(&mut &authority_id.0[..]) { - Ok(id) => id, - _ => return false, - }; - - AuthorityDiscovery::verify(payload, signature, authority_id) - } - } - - impl node_primitives::AccountNonceApi for Runtime { + impl system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) } } - impl node_primitives::ContractsApi for Runtime { + impl contracts_rpc_runtime_api::ContractsApi for Runtime { fn call( origin: AccountId, dest: AccountId, @@ -680,32 +664,54 @@ impl_runtime_apis! { Err(_) => ContractExecResult::Error, } } + + fn get_storage( + address: AccountId, + key: [u8; 32], + ) -> contracts_rpc_runtime_api::GetStorageResult { + Contracts::get_storage(address, key).map_err(|rpc_err| { + use contracts::GetStorageError; + use 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, + } + }) + } + } + + impl transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + Balance, + UncheckedExtrinsic, + > for Runtime { + fn query_info(uxt: UncheckedExtrinsic, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } } impl substrate_session::SessionKeys for Runtime { fn generate_session_keys(seed: Option>) -> Vec { - let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); + let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s) + .expect("Seed is an utf8 string")); SessionKeys::generate(seed) } } } - #[cfg(test)] mod tests { use super::*; - use sr_primitives::app_crypto::RuntimeAppPublic; use system::offchain::SubmitSignedTransaction; - fn is_submit_signed_transaction(_arg: T) where + fn is_submit_signed_transaction(_arg: T) where T: SubmitSignedTransaction< Runtime, Call, Extrinsic=UncheckedExtrinsic, CreateTransaction=Runtime, - Signer=Signer, + Signer=ImOnlineId, >, - Signer: RuntimeAppPublic + From, - Signer::Signature: Into, {} #[test] diff --git a/node/testing/Cargo.toml b/node/testing/Cargo.toml index e622e35d4aad0c37da7aa7db108e51546476609f..8a4c08ed11bd2e53d0d844cd0fb64f721858dba1 100644 --- a/node/testing/Cargo.toml +++ b/node/testing/Cargo.toml @@ -27,4 +27,5 @@ system = { package = "srml-system", path = "../../srml/system" } test-client = { package = "substrate-test-client", path = "../../core/test-client" } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp" } treasury = { package = "srml-treasury", path = "../../srml/treasury" } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment" } wabt = "0.9.2" diff --git a/node/testing/src/genesis.rs b/node/testing/src/genesis.rs index 35ff93d1a69ae65797a2bca6d592bc65e0e474bd..0b99052f250269fe9b58694f7b8f469a2544c0d5 100644 --- a/node/testing/src/genesis.rs +++ b/node/testing/src/genesis.rs @@ -89,12 +89,11 @@ pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig authorities: vec![], }), im_online: Some(Default::default()), - authority_discovery: Some(Default::default()), democracy: Some(Default::default()), collective_Instance1: Some(Default::default()), collective_Instance2: Some(Default::default()), membership_Instance1: Some(Default::default()), - elections: Some(Default::default()), sudo: Some(Default::default()), + treasury: Some(Default::default()), } } diff --git a/node/testing/src/keyring.rs b/node/testing/src/keyring.rs index 0c6eb478cc5a08055cd3c6c0bbb1caf7cf50447d..ca44a53880fc1f661443987406e6158e2a8778aa 100644 --- a/node/testing/src/keyring.rs +++ b/node/testing/src/keyring.rs @@ -23,9 +23,7 @@ use sr_primitives::generic::Era; use codec::Encode; /// Alice's account id. -pub fn alice() -> AccountId { - AccountKeyring::Alice.into() -} +pub fn alice() -> AccountId { AccountKeyring::Alice.into() } /// Bob's account id. pub fn bob() -> AccountId { @@ -72,7 +70,7 @@ pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { system::CheckEra::from(Era::mortal(256, 0)), system::CheckNonce::from(nonce), system::CheckWeight::new(), - balances::TakeFees::from(extra_fee), + transaction_payment::ChargeTransactionPayment::from(extra_fee), Default::default(), ) } @@ -82,7 +80,7 @@ pub fn sign(xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> Unche match xt.signed { Some((signed, extra)) => { let payload = (xt.function, extra.clone(), version, genesis_hash, genesis_hash); - let key = AccountKeyring::from_public(&signed).unwrap(); + let key = AccountKeyring::from_account_id(&signed).unwrap(); let signature = payload.using_encoded(|b| { if b.len() > 256 { key.sign(&sr_io::blake2_256(b)) diff --git a/rustdoc-header.html b/rustdoc-header.html new file mode 100644 index 0000000000000000000000000000000000000000..a679d5e299da7a79e6fd52aa3594a78053b7d641 --- /dev/null +++ b/rustdoc-header.html @@ -0,0 +1,10 @@ + + + + diff --git a/scripts/gitlab/check_runtime.sh b/scripts/gitlab/check_runtime.sh index 725c5077c56494838ba283a59bddf991585bbdef..cd988718d05db52814bf107e15a7e6c63038b0ee 100755 --- a/scripts/gitlab/check_runtime.sh +++ b/scripts/gitlab/check_runtime.sh @@ -32,7 +32,7 @@ github_label () { # check if the wasm sources changed if ! git diff --name-only origin/master...${CI_COMMIT_SHA} \ - | grep -q -e '^node/src/runtime' -e '^srml/' -e '^core/sr-' + | grep -q -e '^node/src/runtime' -e '^srml/' -e '^core/sr-' | grep -v -e '^core/sr-arithmetic/fuzzer' then cat <<-EOT diff --git a/scripts/init.sh b/scripts/init.sh index cf5ecf97926fea7a5e8fd2a91df96853f90e8ee7..1405a41ef333e6af863080d83f854d3edb5fb4fa 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -10,7 +10,3 @@ if [ -z $CI_PROJECT_NAME ] ; then fi rustup target add wasm32-unknown-unknown --toolchain nightly - -# Install wasm-gc. It's useful for stripping slimming down wasm binaries. -command -v wasm-gc || \ - cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force diff --git a/scripts/node-template-release/Cargo.toml b/scripts/node-template-release/Cargo.toml index 34aadc971f1aaa78b2f53860e668d294cee52eb0..8a43435b20d83be0f69007eedb6888acbc2c3282 100644 --- a/scripts/node-template-release/Cargo.toml +++ b/scripts/node-template-release/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" toml = "0.4" tar = "0.4" glob = "0.2" -structopt = "0.2" +structopt = "0.3" tempfile = "3" fs_extra = "1" git2 = "0.8" diff --git a/srml/assets/Cargo.toml b/srml/assets/Cargo.toml index 5f22ab3ba33e5ca51aae10a4d62a2f073f2c2b4e..052a3045b21647e9251546ed0ae37dd8d86f5632 100644 --- a/srml/assets/Cargo.toml +++ b/srml/assets/Cargo.toml @@ -27,5 +27,4 @@ std = [ "sr-primitives/std", "support/std", "system/std", - "runtime-io/std", ] diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index b143085232a3fc1a3be9c034829fa9d7b7598444..5c8b1bbd610a3c436eb9a1dae54ab7e62a297766 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -196,10 +196,11 @@ decl_module! { } decl_event!( - pub enum Event - where ::AccountId, - ::Balance, - ::AssetId { + pub enum Event where + ::AccountId, + ::Balance, + ::AssetId, + { /// Some assets were issued. Issued(AssetId, AccountId, Balance), /// Some assets were transferred. @@ -214,7 +215,7 @@ decl_storage! { /// The number of units of assets held by any given account. Balances: map (T::AssetId, T::AccountId) => T::Balance; /// The next asset identifier up for grabs. - NextAssetId get(next_asset_id): T::AssetId; + NextAssetId get(fn next_asset_id): T::AssetId; /// The total unit supply of an asset. TotalSupply: map T::AssetId => T::Balance; } @@ -239,9 +240,8 @@ impl Module { mod tests { use super::*; - use runtime_io::with_externalities; use support::{impl_outer_origin, assert_ok, assert_noop, parameter_types}; - use primitives::{H256, Blake2Hasher}; + use primitives::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 sr_primitives::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; @@ -271,7 +271,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -288,13 +287,13 @@ mod tests { // This function basically just builds a genesis storage key/value store according to // our desired mockup. - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { system::GenesisConfig::default().build_storage::().unwrap().into() } #[test] fn issuing_asset_units_to_issuer_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); }); @@ -302,7 +301,7 @@ mod tests { #[test] fn querying_total_supply_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -319,7 +318,7 @@ mod tests { #[test] fn transferring_amount_above_available_balance_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -329,8 +328,8 @@ mod tests { } #[test] - fn transferring_amount_less_than_available_balance_should_not_work() { - with_externalities(&mut new_test_ext(), || { + fn transferring_amount_more_than_available_balance_should_not_work() { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 50)); @@ -344,7 +343,7 @@ mod tests { #[test] fn transferring_less_than_one_unit_should_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 0), "transfer amount should be non-zero"); @@ -353,7 +352,7 @@ mod tests { #[test] fn transferring_more_units_than_total_supply_should_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); assert_noop!(Assets::transfer(Origin::signed(1), 0, 2, 101), "origin account balance must be greater than or equal to the transfer amount"); @@ -362,7 +361,7 @@ mod tests { #[test] fn destroying_asset_balance_with_positive_balance_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 1), 100); assert_ok!(Assets::destroy(Origin::signed(1), 0)); @@ -371,7 +370,7 @@ mod tests { #[test] fn destroying_asset_balance_with_zero_balance_should_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_ok!(Assets::issue(Origin::signed(1), 100)); assert_eq!(Assets::balance(0, 2), 0); assert_noop!(Assets::destroy(Origin::signed(2), 0), "origin balance should be non-zero"); diff --git a/srml/aura/src/lib.rs b/srml/aura/src/lib.rs index 834d6b0a14dfe941d9809e0c638a5f44b8f1a470..f27cb98c0131d817e9091765f952bb202aebb6ec 100644 --- a/srml/aura/src/lib.rs +++ b/srml/aura/src/lib.rs @@ -148,10 +148,10 @@ pub trait Trait: timestamp::Trait { decl_storage! { trait Store for Module as Aura { /// The last timestamp. - LastTimestamp get(last) build(|_| 0.into()): T::Moment; + LastTimestamp get(fn last) build(|_| 0.into()): T::Moment; /// The current authorities - pub Authorities get(authorities): Vec; + pub Authorities get(fn authorities): Vec; } add_extra_genesis { config(authorities): Vec; @@ -182,6 +182,10 @@ impl Module { } } +impl sr_primitives::BoundToRuntimeAppPublic for Module { + type Public = T::AuthorityId; +} + impl session::OneSessionHandler for Module { type Key = T::AuthorityId; diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index 6dc8953e8832d1b69e4a68e7e8a5887c1c743c23..5c55e8bdd58dfbdbede54cdf3605cef317f88ebf 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -26,7 +26,7 @@ use sr_primitives::{ }; use support::{impl_outer_origin, parameter_types}; use runtime_io; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; impl_outer_origin!{ pub enum Origin for Test {} @@ -54,7 +54,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -73,7 +72,7 @@ impl Trait for Test { type AuthorityId = AuthorityId; } -pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { +pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig::{ authorities: authorities.into_iter().map(|a| UintAuthorityId(a).to_public_key()).collect(), diff --git a/srml/aura/src/tests.rs b/srml/aura/src/tests.rs index a90eddf18f73095fd2072c715962a907f90a8050..a90ec3a861e84f1d2ac1702efb315ff3fdd150f9 100644 --- a/srml/aura/src/tests.rs +++ b/srml/aura/src/tests.rs @@ -18,12 +18,11 @@ #![cfg(test)] -use runtime_io::with_externalities; use crate::mock::{Aura, new_test_ext}; #[test] fn initial_values() { - with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || { + new_test_ext(vec![0, 1, 2, 3]).execute_with(|| { assert_eq!(Aura::last(), 0u64); assert_eq!(Aura::authorities().len(), 4); }); diff --git a/srml/authority-discovery/src/lib.rs b/srml/authority-discovery/src/lib.rs index 2ec9e988358cc93c6fb91fceac43e3aaa43ef95b..8c2951f3acbccf2de15a025d7123f73c5542cd34 100644 --- a/srml/authority-discovery/src/lib.rs +++ b/srml/authority-discovery/src/lib.rs @@ -41,7 +41,7 @@ pub trait Trait: system::Trait + session::Trait { decl_storage! { trait Store for Module as AuthorityDiscovery { /// The current set of keys that may issue a heartbeat. - Keys get(keys): Vec; + Keys get(fn keys): Vec; } add_extra_genesis { config(keys): Vec; @@ -107,6 +107,10 @@ impl Module { } } +impl sr_primitives::BoundToRuntimeAppPublic for Module { + type Public = T::AuthorityId; +} + impl session::OneSessionHandler for Module { type Key = T::AuthorityId; @@ -135,12 +139,12 @@ impl session::OneSessionHandler for Module { mod tests { use super::*; use app_crypto::Pair; - use primitives::testing::KeyStore; - use primitives::{crypto::key_types, sr25519, traits::BareCryptoStore, H256}; - use runtime_io::{with_externalities, TestExternalities}; - use sr_primitives::testing::{Header, UintAuthorityId}; - use sr_primitives::traits::{ConvertInto, IdentityLookup, OpaqueKeys}; - use sr_primitives::Perbill; + use primitives::{testing::KeyStore, crypto::key_types, sr25519, H256, traits::KeystoreExt}; + use runtime_io::TestExternalities; + use sr_primitives::{ + testing::{Header, UintAuthorityId}, traits::{ConvertInto, IdentityLookup, OpaqueKeys}, + Perbill, KeyTypeId, + }; use support::{impl_outer_origin, parameter_types}; type AuthorityDiscovery = Module; @@ -204,7 +208,6 @@ mod tests { type AccountId = AuthorityId; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -219,6 +222,8 @@ mod tests { pub struct TestSessionHandler; impl session::SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[key_types::DUMMY]; + fn on_new_session( _changed: bool, _validators: &[(AuthorityId, Ks)], @@ -261,9 +266,9 @@ mod tests { // Create externalities. let mut externalities = TestExternalities::new(t); - externalities.set_keystore(key_store); + externalities.register_extension(KeystoreExt(key_store)); - with_externalities(&mut externalities, || { + externalities.execute_with(|| { assert_eq!( authority_id, AuthorityDiscovery::authority_id().expect("Retrieving public key.") @@ -298,9 +303,9 @@ mod tests { // Create externalities. let mut externalities = TestExternalities::new(t); - externalities.set_keystore(key_store); + externalities.register_extension(KeystoreExt(key_store)); - with_externalities(&mut externalities, || { + externalities.execute_with(|| { assert_eq!(None, AuthorityDiscovery::authority_id()); }); } @@ -335,9 +340,9 @@ mod tests { // Create externalities. let mut externalities = TestExternalities::new(t); - externalities.set_keystore(key_store); + externalities.register_extension(KeystoreExt(key_store)); - with_externalities(&mut externalities, || { + externalities.execute_with(|| { let payload = String::from("test payload").into_bytes(); let (sig, authority_id) = AuthorityDiscovery::sign(&payload).expect("signature"); @@ -350,7 +355,7 @@ mod tests { assert!(!AuthorityDiscovery::verify( &String::from("other payload").into_bytes(), sig, - authority_id + authority_id, )) }); } diff --git a/srml/authorship/Cargo.toml b/srml/authorship/Cargo.toml index e860be2f644849ec6e01dda816011c9423ef5279..a59976c69909656deb8e0f31aee178d49ff8ec68 100644 --- a/srml/authorship/Cargo.toml +++ b/srml/authorship/Cargo.toml @@ -14,7 +14,7 @@ sr-primitives = { path = "../../core/sr-primitives", default-features = false } support = { package = "srml-support", path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } runtime-io ={ package = "sr-io", path = "../../core/sr-io", default-features = false } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [features] default = ["std"] diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs index 490774c734e589a3945db8de65d8efcf07f2b530..173af5f729cbb8df5b306e40916b01eb6f38a71b 100644 --- a/srml/authorship/src/lib.rs +++ b/srml/authorship/src/lib.rs @@ -195,8 +195,8 @@ where } } -#[derive(Encode, Decode)] -#[cfg_attr(any(feature = "std", test), derive(PartialEq, Debug))] +#[derive(Encode, Decode, sr_primitives::RuntimeDebug)] +#[cfg_attr(any(feature = "std", test), derive(PartialEq))] enum UncleEntryItem { InclusionHeight(BlockNumber), Uncle(Hash, Option), @@ -412,12 +412,10 @@ impl ProvideInherent for Module { #[cfg(test)] mod tests { use super::*; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; - use sr_primitives::traits::{BlakeTwo256, IdentityLookup}; - use sr_primitives::testing::Header; - use sr_primitives::generic::DigestItem; - use sr_primitives::Perbill; + use primitives::H256; + use sr_primitives::{ + traits::{BlakeTwo256, IdentityLookup}, testing::Header, generic::DigestItem, Perbill, + }; use support::{parameter_types, impl_outer_origin, ConsensusEngineId}; impl_outer_origin!{ @@ -444,7 +442,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -535,7 +532,7 @@ mod tests { ) } - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let t = system::GenesisConfig::default().build_storage::().unwrap(); t.into() } @@ -543,7 +540,7 @@ mod tests { #[test] fn prune_old_uncles_works() { use UncleEntryItem::*; - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let hash = Default::default(); let author = Default::default(); let uncles = vec![ @@ -562,7 +559,7 @@ mod tests { #[test] fn rejects_bad_uncles() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let author_a = 69; struct CanonChain { @@ -675,7 +672,7 @@ mod tests { #[test] fn sets_author_lazily() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let author = 42; let mut header = seal_header( create_header(1, Default::default(), [1; 32].into()), diff --git a/srml/babe/src/lib.rs b/srml/babe/src/lib.rs index 330ec89ba5587c39702cfd0ae0159adde765d7ca..e12e123daf6f265a5cb921e5857e0a00eecce33a 100644 --- a/srml/babe/src/lib.rs +++ b/srml/babe/src/lib.rs @@ -122,8 +122,52 @@ impl ProvideInherentData for InherentDataProvider { } pub trait Trait: timestamp::Trait { + /// The amount of time, in slots, that each epoch should last. type EpochDuration: Get; + + /// The expected average block time at which BABE should be creating + /// blocks. Since BABE is probabilistic it is not trivial to figure out + /// what the expected average block time should be based on the slot + /// duration and the security parameter `c` (where `1 - c` represents + /// the probability of a slot being empty). type ExpectedBlockTime: Get; + + /// BABE requires some logic to be triggered on every block to query for whether an epoch + /// has ended and to perform the transition to the next epoch. + /// + /// Typically, the `ExternalTrigger` type should be used. An internal trigger should only be used + /// when no other module is responsible for changing authority set. + type EpochChangeTrigger: EpochChangeTrigger; +} + +/// Trigger an epoch change, if any should take place. +pub trait EpochChangeTrigger { + /// Trigger an epoch change, if any should take place. This should be called + /// during every block, after initialization is done. + fn trigger(now: T::BlockNumber); +} + +/// A type signifying to BABE that an external trigger +/// for epoch changes (e.g. srml-session) is used. +pub struct ExternalTrigger; + +impl EpochChangeTrigger for ExternalTrigger { + fn trigger(_: T::BlockNumber) { } // nothing - trigger is external. +} + +/// A type signifying to BABE that it should perform epoch changes +/// with an internal trigger, recycling the same authorities forever. +pub struct SameAuthoritiesForever; + +impl EpochChangeTrigger for SameAuthoritiesForever { + fn trigger(now: T::BlockNumber) { + if >::should_epoch_change(now) { + let authorities = >::authorities(); + let next_authorities = authorities.clone(); + + >::enact_epoch_change(authorities, next_authorities); + } + } } /// The length of the BABE randomness @@ -136,17 +180,17 @@ type MaybeVrf = Option<[u8; 32 /* VRF_OUTPUT_LENGTH */]>; decl_storage! { trait Store for Module as Babe { /// Current epoch index. - pub EpochIndex get(epoch_index): u64; + pub EpochIndex get(fn epoch_index): u64; /// Current epoch authorities. - pub Authorities get(authorities): Vec<(AuthorityId, BabeAuthorityWeight)>; + pub Authorities get(fn authorities): Vec<(AuthorityId, BabeAuthorityWeight)>; /// The slot at which the first epoch actually started. This is 0 /// until the first block of the chain. - pub GenesisSlot get(genesis_slot): u64; + pub GenesisSlot get(fn genesis_slot): u64; /// Current slot number. - pub CurrentSlot get(current_slot): u64; + pub CurrentSlot get(fn current_slot): u64; /// The epoch randomness for the *current* epoch. /// @@ -161,7 +205,7 @@ decl_storage! { // NOTE: the following fields don't use the constants to define the // array size because the metadata API currently doesn't resolve the // variable to its underlying value. - pub Randomness get(randomness): [u8; 32 /* RANDOMNESS_LENGTH */]; + pub Randomness get(fn randomness): [u8; 32 /* RANDOMNESS_LENGTH */]; /// Next epoch randomness. NextRandomness: [u8; 32 /* RANDOMNESS_LENGTH */]; @@ -180,7 +224,7 @@ decl_storage! { /// Temporary value (cleared at block finalization) which is `Some` /// if per-block initialization has already been called for current block. - Initialized get(initialized): Option; + Initialized get(fn initialized): Option; } add_extra_genesis { config(authorities): Vec<(AuthorityId, BabeAuthorityWeight)>; @@ -203,8 +247,8 @@ decl_module! { const ExpectedBlockTime: T::Moment = T::ExpectedBlockTime::get(); /// Initialization - fn on_initialize() { - Self::do_initialize(); + fn on_initialize(now: T::BlockNumber) { + Self::do_initialize(now); } /// Block finalization @@ -263,21 +307,10 @@ impl session::ShouldEndSession for Module { // it might be (and it is in current implementation) that session module is calling // should_end_session() from it's own on_initialize() handler // => because session on_initialize() is called earlier than ours, let's ensure - // that we have synced with digest before checking if session should be ended - Self::do_initialize(); + // that we have synced with digest before checking if session should be ended. + Self::do_initialize(now); - // The session has technically ended during the passage of time - // between this block and the last, but we have to "end" the session now, - // since there is no earlier possible block we could have done it. - // - // The exception is for block 1: the genesis has slot 0, so we treat - // epoch 0 as having started at the slot of block 1. We want to use - // the same randomness and validator set as signalled in the genesis, - // so we don't rotate the session. - now != sr_primitives::traits::One::one() && { - let diff = CurrentSlot::get().saturating_sub(Self::current_epoch_start()); - diff >= T::EpochDuration::get() - } + Self::should_epoch_change(now) } } @@ -336,6 +369,69 @@ impl Module { ::MinimumPeriod::get().saturating_mul(2.into()) } + /// Determine whether an epoch change should take place at this block. + /// Assumes that initialization has already taken place. + pub fn should_epoch_change(now: T::BlockNumber) -> bool { + // The epoch has technically ended during the passage of time + // between this block and the last, but we have to "end" the epoch now, + // since there is no earlier possible block we could have done it. + // + // The exception is for block 1: the genesis has slot 0, so we treat + // epoch 0 as having started at the slot of block 1. We want to use + // the same randomness and validator set as signalled in the genesis, + // so we don't rotate the epoch. + now != sr_primitives::traits::One::one() && { + let diff = CurrentSlot::get().saturating_sub(Self::current_epoch_start()); + diff >= T::EpochDuration::get() + } + } + + /// DANGEROUS: Enact an epoch change. Should be done on every block where `should_epoch_change` has returned `true`, + /// and the caller is the only caller of this function. + /// + /// Typically, this is not handled directly by the user, but by higher-level validator-set manager logic like + /// `srml-session`. + pub fn enact_epoch_change( + authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, + next_authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, + ) { + // PRECONDITION: caller has done initialization and is guaranteed + // by the session module to be called before this. + #[cfg(debug_assertions)] + { + assert!(Self::initialized().is_some()) + } + + // Update epoch index + let epoch_index = EpochIndex::get() + .checked_add(1) + .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); + + EpochIndex::put(epoch_index); + Authorities::put(authorities); + + // Update epoch randomness. + let next_epoch_index = epoch_index + .checked_add(1) + .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); + + // Returns randomness for the current epoch and computes the *next* + // epoch randomness. + let randomness = Self::randomness_change_epoch(next_epoch_index); + Randomness::put(randomness); + + // After we update the current epoch, we signal the *next* epoch change + // so that nodes can track changes. + let next_randomness = NextRandomness::get(); + + let next = NextEpochDescriptor { + authorities: next_authorities, + randomness: next_randomness, + }; + + Self::deposit_consensus(ConsensusLog::NextEpochData(next)) + } + // 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`). @@ -363,7 +459,7 @@ impl Module { } } - fn do_initialize() { + fn do_initialize(now: T::BlockNumber) { // since do_initialize can be called twice (if session module is present) // => let's ensure that we only modify the storage once per block let initialized = Self::initialized().is_some(); @@ -414,6 +510,9 @@ impl Module { }); Initialized::put(maybe_vrf); + + // enact epoch change, if necessary. + T::EpochChangeTrigger::trigger::(now) } /// Call this function exactly once when an epoch changes, to update the @@ -447,6 +546,10 @@ impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } +impl sr_primitives::BoundToRuntimeAppPublic for Module { + type Public = AuthorityId; +} + impl session::OneSessionHandler for Module { type Key = AuthorityId; @@ -460,51 +563,15 @@ impl session::OneSessionHandler for Module { fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I) where I: Iterator { - // PRECONDITION: `should_end_session` has done initialization and is guaranteed - // by the session module to be called before this. - #[cfg(debug_assertions)] - { - assert!(Self::initialized().is_some()) - } - - // Update epoch index - let epoch_index = EpochIndex::get() - .checked_add(1) - .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); - - EpochIndex::put(epoch_index); - - // Update authorities. let authorities = validators.map(|(_account, k)| { (k, 1) }).collect::>(); - Authorities::put(authorities); - - // Update epoch randomness. - let next_epoch_index = epoch_index - .checked_add(1) - .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); - - // Returns randomness for the current epoch and computes the *next* - // epoch randomness. - let randomness = Self::randomness_change_epoch(next_epoch_index); - Randomness::put(randomness); - - // After we update the current epoch, we signal the *next* epoch change - // so that nodes can track changes. let next_authorities = queued_validators.map(|(_account, k)| { (k, 1) }).collect::>(); - let next_randomness = NextRandomness::get(); - - let next = NextEpochDescriptor { - authorities: next_authorities, - randomness: next_randomness, - }; - - Self::deposit_consensus(ConsensusLog::NextEpochData(next)) + Self::enact_epoch_change(authorities, next_authorities) } fn on_disabled(i: usize) { diff --git a/srml/babe/src/mock.rs b/srml/babe/src/mock.rs index 741f08fc0845c30b048a0b0d36e2891571cea1f4..d7aed7b42a3fe27e513ec7276ea3e1ccecd4d2d8 100644 --- a/srml/babe/src/mock.rs +++ b/srml/babe/src/mock.rs @@ -20,9 +20,7 @@ use super::{Trait, Module, GenesisConfig}; use babe_primitives::AuthorityId; use sr_primitives::{ - traits::IdentityLookup, Perbill, - testing::{Header, UintAuthorityId}, - impl_opaque_keys, key_types::DUMMY, + traits::IdentityLookup, Perbill, testing::{Header, UintAuthorityId}, impl_opaque_keys, }; use sr_version::RuntimeVersion; use support::{impl_outer_origin, parameter_types}; @@ -62,7 +60,6 @@ impl system::Trait for Test { type AccountId = DummyValidatorId; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -72,7 +69,6 @@ impl system::Trait for Test { impl_opaque_keys! { pub struct MockSessionKeys { - #[id(DUMMY)] pub dummy: UintAuthorityId, } } @@ -98,9 +94,10 @@ impl timestamp::Trait for Test { impl Trait for Test { type EpochDuration = EpochDuration; type ExpectedBlockTime = ExpectedBlockTime; + type EpochChangeTrigger = crate::ExternalTrigger; } -pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { +pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig { authorities: authorities.into_iter().map(|a| (UintAuthorityId(a).to_public_key(), 1)).collect(), diff --git a/srml/babe/src/tests.rs b/srml/babe/src/tests.rs index ef449485b77ed530abad06ac519beef4c0a90f78..f860375de487a646cf7ed89efec13bb9a859d0ef 100644 --- a/srml/babe/src/tests.rs +++ b/srml/babe/src/tests.rs @@ -17,7 +17,6 @@ //! Consensus extension module tests for BABE consensus. use super::*; -use runtime_io::with_externalities; use mock::{new_test_ext, Babe, Test}; use sr_primitives::{traits::OnFinalize, testing::{Digest, DigestItem}}; use session::ShouldEndSession; @@ -53,14 +52,14 @@ fn empty_randomness_is_correct() { #[test] fn initial_values() { - with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || { + new_test_ext(vec![0, 1, 2, 3]).execute_with(|| { assert_eq!(Babe::authorities().len(), 4) }) } #[test] fn check_module() { - with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || { + new_test_ext(vec![0, 1, 2, 3]).execute_with(|| { assert!(!Babe::should_end_session(0), "Genesis does not change sessions"); assert!(!Babe::should_end_session(200000), "BABE does not include the block number in epoch calculations"); @@ -71,7 +70,7 @@ type System = system::Module; #[test] fn first_block_epoch_zero_start() { - with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || { + new_test_ext(vec![0, 1, 2, 3]).execute_with(|| { let genesis_slot = 100; let first_vrf = [1; 32]; let pre_digest = make_pre_digest( @@ -119,7 +118,7 @@ fn first_block_epoch_zero_start() { #[test] fn authority_index() { - with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || { + new_test_ext(vec![0, 1, 2, 3]).execute_with(|| { assert_eq!( Babe::find_author((&[(BABE_ENGINE_ID, &[][..])]).into_iter().cloned()), None, "Trivially invalid authorities are ignored") diff --git a/srml/balances/Cargo.toml b/srml/balances/Cargo.toml index d2b6e00d98e8b5e01c9701bb9229744001e8e2fa..f7f6041c6bb1629b99c3571f27925050a5669a62 100644 --- a/srml/balances/Cargo.toml +++ b/srml/balances/Cargo.toml @@ -17,6 +17,7 @@ system = { package = "srml-system", path = "../system", default-features = false [dev-dependencies] runtime-io = { package = "sr-io", path = "../../core/sr-io" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } +transaction-payment = { package = "srml-transaction-payment", path = "../transaction-payment" } [features] default = ["std"] diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 03e32e1ddf295d11e01a2c79dce854045439809a..71f37cb8f819326a57f9d01d9a64e62632f82756 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -86,17 +86,6 @@ //! //! - `vesting_balance` - Get the amount that is currently being vested and cannot be transferred out of this account. //! -//! ### Signed Extensions -//! -//! The balances module defines the following extensions: -//! -//! - [`TakeFees`]: Consumes fees proportional to the length and weight of the transaction. -//! Additionally, it can contain a single encoded payload as a `tip`. The inclusion priority -//! is increased proportional to the tip. -//! -//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed -//! extensions included in a chain. -//! //! ## Usage //! //! The following examples show how to use the Balances module in your custom module. @@ -159,7 +148,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use rstd::prelude::*; -use rstd::{cmp, result, mem}; +use rstd::{cmp, result, mem, fmt::Debug}; use codec::{Codec, Encode, Decode}; use support::{ StorageValue, Parameter, decl_event, decl_storage, decl_module, @@ -171,15 +160,12 @@ use support::{ dispatch::Result, }; use sr_primitives::{ - transaction_validity::{ - TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, - TransactionValidity, - }, + RuntimeDebug, traits::{ - Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug, - Saturating, Bounded, SignedExtension, SaturatedConversion, Convert, + Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, + Saturating, Bounded, }, - weights::{DispatchInfo, SimpleDispatchInfo, Weight}, + weights::SimpleDispatchInfo, }; use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; @@ -191,7 +177,7 @@ pub use self::imbalances::{PositiveImbalance, NegativeImbalance}; pub trait Subtrait: system::Trait { /// The balance of an account. type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + - MaybeSerializeDebug + From; + MaybeSerializeDeserialize + Debug + From; /// A function that is invoked when the free-balance has fallen below the existential deposit and /// has been reduced to zero. @@ -210,21 +196,12 @@ pub trait Subtrait: system::Trait { /// The fee required to create an account. type CreationFee: Get; - - /// The fee to be paid for making a transaction; the base. - type TransactionBaseFee: Get; - - /// The fee to be paid for making a transaction; the per-byte portion. - type TransactionByteFee: Get; - - /// Convert a weight value into a deductible fee based on the currency type. - type WeightToFee: Convert; } pub trait Trait: system::Trait { /// The balance of an account. type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + - MaybeSerializeDebug + From; + MaybeSerializeDeserialize + Debug + From; /// A function that is invoked when the free-balance has fallen below the existential deposit and /// has been reduced to zero. @@ -235,9 +212,6 @@ pub trait Trait: system::Trait { /// Handler for when a new account is created. type OnNewAccount: OnNewAccount; - /// Handler for the unbalanced reduction when taking transaction fees. - type TransactionPayment: OnUnbalanced>; - /// Handler for the unbalanced reduction when taking fees associated with balance /// transfer (which may also include account creation). type TransferPayment: OnUnbalanced>; @@ -256,15 +230,6 @@ pub trait Trait: system::Trait { /// The fee required to create an account. type CreationFee: Get; - - /// The fee to be paid for making a transaction; the base. - type TransactionBaseFee: Get; - - /// The fee to be paid for making a transaction; the per-byte portion. - type TransactionByteFee: Get; - - /// Convert a weight value into a deductible fee based on the currency type. - type WeightToFee: Convert; } impl, I: Instance> Subtrait for T { @@ -274,9 +239,6 @@ impl, I: Instance> Subtrait for T { type ExistentialDeposit = T::ExistentialDeposit; type TransferFee = T::TransferFee; type CreationFee = T::CreationFee; - type TransactionBaseFee = T::TransactionBaseFee; - type TransactionByteFee = T::TransactionByteFee; - type WeightToFee = T::WeightToFee; } decl_event!( @@ -294,8 +256,7 @@ decl_event!( ); /// Struct to encode the vesting schedule of an individual account. -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] pub struct VestingSchedule { /// Locked amount at genesis. pub locked: Balance, @@ -322,8 +283,7 @@ impl Ves } } -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct BalanceLock { pub id: LockIdentifier, pub amount: Balance, @@ -334,12 +294,12 @@ pub struct BalanceLock { decl_storage! { trait Store for Module, I: Instance=DefaultInstance> as Balances { /// The total units issued in the system. - pub TotalIssuance get(total_issuance) build(|config: &GenesisConfig| { + pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { 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(vesting) build(|config: &GenesisConfig| { + 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 @@ -376,7 +336,7 @@ decl_storage! { /// /// `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(free_balance) + pub FreeBalance get(fn free_balance) build(|config: &GenesisConfig| config.balances.clone()): map T::AccountId => T::Balance; @@ -391,10 +351,10 @@ decl_storage! { /// /// `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(reserved_balance): map T::AccountId => T::Balance; + pub ReservedBalance get(fn reserved_balance): map T::AccountId => T::Balance; /// Any liquidity locks on some account balances. - pub Locks get(locks): map T::AccountId => Vec>; + pub Locks get(fn locks): map T::AccountId => Vec>; } add_extra_genesis { config(balances): Vec<(T::AccountId, T::Balance)>; @@ -414,12 +374,6 @@ decl_module! { /// The fee required to create an account. const CreationFee: T::Balance = T::CreationFee::get(); - /// The fee to be paid for making a transaction; the base. - const TransactionBaseFee: T::Balance = T::TransactionBaseFee::get(); - - /// The fee to be paid for making a transaction; the per-byte portion. - const TransactionByteFee: T::Balance = T::TransactionByteFee::get(); - fn deposit_event() = default; /// Transfer some liquid free balance to another account. @@ -776,7 +730,7 @@ mod imbalances { // 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) +// depend on the Imbalance type (TransferPayment, DustRemoval) // are placed in their own SRML module. struct ElevatedTrait, I: Instance>(T, I); impl, I: Instance> Clone for ElevatedTrait { @@ -796,7 +750,6 @@ impl, I: Instance> system::Trait for ElevatedTrait { type AccountId = T::AccountId; type Lookup = T::Lookup; type Header = T::Header; - type WeightMultiplierUpdate = T::WeightMultiplierUpdate; type Event = (); type BlockHashCount = T::BlockHashCount; type MaximumBlockWeight = T::MaximumBlockWeight; @@ -809,20 +762,16 @@ impl, I: Instance> Trait for ElevatedTrait { type OnFreeBalanceZero = T::OnFreeBalanceZero; type OnNewAccount = T::OnNewAccount; type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = T::ExistentialDeposit; type TransferFee = T::TransferFee; type CreationFee = T::CreationFee; - type TransactionBaseFee = T::TransactionBaseFee; - type TransactionByteFee = T::TransactionByteFee; - type WeightToFee = T::WeightToFee; } impl, I: Instance> Currency for Module where - T::Balance: MaybeSerializeDebug + T::Balance: MaybeSerializeDeserialize + Debug { type Balance = T::Balance; type PositiveImbalance = PositiveImbalance; @@ -875,13 +824,13 @@ where fn ensure_can_withdraw( who: &T::AccountId, _amount: T::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, new_balance: T::Balance, ) -> Result { - match reason { - WithdrawReason::Reserve | WithdrawReason::Transfer if Self::vesting_balance(who) > new_balance => - return Err("vesting balance too high to send value"), - _ => {} + if reasons.intersects(WithdrawReason::Reserve | WithdrawReason::Transfer) + && Self::vesting_balance(who) > new_balance + { + return Err("vesting balance too high to send value"); } let locks = Self::locks(who); if locks.is_empty() { @@ -893,7 +842,7 @@ where .all(|l| now >= l.until || new_balance >= l.amount - || !l.reasons.contains(reason) + || !l.reasons.intersects(reasons) ) { Ok(()) @@ -919,7 +868,7 @@ where if would_create && value < T::ExistentialDeposit::get() { return Err("value too low to create account"); } - Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; + Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer.into(), new_from_balance)?; // NOTE: total stake being stored in the same type means that this could never overflow // but better to be safe than sorry. @@ -944,14 +893,21 @@ where fn withdraw( who: &T::AccountId, value: Self::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, liveness: ExistenceRequirement, ) -> result::Result { - if let Some(new_balance) = Self::free_balance(who).checked_sub(&value) { - if liveness == ExistenceRequirement::KeepAlive && new_balance < T::ExistentialDeposit::get() { + 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() + { return Err("payment would kill account") } - Self::ensure_can_withdraw(who, value, reason, new_balance)?; + Self::ensure_can_withdraw(who, value, reasons, new_balance)?; Self::set_free_balance(who, new_balance); Ok(NegativeImbalance::new(value)) } else { @@ -1052,13 +1008,13 @@ where impl, I: Instance> ReservableCurrency for Module where - T::Balance: MaybeSerializeDebug + T::Balance: MaybeSerializeDeserialize + Debug { fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { Self::free_balance(who) .checked_sub(&value) .map_or(false, |new_balance| - Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance).is_ok() + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), new_balance).is_ok() ) } @@ -1072,7 +1028,7 @@ where return Err("not enough free funds") } let new_balance = b - value; - Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance)?; + 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(()) @@ -1115,7 +1071,7 @@ where impl, I: Instance> LockableCurrency for Module where - T::Balance: MaybeSerializeDebug + T::Balance: MaybeSerializeDeserialize + Debug { type Moment = T::BlockNumber; @@ -1187,94 +1143,9 @@ where } } -/// Require the transactor pay for themselves and maybe include a tip to gain additional priority -/// in the queue. -#[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct TakeFees, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance); - -impl, I: Instance> TakeFees { - /// utility constructor. Used only in client/factory code. - pub fn from(fee: T::Balance) -> Self { - Self(fee) - } - - /// Compute the final fee value for a particular transaction. - /// - /// The final fee is composed of: - /// - _length-fee_: This is the amount paid merely to pay for size of the transaction. - /// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike - /// size-fee, this is not input dependent and reflects the _complexity_ of the execution - /// and the time it consumes. - /// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed - /// transactions can have a tip. - fn compute_fee(len: usize, info: DispatchInfo, tip: T::Balance) -> T::Balance { - let len_fee = if info.pay_length_fee() { - let len = T::Balance::from(len as u32); - let base = T::TransactionBaseFee::get(); - let per_byte = T::TransactionByteFee::get(); - base.saturating_add(per_byte.saturating_mul(len)) - } else { - Zero::zero() - }; - - let weight_fee = { - // cap the weight to the maximum defined in runtime, otherwise it will be the `Bounded` - // maximum of its data type, which is not desired. - let capped_weight = info.weight.min(::MaximumBlockWeight::get()); - let weight_update = >::next_weight_multiplier(); - let adjusted_weight = weight_update.apply_to(capped_weight); - T::WeightToFee::convert(adjusted_weight) - }; - - len_fee.saturating_add(weight_fee).saturating_add(tip) - } -} - -#[cfg(feature = "std")] -impl, I: Instance> rstd::fmt::Debug for TakeFees { - fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { - self.0.fmt(f) - } -} - -impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { - type AccountId = T::AccountId; - type Call = T::Call; - type AdditionalSigned = (); - type Pre = (); - fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) } - - fn validate( - &self, - who: &Self::AccountId, - _call: &Self::Call, - info: DispatchInfo, - len: usize, - ) -> TransactionValidity { - // pay any fees. - let fee = Self::compute_fee(len, info, self.0); - let imbalance = match >::withdraw( - who, - fee, - WithdrawReason::TransactionPayment, - ExistenceRequirement::KeepAlive, - ) { - Ok(imbalance) => imbalance, - Err(_) => return InvalidTransaction::Payment.into(), - }; - T::TransactionPayment::on_unbalanced(imbalance); - - let mut r = ValidTransaction::default(); - // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which - // will be a bit more than setting the priority to tip. For now, this is enough. - r.priority = fee.saturated_into::(); - Ok(r) - } -} - impl, I: Instance> IsDeadAccount for Module where - T::Balance: MaybeSerializeDebug + T::Balance: MaybeSerializeDeserialize + Debug { fn is_dead_account(who: &T::AccountId) -> bool { Self::total_balance(who).is_zero() diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 12a49fc90d2cfa987197553252200ece1ae68a3b..600d0e6fb78799dd796ab9a66d4dd67bbe133225 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -18,9 +18,9 @@ #![cfg(test)] -use sr_primitives::{Perbill, traits::{Convert, IdentityLookup}, testing::Header, +use sr_primitives::{Perbill, traits::{ConvertInto, IdentityLookup}, testing::Header, weights::{DispatchInfo, Weight}}; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; use runtime_io; use support::{impl_outer_origin, parameter_types}; use support::traits::Get; @@ -35,10 +35,6 @@ thread_local! { static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); static TRANSFER_FEE: RefCell = RefCell::new(0); static CREATION_FEE: RefCell = RefCell::new(0); - static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); - static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); - static TRANSACTION_WEIGHT_FEE: RefCell = RefCell::new(1); - static WEIGHT_TO_FEE: RefCell = RefCell::new(1); } pub struct ExistentialDeposit; @@ -56,23 +52,6 @@ impl Get for CreationFee { fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } } -pub struct TransactionBaseFee; -impl Get for TransactionBaseFee { - fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) } -} - -pub struct TransactionByteFee; -impl Get for TransactionByteFee { - fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) } -} - -pub struct WeightToFee(u64); -impl Convert for WeightToFee { - fn convert(t: Weight) -> u64 { - WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64)) - } -} - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Runtime; @@ -92,7 +71,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -100,26 +78,31 @@ impl system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); } +parameter_types! { + pub const TransactionBaseFee: u64 = 0; + pub const TransactionByteFee: u64 = 1; +} +impl transaction_payment::Trait for Runtime { + type Currency = Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); +} impl Trait for Runtime { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = WeightToFee; } pub struct ExtBuilder { - transaction_base_fee: u64, - transaction_byte_fee: u64, - weight_to_fee: u64, existential_deposit: u64, transfer_fee: u64, creation_fee: u64, @@ -129,9 +112,6 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { - transaction_base_fee: 0, - transaction_byte_fee: 0, - weight_to_fee: 0, existential_deposit: 0, transfer_fee: 0, creation_fee: 0, @@ -141,12 +121,6 @@ impl Default for ExtBuilder { } } impl ExtBuilder { - pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64, weight_fee: u64) -> Self { - self.transaction_base_fee = base_fee; - self.transaction_byte_fee = byte_fee; - self.weight_to_fee = weight_fee; - self - } pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { self.existential_deposit = existential_deposit; self @@ -175,11 +149,8 @@ impl ExtBuilder { 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); - TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.transaction_base_fee); - TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.transaction_byte_fee); - WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); } - pub fn build(self) -> runtime_io::TestExternalities { + pub fn build(self) -> runtime_io::TestExternalities { self.set_associated_consts(); let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig:: { @@ -211,7 +182,6 @@ impl ExtBuilder { pub type System = system::Module; pub type Balances = Module; - pub const CALL: &::Call = &(); /// create a transaction info struct from weight. Handy to avoid building the whole struct. diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 8e9f6acdd850b6cc055f31615e5ddc4ceb88047b..839ac67991c83f92142965c49b6fa979599c939c 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -20,12 +20,13 @@ use super::*; use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight, CALL}; -use runtime_io::with_externalities; +use sr_primitives::traits::SignedExtension; use support::{ assert_noop, assert_ok, assert_err, traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, Currency, ReservableCurrency} }; +use transaction_payment::ChargeTransactionPayment; use system::RawOrigin; const ID_1: LockIdentifier = *b"1 "; @@ -34,7 +35,7 @@ const ID_3: LockIdentifier = *b"3 "; #[test] fn basic_locking_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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!( @@ -46,7 +47,7 @@ fn basic_locking_should_work() { #[test] fn partial_locking_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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)); }); @@ -54,7 +55,7 @@ fn partial_locking_should_work() { #[test] fn lock_removal_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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)); @@ -63,7 +64,7 @@ fn lock_removal_should_work() { #[test] fn lock_replacement_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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)); @@ -72,7 +73,7 @@ fn lock_replacement_should_work() { #[test] fn double_locking_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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)); @@ -81,7 +82,7 @@ fn double_locking_should_work() { #[test] fn combination_locking_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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()); @@ -91,7 +92,7 @@ fn combination_locking_should_work() { #[test] fn lock_value_extension_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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), @@ -112,12 +113,11 @@ fn lock_value_extension_should_work() { #[test] fn lock_reasons_should_work() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(1) - .monied(true).transaction_fees(0, 1, 0) - .build(), - || { + 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), @@ -125,8 +125,8 @@ fn lock_reasons_should_work() { ); assert_ok!(>::reserve(&1, 1)); // NOTE: this causes a fee payment. - assert!( as SignedExtension>::pre_dispatch( - TakeFees::from(1), + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), &1, CALL, info_from_weight(1), @@ -139,8 +139,8 @@ fn lock_reasons_should_work() { >::reserve(&1, 1), "account liquidity restrictions prevent withdrawal" ); - assert!( as SignedExtension>::pre_dispatch( - TakeFees::from(1), + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), &1, CALL, info_from_weight(1), @@ -150,20 +150,19 @@ fn lock_reasons_should_work() { Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); assert_ok!(>::transfer(&1, &2, 1)); assert_ok!(>::reserve(&1, 1)); - assert!( as SignedExtension>::pre_dispatch( - TakeFees::from(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() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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), @@ -177,7 +176,7 @@ fn lock_block_number_should_work() { #[test] fn lock_block_number_extension_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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), @@ -199,7 +198,7 @@ fn lock_block_number_extension_should_work() { #[test] fn lock_reasons_extension_should_work() { - with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + 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), @@ -220,14 +219,12 @@ fn lock_reasons_extension_should_work() { #[test] fn default_indexing_on_new_accounts_should_not_work2() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(10) - .creation_fee(50) - .monied(true) - .build(), - || { - + 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!( @@ -236,18 +233,16 @@ fn default_indexing_on_new_accounts_should_not_work2() { ); 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() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(256 * 1) - .monied(true) - .build(), - || { + 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); @@ -274,14 +269,13 @@ fn reserved_balance_should_prevent_reclaim_count() { 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() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + 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); @@ -291,12 +285,11 @@ fn reward_should_work() { #[test] fn dust_account_removal_should_work() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(100) - .monied(true) - .build(), - || { + 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); @@ -305,19 +298,17 @@ fn dust_account_removal_should_work() { 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() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(100) - .creation_fee(50) - .monied(true) - .build(), - || { + 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); @@ -326,13 +317,12 @@ fn dust_account_removal_should_work2() { 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() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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); @@ -345,7 +335,7 @@ fn balance_works() { #[test] fn balance_transfer_works() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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); @@ -355,7 +345,7 @@ fn balance_transfer_works() { #[test] fn force_transfer_works() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 111); assert_noop!( Balances::force_transfer(Some(2).into(), 1, 2, 69), @@ -369,7 +359,7 @@ fn force_transfer_works() { #[test] fn reserving_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 111); assert_eq!(Balances::total_balance(&1), 111); @@ -386,7 +376,7 @@ fn reserving_balance_should_work() { #[test] fn balance_transfer_when_reserved_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 69)); assert_noop!( @@ -398,7 +388,7 @@ fn balance_transfer_when_reserved_should_not_work() { #[test] fn deducting_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 69)); assert_eq!(Balances::free_balance(&1), 42); @@ -407,7 +397,7 @@ fn deducting_balance_should_work() { #[test] fn refunding_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 42); Balances::set_reserved_balance(&1, 69); Balances::unreserve(&1, 69); @@ -418,7 +408,7 @@ fn refunding_balance_should_work() { #[test] fn slashing_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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()); @@ -430,7 +420,7 @@ fn slashing_balance_should_work() { #[test] fn slashing_incomplete_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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); @@ -442,7 +432,7 @@ fn slashing_incomplete_balance_should_work() { #[test] fn unreserving_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 111)); Balances::unreserve(&1, 42); @@ -453,7 +443,7 @@ fn unreserving_balance_should_work() { #[test] fn slashing_reserved_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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); @@ -465,7 +455,7 @@ fn slashing_reserved_balance_should_work() { #[test] fn slashing_incomplete_reserved_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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); @@ -477,7 +467,7 @@ fn slashing_incomplete_reserved_balance_should_work() { #[test] fn transferring_reserved_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 110); let _ = Balances::deposit_creating(&2, 1); assert_ok!(Balances::reserve(&1, 110)); @@ -491,7 +481,7 @@ fn transferring_reserved_balance_should_work() { #[test] fn transferring_reserved_balance_to_nonexistent_should_fail() { - with_externalities(&mut ExtBuilder::default().build(), || { + 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), "beneficiary account must pre-exist"); @@ -500,7 +490,7 @@ fn transferring_reserved_balance_to_nonexistent_should_fail() { #[test] fn transferring_incomplete_reserved_balance_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let _ = Balances::deposit_creating(&1, 110); let _ = Balances::deposit_creating(&2, 1); assert_ok!(Balances::reserve(&1, 41)); @@ -514,7 +504,7 @@ fn transferring_incomplete_reserved_balance_should_work() { #[test] fn transferring_too_high_value_should_not_panic() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { >::insert(1, u64::max_value()); >::insert(2, 1); @@ -530,89 +520,76 @@ fn transferring_too_high_value_should_not_panic() { #[test] fn account_create_on_free_too_low_with_other() { - with_externalities( - &mut ExtBuilder::default().existential_deposit(100).build(), - || { - let _ = Balances::deposit_creating(&1, 100); - assert_eq!(>::get(), 100); + 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); - } - ) + // 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() { - with_externalities( - &mut ExtBuilder::default().existential_deposit(100).build(), - || { - // No-op. - let _ = Balances::deposit_creating(&2, 50); - assert_eq!(Balances::free_balance(&2), 0); - assert_eq!(>::get(), 0); - } - ) + 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() { - with_externalities( - &mut ExtBuilder::default().existential_deposit(100).build(), - || { - assert_eq!(>::get(), 0); + 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); + // 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); + 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)); + // 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 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); - }, - ); + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 130); + }); } #[test] fn transfer_overflow_isnt_exploitable() { - with_externalities( - &mut ExtBuilder::default().creation_fee(50).build(), - || { - // 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), - "got overflow after adding a fee to value", - ); - } - ); + 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), + "got overflow after adding a fee to value", + ); + }); } #[test] fn check_vesting_status() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(256) - .monied(true) - .vesting(true) - .build(), - || { + 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); @@ -663,19 +640,17 @@ fn check_vesting_status() { 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() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .vesting(true) - .build(), - || { + 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 @@ -685,38 +660,34 @@ fn unvested_balance_should_not_transfer() { Balances::transfer(Some(1).into(), 2, 56), "vesting balance too high to send value", ); // Account 1 cannot send more than vested amount - } - ); + }); } #[test] fn vested_balance_should_transfer() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .vesting(true) - .build(), - || { + 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() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(10) - .monied(true) - .vesting(true) - .build(), - || { + 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)); @@ -734,19 +705,17 @@ fn extra_balance_should_transfer() { // 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() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(256) - .monied(true) - .vesting(true) - .build(), - || { + 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); @@ -764,62 +733,16 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { // Account 12 can still send liquid funds assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); - } - ); -} - -#[test] -fn signed_extension_take_fees_work() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(10) - .transaction_fees(10, 1, 5) - .monied(true) - .build(), - || { - let len = 10; - assert!(TakeFees::::from(0).pre_dispatch(&1, CALL, info_from_weight(5), len).is_ok()); - assert_eq!(Balances::free_balance(&1), 100 - 20 - 25); - assert!(TakeFees::::from(5 /* tipped */).pre_dispatch(&1, CALL, info_from_weight(3), len).is_ok()); - assert_eq!(Balances::free_balance(&1), 100 - 20 - 25 - 20 - 5 - 15); - } - ); -} - -#[test] -fn signed_extension_take_fees_is_bounded() { - with_externalities( - &mut ExtBuilder::default() - .existential_deposit(1000) - .transaction_fees(0, 0, 1) - .monied(true) - .build(), - || { - use sr_primitives::weights::Weight; - - // maximum weight possible - assert!(TakeFees::::from(0).pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10).is_ok()); - // fee will be proportional to what is the actual maximum weight in the runtime. - assert_eq!( - Balances::free_balance(&1), - (10000 - ::MaximumBlockWeight::get()) as u64 - ); - } - ); + }); } #[test] fn burn_must_work() { - with_externalities( - &mut ExtBuilder::default() - .monied(true) - .build(), - || { - 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); - } - ); + 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); + }); } diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs index 4a157569c0ffec890a9f4985a69dfa5bd01adfe6..37c1482f9ba15e785a1e784bb7f7aed53142a208 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -25,6 +25,7 @@ use rstd::{prelude::*, result}; use primitives::u32_trait::Value as U32; +use sr_primitives::RuntimeDebug; use sr_primitives::traits::{Hash, EnsureOrigin}; use sr_primitives::weights::SimpleDispatchInfo; use support::{ @@ -55,8 +56,7 @@ pub trait Trait: system::Trait { } /// Origin for the collective module. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub enum RawOrigin { /// It has been condoned by a given number of members of the collective from a given total. Members(MemberCount, MemberCount), @@ -69,8 +69,7 @@ pub enum RawOrigin { /// Origin for the collective module. pub type Origin = RawOrigin<::AccountId, I>; -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] /// Info for keeping track of a motion being voted on. pub struct Votes { /// The proposal's unique index. @@ -86,15 +85,15 @@ pub struct Votes { decl_storage! { trait Store for Module, I: Instance=DefaultInstance> as Collective { /// The hashes of the active proposals. - pub Proposals get(proposals): Vec; + pub Proposals get(fn proposals): Vec; /// Actual proposal for a given hash, if it's current. - pub ProposalOf get(proposal_of): map T::Hash => Option<>::Proposal>; + pub ProposalOf get(fn proposal_of): map T::Hash => Option<>::Proposal>; /// Votes on a given proposal, if it is ongoing. - pub Voting get(voting): map T::Hash => Option>; + pub Voting get(fn voting): map T::Hash => Option>; /// Proposals so far. - pub ProposalCount get(proposal_count): u32; + pub ProposalCount get(fn proposal_count): u32; /// The current members of the collective. This is stored sorted (just by value). - pub Members get(members): Vec; + pub Members get(fn members): Vec; } add_extra_genesis { config(phantom): rstd::marker::PhantomData; @@ -382,10 +381,10 @@ mod tests { use support::{Hashable, assert_ok, assert_noop, parameter_types}; use system::{EventRecord, Phase}; use hex_literal::hex; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; + use primitives::H256; use sr_primitives::{ - Perbill, traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, testing::Header, BuildStorage + Perbill, traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, testing::Header, + BuildStorage, }; use crate as collective; @@ -406,7 +405,6 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; @@ -439,7 +437,7 @@ mod tests { } ); - fn make_ext() -> runtime_io::TestExternalities { + fn make_ext() -> runtime_io::TestExternalities { GenesisConfig { collective_Instance1: Some(collective::GenesisConfig { members: vec![1, 2, 3], @@ -451,7 +449,7 @@ mod tests { #[test] fn motions_basic_environment_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); assert_eq!(Collective::members(), vec![1, 2, 3]); assert_eq!(Collective::proposals(), Vec::::new()); @@ -464,7 +462,7 @@ mod tests { #[test] fn removal_of_old_voters_votes_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash = BlakeTwo256::hash_of(&proposal); @@ -498,7 +496,7 @@ mod tests { #[test] fn removal_of_old_voters_votes_works_with_set_members() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash = BlakeTwo256::hash_of(&proposal); @@ -532,7 +530,7 @@ mod tests { #[test] fn propose_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash = proposal.blake2_256().into(); @@ -561,7 +559,7 @@ mod tests { #[test] fn motions_ignoring_non_collective_proposals_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); assert_noop!( @@ -573,29 +571,35 @@ mod tests { #[test] fn motions_ignoring_non_collective_votes_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash: H256 = proposal.blake2_256().into(); assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_noop!(Collective::vote(Origin::signed(42), hash.clone(), 0, true), "voter not a member"); + assert_noop!( + Collective::vote(Origin::signed(42), hash.clone(), 0, true), + "voter not a member", + ); }); } #[test] fn motions_ignoring_bad_index_collective_vote_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(3); let proposal = make_proposal(42); let hash: H256 = proposal.blake2_256().into(); assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_noop!(Collective::vote(Origin::signed(2), hash.clone(), 1, true), "mismatched index"); + assert_noop!( + Collective::vote(Origin::signed(2), hash.clone(), 1, true), + "mismatched index", + ); }); } #[test] fn motions_revoting_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash: H256 = proposal.blake2_256().into(); @@ -604,13 +608,19 @@ mod tests { Collective::voting(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![1], nays: vec![] }) ); - assert_noop!(Collective::vote(Origin::signed(1), hash.clone(), 0, true), "duplicate vote ignored"); + assert_noop!( + Collective::vote(Origin::signed(1), hash.clone(), 0, true), + "duplicate vote ignored", + ); assert_ok!(Collective::vote(Origin::signed(1), hash.clone(), 0, false)); assert_eq!( Collective::voting(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![1] }) ); - assert_noop!(Collective::vote(Origin::signed(1), hash.clone(), 0, false), "duplicate vote ignored"); + assert_noop!( + Collective::vote(Origin::signed(1), hash.clone(), 0, false), + "duplicate vote ignored", + ); assert_eq!(System::events(), vec![ EventRecord { @@ -640,7 +650,7 @@ mod tests { #[test] fn motions_disapproval_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash: H256 = proposal.blake2_256().into(); @@ -683,7 +693,7 @@ mod tests { #[test] fn motions_approval_works() { - with_externalities(&mut make_ext(), || { + make_ext().execute_with(|| { System::set_block_number(1); let proposal = make_proposal(42); let hash: H256 = proposal.blake2_256().into(); diff --git a/srml/contracts/Cargo.toml b/srml/contracts/Cargo.toml index f076f5fedae1ded702d9eeb593d053b751c5de3d..365566cfb547e23324891402cfb106116d526d22 100644 --- a/srml/contracts/Cargo.toml +++ b/srml/contracts/Cargo.toml @@ -25,6 +25,7 @@ hex-literal = "0.2.1" balances = { package = "srml-balances", path = "../balances" } hex = "0.3.2" timestamp = { package = "srml-timestamp", path = "../timestamp" } +randomness-collective-flip = { package = "srml-randomness-collective-flip", path = "../randomness-collective-flip" } [features] default = ["std"] diff --git a/srml/contracts/rpc/Cargo.toml b/srml/contracts/rpc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..90bf34bec1fd9fefe3768cd849af835d1e5b45d4 --- /dev/null +++ b/srml/contracts/rpc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "srml-contracts-rpc" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +client = { package = "substrate-client", path = "../../../core/client" } +codec = { package = "parity-scale-codec", version = "1.0.0" } +jsonrpc-core = "13.2.0" +jsonrpc-core-client = "13.2.0" +jsonrpc-derive = "13.2.0" +primitives = { package = "substrate-primitives", path = "../../../core/primitives" } +rpc-primitives = { package = "substrate-rpc-primitives", path = "../../../core/rpc/primitives" } +serde = { version = "1.0.101", features = ["derive"] } +sr-primitives = { path = "../../../core/sr-primitives" } +srml-contracts-rpc-runtime-api = { path = "./runtime-api" } diff --git a/srml/contracts/rpc/runtime-api/Cargo.toml b/srml/contracts/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..2a36ed2c96cc451e622b4c58418f0bb28bf48287 --- /dev/null +++ b/srml/contracts/rpc/runtime-api/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "srml-contracts-rpc-runtime-api" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +client = { package = "substrate-client", path = "../../../../core/client", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../../../../core/sr-std", default-features = false } +serde = { version = "1.0.101", optional = true, features = ["derive"] } +sr-primitives = { path = "../../../../core/sr-primitives", default-features = false } + +[features] +default = ["std"] +std = [ + "client/std", + "codec/std", + "rstd/std", + "serde", + "sr-primitives/std", +] diff --git a/srml/contracts/rpc/runtime-api/src/lib.rs b/srml/contracts/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..054f110beb4d0f202bf7d963a8d97615a2ebabc5 --- /dev/null +++ b/srml/contracts/rpc/runtime-api/src/lib.rs @@ -0,0 +1,90 @@ +// 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 . + +//! Runtime API definition required by Contracts RPC extensions. +//! +//! This API should be imported and implemented by the runtime, +//! of a node that wants to use the custom RPC extension +//! adding Contracts access methods. + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::vec::Vec; +use codec::{Encode, Decode, Codec}; +use sr_primitives::RuntimeDebug; + +/// A result of execution of a contract. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum ContractExecResult { + /// The contract returned successfully. + /// + /// There is a status code and, optionally, some data returned by the contract. + Success { + /// Status code returned by the contract. + status: u8, + /// Output data returned by the contract. + /// + /// Can be empty. + data: Vec, + }, + /// The contract execution either trapped or returned an error. + 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, +} + +client::decl_runtime_apis! { + /// The API to interact with contracts without using executive. + pub trait ContractsApi where + AccountId: Codec, + Balance: Codec, + { + /// Perform a call from a specified account to a given contract. + /// + /// See the contracts' `call` dispatchable function for more details. + fn call( + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: u64, + input_data: Vec, + ) -> ContractExecResult; + + /// Query a given storage key in a given contract. + /// + /// Returns `Ok(Some(Vec))` if the storage value exists under the given key in the + /// specified account and `Ok(None)` if it doesn't. If the account specified by the address + /// doesn't exist, or doesn't have a contract or if the contract is a tombstone, then `Err` + /// is returned. + fn get_storage( + address: AccountId, + key: [u8; 32], + ) -> GetStorageResult; + } +} diff --git a/srml/contracts/rpc/src/lib.rs b/srml/contracts/rpc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..91783df99617eebf3d3ae41cb77d4664ac23d846 --- /dev/null +++ b/srml/contracts/rpc/src/lib.rs @@ -0,0 +1,187 @@ +// 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 . + +//! Node-specific RPC methods for interaction with contracts. + +use std::sync::Arc; + +use client::blockchain::HeaderBackend; +use codec::Codec; +use jsonrpc_core::{Error, ErrorCode, Result}; +use jsonrpc_derive::rpc; +use primitives::{H256, Bytes}; +use rpc_primitives::number; +use serde::{Deserialize, Serialize}; +use sr_primitives::{ + generic::BlockId, + traits::{Block as BlockT, ProvideRuntimeApi}, +}; + +pub use self::gen_client::Client as ContractsClient; +pub use srml_contracts_rpc_runtime_api::{ + self as runtime_api, ContractExecResult, ContractsApi as ContractsRuntimeApi, GetStorageResult, +}; + +const RUNTIME_ERROR: i64 = 1; +const CONTRACT_DOESNT_EXIST: i64 = 2; +const CONTRACT_IS_A_TOMBSTONE: i64 = 3; + +// 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::*; + match e.0 { + ContractDoesntExist => Error { + code: ErrorCode::ServerError(CONTRACT_DOESNT_EXIST), + message: "The specified contract doesn't exist.".into(), + data: None, + }, + IsTombstone => Error { + code: ErrorCode::ServerError(CONTRACT_IS_A_TOMBSTONE), + message: "The contract is a tombstone and doesn't have any storage.".into(), + data: None, + } + } + } +} + +/// A struct that encodes RPC parameters required for a call to a smart-contract. +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct CallRequest { + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: number::NumberOrHex, + input_data: Bytes, +} + +/// Contracts RPC methods. +#[rpc] +pub trait ContractsApi { + /// Executes a call to a contract. + /// + /// This call is performed locally without submitting any transactions. Thus executing this + /// won't change any state. Nonetheless, the calling state-changing contracts is still possible. + /// + /// This method is useful for calling getter-like methods on contracts. + #[rpc(name = "contracts_call")] + fn call( + &self, + call_request: CallRequest, + at: Option, + ) -> Result; + + /// Returns the value under a specified storage `key` in a contract given by `address` param, + /// or `None` if it is not set. + #[rpc(name = "contracts_getStorage")] + fn get_storage( + &self, + address: AccountId, + key: H256, + at: Option, + ) -> Result>; +} + +/// An implementation of contract specific RPC methods. +pub struct Contracts { + client: Arc, + _marker: std::marker::PhantomData, +} + +impl Contracts { + /// Create new `Contracts` with the given reference to the client. + pub fn new(client: Arc) -> Self { + Contracts { + client, + _marker: Default::default(), + } + } +} + +impl ContractsApi<::Hash, AccountId, Balance> + for Contracts +where + Block: BlockT, + C: Send + Sync + 'static, + C: ProvideRuntimeApi, + C: HeaderBackend, + C::Api: ContractsRuntimeApi, + AccountId: Codec, + Balance: Codec, +{ + fn call( + &self, + call_request: CallRequest, + at: Option<::Hash>, + ) -> 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. + self.client.info().best_hash)); + + let CallRequest { + origin, + dest, + value, + gas_limit, + input_data, + } = call_request; + let gas_limit = gas_limit.to_number().map_err(|e| Error { + code: ErrorCode::InvalidParams, + message: e, + data: None, + })?; + + 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()), + })?; + + Ok(exec_result) + } + + fn get_storage( + &self, + address: AccountId, + key: H256, + at: Option<::Hash>, + ) -> 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. + self.client.info().best_hash)); + + let get_storage_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(Bytes); + + Ok(get_storage_result) + } +} diff --git a/srml/contracts/src/exec.rs b/srml/contracts/src/exec.rs index 94343c5fb93c2b25c8b68084abd7575195a583c9..08fd8999a4780d56b95ab721031da82558a00abc 100644 --- a/srml/contracts/src/exec.rs +++ b/srml/contracts/src/exec.rs @@ -22,13 +22,14 @@ use crate::rent; use rstd::prelude::*; use sr_primitives::traits::{Bounded, CheckedAdd, CheckedSub, Zero}; -use support::traits::{WithdrawReason, Currency, Time}; +use support::traits::{WithdrawReason, Currency, Time, Randomness}; pub type AccountIdOf = ::AccountId; pub type CallOf = ::Call; pub type MomentOf = <::Time as Time>::Moment; pub type SeedOf = ::Hash; pub type BlockNumberOf = ::BlockNumber; +pub type StorageKey = [u8; 32]; /// A type that represents a topic of an event. At the moment a hash is used. pub type TopicOf = ::Hash; @@ -60,7 +61,7 @@ impl ExecReturnValue { /// VM-specific errors during execution (eg. division by 0, OOB access, failure to satisfy some /// precondition of a system call, etc.) or errors with the orchestration (eg. out-of-gas errors, a /// non-existent destination contract, etc.). -#[cfg_attr(test, derive(Debug))] +#[cfg_attr(test, derive(sr_primitives::RuntimeDebug))] pub struct ExecError { pub reason: &'static str, /// This is an allocated buffer that may be reused. The buffer must be cleared explicitly @@ -84,8 +85,6 @@ macro_rules! try_or_exec_error { } } -pub type StorageKey = [u8; 32]; - /// An interface that provides access to the external environment in which the /// smart-contract is executed. /// @@ -231,7 +230,8 @@ impl Token for ExecFeeToken { } } -#[cfg_attr(any(feature = "std", test), derive(Debug, PartialEq, Eq, Clone))] +#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq, Clone))] +#[derive(sr_primitives::RuntimeDebug)] pub enum DeferredAction { DepositEvent { /// A list of topics this event will be deposited with. @@ -641,7 +641,7 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( if would_create && value < ctx.config.existential_deposit { return Err("value too low to create account"); } - T::Currency::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; + T::Currency::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer.into(), new_from_balance)?; let new_to_balance = match to_balance.checked_add(&value) { Some(b) => b, @@ -753,7 +753,7 @@ where } fn random(&self, subject: &[u8]) -> SeedOf { - system::Module::::random(subject) + T::Randomness::random(subject) } fn now(&self) -> &MomentOf { @@ -803,16 +803,11 @@ mod tests { BalanceOf, ExecFeeToken, ExecutionContext, Ext, Loader, TransferFeeKind, TransferFeeToken, Vm, ExecResult, RawEvent, DeferredAction, }; - use crate::account_db::AccountDb; - use crate::exec::{ExecReturnValue, ExecError, STATUS_SUCCESS}; - use crate::gas::GasMeter; - use crate::tests::{ExtBuilder, Test}; - use crate::{CodeHash, Config}; - use runtime_io::with_externalities; - use std::cell::RefCell; - use std::rc::Rc; - use std::collections::HashMap; - use std::marker::PhantomData; + use crate::{ + account_db::AccountDb, gas::GasMeter, tests::{ExtBuilder, Test}, + exec::{ExecReturnValue, ExecError, STATUS_SUCCESS}, CodeHash, Config, + }; + use std::{cell::RefCell, rc::Rc, collections::HashMap, marker::PhantomData}; use assert_matches::assert_matches; const ALICE: u64 = 1; @@ -937,7 +932,7 @@ mod tests { exec_success() }); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, exec_ch).unwrap(); @@ -957,7 +952,7 @@ mod tests { let dest = BOB; // This test verifies that base fee for call is taken. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let vm = MockVm::new(); let loader = MockLoader::empty(); let cfg = Config::preload(); @@ -975,7 +970,7 @@ mod tests { }); // This test verifies that base fee for instantiation is taken. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let mut loader = MockLoader::empty(); let code = loader.insert(|_| exec_success()); @@ -1005,7 +1000,7 @@ mod tests { let vm = MockVm::new(); let loader = MockLoader::empty(); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); ctx.overlay.set_balance(&origin, 100); @@ -1037,7 +1032,7 @@ mod tests { |_| Ok(ExecReturnValue { status: 1, data: Vec::new() }) ); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap(); @@ -1065,93 +1060,84 @@ mod tests { // This test sends 50 units of currency to a non-existent account. // This should lead to creation of a new account thus // a fee should be charged. - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let vm = MockVm::new(); - let loader = MockLoader::empty(); - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 0); - - let mut gas_meter = GasMeter::::with_limit(1000, 1); - - let result = ctx.call(dest, 50, &mut gas_meter, vec![]); - assert_matches!(result, Ok(_)); - - let mut toks = gas_meter.tokens().iter(); - match_tokens!( - toks, - ExecFeeToken::Call, - TransferFeeToken { - kind: TransferFeeKind::AccountCreate, - gas_price: 1u64 - }, - ); - }, - ); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let vm = MockVm::new(); + let loader = MockLoader::empty(); + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); + ctx.overlay.set_balance(&origin, 100); + ctx.overlay.set_balance(&dest, 0); + + let mut gas_meter = GasMeter::::with_limit(1000, 1); + + let result = ctx.call(dest, 50, &mut gas_meter, vec![]); + assert_matches!(result, Ok(_)); + + let mut toks = gas_meter.tokens().iter(); + match_tokens!( + toks, + ExecFeeToken::Call, + TransferFeeToken { + kind: TransferFeeKind::AccountCreate, + gas_price: 1u64 + }, + ); + }); // This one is similar to the previous one but transfer to an existing account. // In this test we expect that a regular transfer fee is charged. - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let vm = MockVm::new(); - let loader = MockLoader::empty(); - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 15); - - let mut gas_meter = GasMeter::::with_limit(1000, 1); - - let result = ctx.call(dest, 50, &mut gas_meter, vec![]); - assert_matches!(result, Ok(_)); - - let mut toks = gas_meter.tokens().iter(); - match_tokens!( - toks, - ExecFeeToken::Call, - TransferFeeToken { - kind: TransferFeeKind::Transfer, - gas_price: 1u64 - }, - ); - }, - ); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let vm = MockVm::new(); + let loader = MockLoader::empty(); + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); + ctx.overlay.set_balance(&origin, 100); + ctx.overlay.set_balance(&dest, 15); + + let mut gas_meter = GasMeter::::with_limit(1000, 1); + + let result = ctx.call(dest, 50, &mut gas_meter, vec![]); + assert_matches!(result, Ok(_)); + + let mut toks = gas_meter.tokens().iter(); + match_tokens!( + toks, + ExecFeeToken::Call, + TransferFeeToken { + kind: TransferFeeKind::Transfer, + gas_price: 1u64 + }, + ); + }); // This test sends 50 units of currency as an endownment to a newly // instantiated contract. - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let mut loader = MockLoader::empty(); - let code = loader.insert(|_| exec_success()); - - let vm = MockVm::new(); - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 15); - - let mut gas_meter = GasMeter::::with_limit(1000, 1); - - let result = ctx.instantiate(50, &mut gas_meter, &code, vec![]); - assert_matches!(result, Ok(_)); - - let mut toks = gas_meter.tokens().iter(); - match_tokens!( - toks, - ExecFeeToken::Instantiate, - TransferFeeToken { - kind: TransferFeeKind::ContractInstantiate, - gas_price: 1u64 - }, - ); - }, - ); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let mut loader = MockLoader::empty(); + let code = loader.insert(|_| exec_success()); + + let vm = MockVm::new(); + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); + + ctx.overlay.set_balance(&origin, 100); + ctx.overlay.set_balance(&dest, 15); + + let mut gas_meter = GasMeter::::with_limit(1000, 1); + + let result = ctx.instantiate(50, &mut gas_meter, &code, vec![]); + assert_matches!(result, Ok(_)); + + let mut toks = gas_meter.tokens().iter(); + match_tokens!( + toks, + ExecFeeToken::Instantiate, + TransferFeeToken { + kind: TransferFeeKind::ContractInstantiate, + gas_price: 1u64 + }, + ); + }); } #[test] @@ -1164,7 +1150,7 @@ mod tests { let vm = MockVm::new(); let loader = MockLoader::empty(); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); ctx.overlay.set_balance(&origin, 0); @@ -1198,7 +1184,7 @@ mod tests { |_| Ok(ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] }) ); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap(); @@ -1229,7 +1215,7 @@ mod tests { |_| Ok(ExecReturnValue { status: 1, data: vec![1, 2, 3, 4] }) ); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap(); @@ -1257,7 +1243,7 @@ mod tests { }); // This one tests passing the input data into a contract via call. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, input_data_ch).unwrap(); @@ -1282,7 +1268,7 @@ mod tests { }); // This one tests passing the input data into a contract via instantiate. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); @@ -1326,7 +1312,7 @@ mod tests { exec_success() }); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, recurse_ch).unwrap(); @@ -1370,7 +1356,7 @@ mod tests { exec_success() }); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); @@ -1412,7 +1398,7 @@ mod tests { exec_success() }); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); ctx.overlay.instantiate_contract(&BOB, bob_ch).unwrap(); @@ -1436,23 +1422,20 @@ mod tests { let mut loader = MockLoader::empty(); let dummy_ch = loader.insert(|_| exec_success()); - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - assert_matches!( - ctx.instantiate( - 0, // <- zero endowment - &mut GasMeter::::with_limit(10000, 1), - &dummy_ch, - vec![], - ), - Err(_) - ); - } - ); + assert_matches!( + ctx.instantiate( + 0, // <- zero endowment + &mut GasMeter::::with_limit(10000, 1), + &dummy_ch, + vec![], + ), + Err(_) + ); + }); } #[test] @@ -1464,38 +1447,35 @@ mod tests { |_| Ok(ExecReturnValue { status: STATUS_SUCCESS, data: vec![80, 65, 83, 83] }) ); - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); - - let instantiated_contract_address = assert_matches!( - ctx.instantiate( - 100, - &mut GasMeter::::with_limit(10000, 1), - &dummy_ch, - vec![], - ), - Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address - ); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&ALICE, 1000); - // Check that the newly created account has the expected code hash and - // there are instantiation event. - assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch); - assert_eq!(&ctx.events(), &[ - DeferredAction::DepositEvent { - event: RawEvent::Transfer(ALICE, instantiated_contract_address, 100), - topics: Vec::new(), - }, - DeferredAction::DepositEvent { - event: RawEvent::Instantiated(ALICE, instantiated_contract_address), - topics: Vec::new(), - } - ]); - } - ); + let instantiated_contract_address = assert_matches!( + ctx.instantiate( + 100, + &mut GasMeter::::with_limit(10000, 1), + &dummy_ch, + vec![], + ), + Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address + ); + + // Check that the newly created account has the expected code hash and + // there are instantiation event. + assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch); + assert_eq!(&ctx.events(), &[ + DeferredAction::DepositEvent { + event: RawEvent::Transfer(ALICE, instantiated_contract_address, 100), + topics: Vec::new(), + }, + DeferredAction::DepositEvent { + event: RawEvent::Instantiated(ALICE, instantiated_contract_address), + topics: Vec::new(), + } + ]); + }); } #[test] @@ -1507,28 +1487,25 @@ mod tests { |_| Ok(ExecReturnValue { status: 1, data: vec![70, 65, 73, 76] }) ); - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); - - let instantiated_contract_address = assert_matches!( - ctx.instantiate( - 100, - &mut GasMeter::::with_limit(10000, 1), - &dummy_ch, - vec![], - ), - Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address - ); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&ALICE, 1000); - // Check that the account has not been created. - assert!(ctx.overlay.get_code_hash(&instantiated_contract_address).is_none()); - assert!(ctx.events().is_empty()); - } - ); + let instantiated_contract_address = assert_matches!( + ctx.instantiate( + 100, + &mut GasMeter::::with_limit(10000, 1), + &dummy_ch, + vec![], + ), + Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address + ); + + // Check that the account has not been created. + assert!(ctx.overlay.get_code_hash(&instantiated_contract_address).is_none()); + assert!(ctx.events().is_empty()); + }); } #[test] @@ -1555,40 +1532,37 @@ mod tests { } }); - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); - ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap(); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&ALICE, 1000); + ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap(); - assert_matches!( - ctx.call(BOB, 20, &mut GasMeter::::with_limit(1000, 1), vec![]), - Ok(_) - ); + assert_matches!( + ctx.call(BOB, 20, &mut GasMeter::::with_limit(1000, 1), vec![]), + Ok(_) + ); - let instantiated_contract_address = instantiated_contract_address.borrow().as_ref().unwrap().clone(); - - // Check that the newly created account has the expected code hash and - // there are instantiation event. - assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch); - assert_eq!(&ctx.events(), &[ - DeferredAction::DepositEvent { - event: RawEvent::Transfer(ALICE, BOB, 20), - topics: Vec::new(), - }, - DeferredAction::DepositEvent { - event: RawEvent::Transfer(BOB, instantiated_contract_address, 15), - topics: Vec::new(), - }, - DeferredAction::DepositEvent { - event: RawEvent::Instantiated(BOB, instantiated_contract_address), - topics: Vec::new(), - }, - ]); - } - ); + let instantiated_contract_address = instantiated_contract_address.borrow().as_ref().unwrap().clone(); + + // Check that the newly created account has the expected code hash and + // there are instantiation event. + assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch); + assert_eq!(&ctx.events(), &[ + DeferredAction::DepositEvent { + event: RawEvent::Transfer(ALICE, BOB, 20), + topics: Vec::new(), + }, + DeferredAction::DepositEvent { + event: RawEvent::Transfer(BOB, instantiated_contract_address, 15), + topics: Vec::new(), + }, + DeferredAction::DepositEvent { + event: RawEvent::Instantiated(BOB, instantiated_contract_address), + topics: Vec::new(), + }, + ]); + }); } #[test] @@ -1617,29 +1591,26 @@ mod tests { } }); - with_externalities( - &mut ExtBuilder::default().existential_deposit(15).build(), - || { - let cfg = Config::preload(); - let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); - ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap(); + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let cfg = Config::preload(); + let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); + ctx.overlay.set_balance(&ALICE, 1000); + ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap(); - assert_matches!( - ctx.call(BOB, 20, &mut GasMeter::::with_limit(1000, 1), vec![]), - Ok(_) - ); + assert_matches!( + ctx.call(BOB, 20, &mut GasMeter::::with_limit(1000, 1), vec![]), + Ok(_) + ); - // The contract wasn't instantiated so we don't expect to see an instantiation - // event here. - assert_eq!(&ctx.events(), &[ - DeferredAction::DepositEvent { - event: RawEvent::Transfer(ALICE, BOB, 20), - topics: Vec::new(), - }, - ]); - } - ); + // The contract wasn't instantiated so we don't expect to see an instantiation + // event here. + assert_eq!(&ctx.events(), &[ + DeferredAction::DepositEvent { + event: RawEvent::Transfer(ALICE, BOB, 20), + topics: Vec::new(), + }, + ]); + }); } #[test] @@ -1653,7 +1624,7 @@ mod tests { exec_success() }); - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); diff --git a/srml/contracts/src/gas.rs b/srml/contracts/src/gas.rs index 08070916fbf293d439783fae9e5f30287d1e0ddb..64997fbe81fdf6c697b752dbe62afa11c64a6726 100644 --- a/srml/contracts/src/gas.rs +++ b/srml/contracts/src/gas.rs @@ -215,7 +215,7 @@ pub fn buy_gas( let imbalance = T::Currency::withdraw( transactor, cost, - WithdrawReason::Fee, + WithdrawReason::Fee.into(), ExistenceRequirement::KeepAlive )?; diff --git a/srml/contracts/src/lib.rs b/srml/contracts/src/lib.rs index 918d0838c47154678a1c689affff5937ab46c389..df38747cc5d521aa3048fc50314e3f00cd92ccde 100644 --- a/srml/contracts/src/lib.rs +++ b/srml/contracts/src/lib.rs @@ -109,22 +109,23 @@ pub use crate::exec::{ExecResult, ExecReturnValue, ExecError, StatusCode}; #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; use primitives::crypto::UncheckedFrom; -use rstd::{prelude::*, marker::PhantomData}; +use rstd::{prelude::*, marker::PhantomData, fmt::Debug}; use codec::{Codec, Encode, Decode}; use runtime_io::blake2_256; use sr_primitives::{ - traits::{Hash, StaticLookup, Zero, MaybeSerializeDebug, Member, SignedExtension}, + traits::{Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, SignedExtension}, weights::DispatchInfo, transaction_validity::{ ValidTransaction, InvalidTransaction, TransactionValidity, TransactionValidityError, }, + RuntimeDebug, }; use support::dispatch::{Result, Dispatchable}; use support::{ Parameter, decl_module, decl_event, decl_storage, storage::child, - parameter_types, + parameter_types, IsSubType }; -use support::{traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get, Time}, IsSubType}; +use support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get, Time, Randomness}; use system::{ensure_signed, RawOrigin, ensure_root}; use primitives::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; @@ -143,8 +144,7 @@ pub trait ComputeDispatchFee { /// Information for managing an acocunt and its sub trie abstraction. /// This is the required info to cache for an account -#[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, RuntimeDebug)] pub enum ContractInfo { Alive(AliveContractInfo), Tombstone(TombstoneContractInfo), @@ -207,9 +207,7 @@ pub type AliveContractInfo = /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct RawAliveContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, @@ -228,15 +226,14 @@ pub struct RawAliveContractInfo { pub type TombstoneContractInfo = RawTombstoneContractInfo<::Hash, ::Hashing>; -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Encode, Decode, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub struct RawTombstoneContractInfo(H, PhantomData); impl RawTombstoneContractInfo where - H: Member + MaybeSerializeDebug + AsRef<[u8]> + AsMut<[u8]> + Copy + Default + rstd::hash::Hash - + Codec, + H: Member + MaybeSerializeDeserialize+ Debug + + AsRef<[u8]> + AsMut<[u8]> + Copy + Default + + rstd::hash::Hash + Codec, Hasher: Hash, { fn new(storage_root: &[u8], code_hash: H) -> Self { @@ -325,7 +322,7 @@ parameter_types! { /// A reasonable default value for [`Trait::InstantiateBaseFee`]. pub const DefaultInstantiateBaseFee: u32 = 1000; /// A reasonable default value for [`Trait::MaxDepth`]. - pub const DefaultMaxDepth: u32 = 1024; + pub const DefaultMaxDepth: u32 = 32; /// A reasonable default value for [`Trait::MaxValueSize`]. pub const DefaultMaxValueSize: u32 = 16_384; /// A reasonable default value for [`Trait::BlockGasLimit`]. @@ -335,6 +332,7 @@ parameter_types! { pub trait Trait: system::Trait { type Currency: Currency; type Time: Time; + type Randomness: Randomness; /// The outer call dispatch type. type Call: Parameter + Dispatchable::Origin> + IsSubType, Self>; @@ -357,6 +355,9 @@ pub trait Trait: system::Trait { /// Handler for the unbalanced reduction when making a gas payment. type GasPayment: OnUnbalanced>; + /// Handler for rent payments. + type RentPayment: OnUnbalanced>; + /// Number of block delay an extrinsic claim surcharge has. /// /// When claim surcharge is called by an extrinsic the rent is checked @@ -441,7 +442,7 @@ where } /// The default dispatch fee computor computes the fee in the same way that -/// the implementation of `TakeFees` for the Balances module does. Note that this only takes a fixed +/// the implementation of `ChargeTransactionPayment` for the Balances module does. Note that this only takes a fixed /// fee based on size. Unlike the balances module, weight-fee is applied. pub struct DefaultDispatchFeeComputor(PhantomData); impl ComputeDispatchFee<::Call, BalanceOf> for DefaultDispatchFeeComputor { @@ -649,10 +650,19 @@ 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. /// - /// This function is similar to `Self::call`, but doesn't perform any lookups and better + /// This function is similar to `Self::call`, but doesn't perform any address lookups and better /// suitable for calling directly from Rust. pub fn bare_call( origin: T::AccountId, @@ -666,6 +676,27 @@ impl Module { }) } + /// Query storage of a specified contract under a specified key. + pub fn get_storage( + address: T::AccountId, + key: [u8; 32], + ) -> rstd::result::Result>, GetStorageError> { + let contract_info = >::get(&address) + .ok_or(GetStorageError::ContractDoesntExist)? + .get_alive() + .ok_or(GetStorageError::IsTombstone)?; + + let maybe_value = AccountDb::::get_storage( + &DirectAccountDb, + &address, + Some(&contract_info.trie_id), + &key, + ); + Ok(maybe_value) + } +} + +impl Module { fn execute_wasm( origin: T::AccountId, gas_limit: Gas, @@ -837,9 +868,9 @@ decl_event! { decl_storage! { trait Store for Module as Contract { /// Gas spent so far in this block. - GasSpent get(gas_spent): Gas; + GasSpent get(fn gas_spent): Gas; /// Current cost schedule for contracts. - CurrentSchedule get(current_schedule) config(): Schedule = Schedule::default(); + 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>; /// A mapping between an original code hash and instrumented wasm code, ready for execution. @@ -849,7 +880,7 @@ decl_storage! { /// The code associated with a given account. pub ContractInfoOf: map T::AccountId => Option>; /// The price of one unit of gas. - GasPrice get(gas_price) config(): BalanceOf = 1.into(); + GasPrice get(fn gas_price) config(): BalanceOf = 1.into(); } } @@ -890,8 +921,8 @@ impl Config { } /// Definition of the cost schedule and other parameterizations for wasm vm. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub struct Schedule { /// Version of the schedule. pub version: u32, @@ -987,9 +1018,14 @@ impl Default for CheckBlockGasLimit { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for CheckBlockGasLimit { - fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result { +impl rstd::fmt::Debug for CheckBlockGasLimit { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "CheckBlockGasLimit") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { Ok(()) } } diff --git a/srml/contracts/src/rent.rs b/srml/contracts/src/rent.rs index 776f804ee7cac094634c96636dceee01a6bc47c1..6647f8963165dd174bbf10a8d78c673515a1c1d0 100644 --- a/srml/contracts/src/rent.rs +++ b/srml/contracts/src/rent.rs @@ -17,7 +17,7 @@ use crate::{BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo, Trait, AliveContractInfo}; use sr_primitives::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero, SaturatedConversion}; -use support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason}; +use support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason, OnUnbalanced}; use support::StorageMap; #[derive(PartialEq, Eq, Copy, Clone)] @@ -119,17 +119,17 @@ fn try_evict_or_and_pay_rent( let can_withdraw_rent = T::Currency::ensure_can_withdraw( account, dues_limited, - WithdrawReason::Fee, + WithdrawReason::Fee.into(), balance.saturating_sub(dues_limited), ) .is_ok(); if can_withdraw_rent && (insufficient_rent || pay_rent) { // Collect dues. - let _ = T::Currency::withdraw( + let imbalance = T::Currency::withdraw( account, dues_limited, - WithdrawReason::Fee, + WithdrawReason::Fee.into(), ExistenceRequirement::KeepAlive, ) .expect( @@ -137,6 +137,8 @@ fn try_evict_or_and_pay_rent( dues_limited < rent_budget < balance - subsistence < balance - existential_deposit; qed", ); + + T::RentPayment::on_unbalanced(imbalance); } if insufficient_rent || !can_withdraw_rent { diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index f2ef8a275d66bc4263ab7102958a85bdb263a63a..7a13a66de26c7a7c41523bc85f725c885852f887 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -19,17 +19,14 @@ #![allow(unused)] -use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}; use crate::{ BalanceOf, ComputeDispatchFee, ContractAddressFor, ContractInfo, ContractInfoOf, GenesisConfig, Module, RawAliveContractInfo, RawEvent, Trait, TrieId, TrieIdFromParentCounter, Schedule, - TrieIdGenerator, CheckBlockGasLimit, + TrieIdGenerator, CheckBlockGasLimit, account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}, }; use assert_matches::assert_matches; use hex_literal::*; use codec::{Decode, Encode, KeyedVec}; -use runtime_io; -use runtime_io::with_externalities; use sr_primitives::{ Perbill, BuildStorage, transaction_validity::{InvalidTransaction, ValidTransaction}, traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension}, @@ -41,7 +38,7 @@ use support::{ storage::child, StorageMap, StorageValue, traits::{Currency, Get}, }; use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}}; -use primitives::{storage::well_known_keys, Blake2Hasher}; +use primitives::storage::well_known_keys; use system::{self, EventRecord, Phase}; mod contract { @@ -99,8 +96,6 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; - pub const BalancesTransactionBaseFee: u64 = 0; - pub const BalancesTransactionByteFee: u64 = 0; pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl system::Trait for Test { @@ -113,7 +108,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -126,15 +120,11 @@ impl balances::Trait for Test { type OnFreeBalanceZero = Contract; type OnNewAccount = (); type Event = MetaEvent; - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = BalancesTransactionBaseFee; - type TransactionByteFee = BalancesTransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const MinimumPeriod: u64 = 1; @@ -162,12 +152,14 @@ parameter_types! { impl Trait for Test { type Currency = Balances; type Time = Timestamp; + type Randomness = Randomness; type Call = Call; type DetermineContractAddress = DummyContractAddressFor; type Event = MetaEvent; type ComputeDispatchFee = DummyComputeDispatchFee; type TrieIdGenerator = DummyTrieIdGenerator; type GasPayment = (); + type RentPayment = (); type SignedClaimHandicap = SignedClaimHandicap; type TombstoneDeposit = TombstoneDeposit; type StorageSizeOffset = StorageSizeOffset; @@ -190,6 +182,7 @@ type Balances = balances::Module; type Timestamp = timestamp::Module; type Contract = Module; type System = system::Module; +type Randomness = randomness_collective_flip::Module; pub struct DummyContractAddressFor; impl ContractAddressFor for DummyContractAddressFor { @@ -275,7 +268,7 @@ impl ExtBuilder { INSTANTIATION_FEE.with(|v| *v.borrow_mut() = self.instantiation_fee); BLOCK_GAS_LIMIT.with(|v| *v.borrow_mut() = self.block_gas_limit); } - pub fn build(self) -> runtime_io::TestExternalities { + pub fn build(self) -> runtime_io::TestExternalities { self.set_associated_consts(); let mut t = system::GenesisConfig::default().build_storage::().unwrap(); balances::GenesisConfig:: { @@ -307,7 +300,7 @@ fn compile_module(wabt_module: &str) // Then we check that the all unused gas is refunded. #[test] fn refunds_unused_gas() { - with_externalities(&mut ExtBuilder::default().gas_price(2).build(), || { + 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())); @@ -319,70 +312,67 @@ fn refunds_unused_gas() { #[test] fn account_removal_removes_storage() { - with_externalities( - &mut ExtBuilder::default().existential_deposit(100).build(), - || { - let trie_id1 = ::TrieIdGenerator::trie_id(&1); - let trie_id2 = ::TrieIdGenerator::trie_id(&2); - let key1 = &[1; 32]; - let key2 = &[2; 32]; - - // Set up two accounts with free balance above the existential threshold. - { - Balances::deposit_creating(&1, 110); - ContractInfoOf::::insert(1, &ContractInfo::Alive(RawAliveContractInfo { - trie_id: trie_id1.clone(), - storage_size: ::StorageSizeOffset::get(), - deduct_block: System::block_number(), - code_hash: H256::repeat_byte(1), - rent_allowance: 40, - last_write: None, - })); - - let mut overlay = OverlayAccountDb::::new(&DirectAccountDb); - overlay.set_storage(&1, key1.clone(), Some(b"1".to_vec())); - overlay.set_storage(&1, key2.clone(), Some(b"2".to_vec())); - DirectAccountDb.commit(overlay.into_change_set()); - - Balances::deposit_creating(&2, 110); - ContractInfoOf::::insert(2, &ContractInfo::Alive(RawAliveContractInfo { - trie_id: trie_id2.clone(), - storage_size: ::StorageSizeOffset::get(), - deduct_block: System::block_number(), - code_hash: H256::repeat_byte(2), - rent_allowance: 40, - last_write: None, - })); - - let mut overlay = OverlayAccountDb::::new(&DirectAccountDb); - overlay.set_storage(&2, key1.clone(), Some(b"3".to_vec())); - overlay.set_storage(&2, key2.clone(), Some(b"4".to_vec())); - DirectAccountDb.commit(overlay.into_change_set()); - } + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let trie_id1 = ::TrieIdGenerator::trie_id(&1); + let trie_id2 = ::TrieIdGenerator::trie_id(&2); + let key1 = &[1; 32]; + let key2 = &[2; 32]; + + // Set up two accounts with free balance above the existential threshold. + { + Balances::deposit_creating(&1, 110); + ContractInfoOf::::insert(1, &ContractInfo::Alive(RawAliveContractInfo { + trie_id: trie_id1.clone(), + storage_size: ::StorageSizeOffset::get(), + deduct_block: System::block_number(), + code_hash: H256::repeat_byte(1), + rent_allowance: 40, + last_write: None, + })); + + let mut overlay = OverlayAccountDb::::new(&DirectAccountDb); + overlay.set_storage(&1, key1.clone(), Some(b"1".to_vec())); + overlay.set_storage(&1, key2.clone(), Some(b"2".to_vec())); + DirectAccountDb.commit(overlay.into_change_set()); + + Balances::deposit_creating(&2, 110); + ContractInfoOf::::insert(2, &ContractInfo::Alive(RawAliveContractInfo { + trie_id: trie_id2.clone(), + storage_size: ::StorageSizeOffset::get(), + deduct_block: System::block_number(), + code_hash: H256::repeat_byte(2), + rent_allowance: 40, + last_write: None, + })); + + let mut overlay = OverlayAccountDb::::new(&DirectAccountDb); + overlay.set_storage(&2, key1.clone(), Some(b"3".to_vec())); + overlay.set_storage(&2, key2.clone(), Some(b"4".to_vec())); + DirectAccountDb.commit(overlay.into_change_set()); + } - // 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 storage associated with this account. - assert_ok!(Balances::transfer(Origin::signed(1), 2, 20)); - - // Verify that all entries from account 1 is removed, while - // entries from account 2 is in place. - { - assert!(>::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key1).is_none()); - assert!(>::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key2).is_none()); - - assert_eq!( - >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key1), - Some(b"3".to_vec()) - ); - assert_eq!( - >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key2), - Some(b"4".to_vec()) - ); - } - }, - ); + // 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 storage associated with this account. + assert_ok!(Balances::transfer(Origin::signed(1), 2, 20)); + + // Verify that all entries from account 1 is removed, while + // entries from account 2 is in place. + { + assert!(>::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key1).is_none()); + assert!(>::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key2).is_none()); + + assert_eq!( + >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key1), + Some(b"3".to_vec()) + ); + assert_eq!( + >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key2), + Some(b"4".to_vec()) + ); + } + }); } const CODE_RETURN_FROM_START_FN: &str = r#" @@ -419,61 +409,58 @@ const CODE_RETURN_FROM_START_FN: &str = r#" fn instantiate_and_call_and_deposit_event() { let (wasm, code_hash) = compile_module::(CODE_RETURN_FROM_START_FN).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(100).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - - // Check at the end to get hash on error easily - let creation = Contract::instantiate( - Origin::signed(ALICE), - 100, - 100_000, - code_hash.into(), - vec![], - ); + 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)); + + // Check at the end to get hash on error easily + let creation = Contract::instantiate( + Origin::signed(ALICE), + 100, + 100_000, + code_hash.into(), + vec![], + ); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances( + balances::RawEvent::NewAccount(BOB, 100) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Contract(BOB, vec![1, 2, 3, 4])), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + topics: vec![], + } + ]); - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances( - balances::RawEvent::NewAccount(BOB, 100) - ), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Contract(BOB, vec![1, 2, 3, 4])), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), - topics: vec![], - } - ]); - - assert_ok!(creation); - assert!(ContractInfoOf::::exists(BOB)); - }, - ); + assert_ok!(creation); + assert!(ContractInfoOf::::exists(BOB)); + }); } const CODE_DISPATCH_CALL: &str = r#" @@ -502,98 +489,95 @@ fn dispatch_call() { let (wasm, code_hash) = compile_module::(CODE_DISPATCH_CALL).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - - assert_ok!(Contract::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(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - ]); - - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 100, - 100_000, - code_hash.into(), - vec![], - )); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::call( - Origin::signed(ALICE), - BOB, // newly created account - 0, - 100_000, - vec![], - )); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances( - balances::RawEvent::NewAccount(BOB, 100) - ), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), - topics: vec![], - }, - - // Dispatching the call. - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances( - balances::RawEvent::NewAccount(CHARLIE, 50) - ), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances( - balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0) - ), - topics: vec![], - }, - - // Event emited as a result of dispatch. - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)), - topics: vec![], - } - ]); - }, - ); + assert_ok!(Contract::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(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + ]); + + assert_ok!(Contract::instantiate( + Origin::signed(ALICE), + 100, + 100_000, + code_hash.into(), + vec![], + )); + + assert_ok!(Contract::call( + Origin::signed(ALICE), + BOB, // newly created account + 0, + 100_000, + vec![], + )); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances( + balances::RawEvent::NewAccount(BOB, 100) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + topics: vec![], + }, + + // Dispatching the call. + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances( + balances::RawEvent::NewAccount(CHARLIE, 50) + ), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances( + balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0) + ), + topics: vec![], + }, + + // Event emited as a result of dispatch. + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)), + topics: vec![], + } + ]); + }); } const CODE_DISPATCH_CALL_THEN_TRAP: &str = r#" @@ -623,80 +607,77 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { let (wasm, code_hash) = compile_module::(CODE_DISPATCH_CALL_THEN_TRAP).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - - assert_ok!(Contract::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(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - ]); - - assert_ok!(Contract::instantiate( + 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)); + + // 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(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + ]); + + assert_ok!(Contract::instantiate( + Origin::signed(ALICE), + 100, + 100_000, + code_hash.into(), + vec![], + )); + + // Call the newly instantiated contract. The contract is expected to dispatch a call + // and then trap. + assert_err!( + Contract::call( Origin::signed(ALICE), - 100, + BOB, // newly created account + 0, 100_000, - code_hash.into(), vec![], - )); - - // Call the newly instantiated contract. The contract is expected to dispatch a call - // and then trap. - assert_err!( - Contract::call( - Origin::signed(ALICE), - BOB, // newly created account - 0, - 100_000, - vec![], + ), + "during execution" + ); + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances( + balances::RawEvent::NewAccount(BOB, 100) ), - "during execution" - ); - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances( - balances::RawEvent::NewAccount(BOB, 100) - ), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), - topics: vec![], - }, - // ABSENCE of events which would be caused by dispatched Balances::transfer call - ]); - }, - ); + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + topics: vec![], + }, + // ABSENCE of events which would be caused by dispatched Balances::transfer call + ]); + }); } const CODE_SET_RENT: &str = r#" @@ -826,28 +807,25 @@ fn test_set_rent_code_and_hash() { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::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(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), - topics: vec![], - }, - ]); - } - ); + 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)); + + // 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(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + ]); + }); } #[test] @@ -855,92 +833,86 @@ fn storage_size() { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); // Storage size - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 30_000, - 100_000, code_hash.into(), - ::Balance::from(1_000u32).encode() // rent allowance - )); - 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())); - 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())); - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); - } - ); + 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( + Origin::signed(ALICE), + 30_000, + 100_000, code_hash.into(), + ::Balance::from(1_000u32).encode() // rent allowance + )); + 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())); + 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())); + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); + }); } #[test] fn deduct_blocks() { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 30_000, - 100_000, code_hash.into(), - ::Balance::from(1_000u32).encode() // rent allowance - )); - - // Check creation - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000); - - // Advance 4 blocks - System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent through call - assert_ok!(Contract::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 - * 4 // rent byte price - * 4; // blocks to rent - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent); - assert_eq!(bob_contract.deduct_block, 5); - assert_eq!(Balances::free_balance(BOB), 30_000 - rent); - - // Advance 7 blocks more - System::initialize(&12, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent through call - assert_ok!(Contract::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 - * 4 // rent byte price - * 7; // blocks to rent - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2); - assert_eq!(bob_contract.deduct_block, 12); - 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())); - - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2); - assert_eq!(bob_contract.deduct_block, 12); - assert_eq!(Balances::free_balance(BOB), 30_000 - rent - rent_2); - } - ); + 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( + Origin::signed(ALICE), + 30_000, + 100_000, code_hash.into(), + ::Balance::from(1_000u32).encode() // rent allowance + )); + + // Check creation + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, 1_000); + + // Advance 4 blocks + System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent through call + assert_ok!(Contract::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 + * 4 // rent byte price + * 4; // blocks to rent + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, 1_000 - rent); + assert_eq!(bob_contract.deduct_block, 5); + assert_eq!(Balances::free_balance(BOB), 30_000 - rent); + + // Advance 7 blocks more + System::initialize(&12, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent through call + assert_ok!(Contract::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 + * 4 // rent byte price + * 7; // blocks to rent + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2); + assert_eq!(bob_contract.deduct_block, 12); + 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())); + + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2); + assert_eq!(bob_contract.deduct_block, 12); + assert_eq!(Balances::free_balance(BOB), 30_000 - rent - rent_2); + }); } #[test] @@ -982,32 +954,29 @@ fn claim_surcharge_malus() { fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 100, - 100_000, code_hash.into(), - ::Balance::from(1_000u32).encode() // rent allowance - )); + 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( + Origin::signed(ALICE), + 100, + 100_000, code_hash.into(), + ::Balance::from(1_000u32).encode() // rent allowance + )); - // Advance blocks - System::initialize(&blocks, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + // Advance blocks + System::initialize(&blocks, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - // Trigger rent through call - assert!(trigger_call()); + // Trigger rent through call + assert!(trigger_call()); - if removes { - assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - } else { - assert!(ContractInfoOf::::get(BOB).unwrap().get_alive().is_some()); - } + if removes { + assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + } else { + assert!(ContractInfoOf::::get(BOB).unwrap().get_alive().is_some()); } - ); + }); } /// Test for all kind of removals for the given trigger: @@ -1018,123 +987,114 @@ fn removals(trigger_call: impl Fn() -> bool) { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); // Balance reached and superior to subsistence threshold - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 100, - 100_000, code_hash.into(), - ::Balance::from(1_000u32).encode() // rent allowance - )); - - let subsistence_threshold = 50 /*existential_deposit*/ + 16 /*tombstone_deposit*/; - - // 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); - - // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent through call - assert!(trigger_call()); - assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); - - // Advance blocks - System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // 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); - } - ); + 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( + Origin::signed(ALICE), + 100, + 100_000, code_hash.into(), + ::Balance::from(1_000u32).encode() // rent allowance + )); + + let subsistence_threshold = 50 /*existential_deposit*/ + 16 /*tombstone_deposit*/; + + // 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); + + // Advance blocks + System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent through call + assert!(trigger_call()); + assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + assert_eq!(Balances::free_balance(&BOB), subsistence_threshold); + + // Advance blocks + System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // 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); + }); // Allowance exceeded - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 1_000, - 100_000, code_hash.into(), - ::Balance::from(100u32).encode() // rent allowance - )); - - // 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); - - // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent through call - 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); - - // Advance blocks - System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // 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); - } - ); + 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( + Origin::signed(ALICE), + 1_000, + 100_000, code_hash.into(), + ::Balance::from(100u32).encode() // rent allowance + )); + + // 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); + + // Advance blocks + System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent through call + 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); + + // Advance blocks + System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // 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); + }); // Balance reached and inferior to subsistence threshold - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 50+Balances::minimum_balance(), - 100_000, code_hash.into(), - ::Balance::from(1_000u32).encode() // rent allowance - )); - - // 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()); - - // Transfer funds - assert_ok!(Contract::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()); - - // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent through call - assert!(trigger_call()); - assert!(ContractInfoOf::::get(BOB).is_none()); - assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); - - // Advance blocks - System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent must have no effect - assert!(trigger_call()); - assert!(ContractInfoOf::::get(BOB).is_none()); - assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); - } - ); + 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( + Origin::signed(ALICE), + 50+Balances::minimum_balance(), + 100_000, code_hash.into(), + ::Balance::from(1_000u32).encode() // rent allowance + )); + + // 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()); + + // Transfer funds + assert_ok!(Contract::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()); + + // Advance blocks + System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent through call + assert!(trigger_call()); + assert!(ContractInfoOf::::get(BOB).is_none()); + assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); + + // Advance blocks + System::initialize(&20, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent must have no effect + assert!(trigger_call()); + assert!(ContractInfoOf::::get(BOB).is_none()); + assert_eq!(Balances::free_balance(&BOB), Balances::minimum_balance()); + }); } #[test] @@ -1142,38 +1102,35 @@ fn call_removed_contract() { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); // Balance reached and superior to subsistence threshold - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 100, - 100_000, code_hash.into(), - ::Balance::from(1_000u32).encode() // rent allowance - )); - - // Calling contract should succeed. - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); - - // Advance blocks - System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Calling contract should remove contract and fail. - assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), - "contract has been evicted" - ); - - // Subsequent contract calls should also fail. - assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), - "contract has been evicted" - ); - } - ) + 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( + Origin::signed(ALICE), + 100, + 100_000, code_hash.into(), + ::Balance::from(1_000u32).encode() // rent allowance + )); + + // Calling contract should succeed. + assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + + // Advance blocks + System::initialize(&10, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Calling contract should remove contract and fail. + assert_err!( + Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + "contract has been evicted" + ); + + // Subsequent contract calls should also fail. + assert_err!( + Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + "contract has been evicted" + ); + }) } const CODE_CHECK_DEFAULT_RENT_ALLOWANCE: &str = r#" @@ -1230,35 +1187,32 @@ const CODE_CHECK_DEFAULT_RENT_ALLOWANCE: &str = r#" fn default_rent_allowance_on_instantiate() { let (wasm, code_hash) = compile_module::(CODE_CHECK_DEFAULT_RENT_ALLOWANCE).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 30_000, - 100_000, - code_hash.into(), - vec![], - )); - - // Check creation - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, >::max_value()); - - // Advance blocks - System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); - - // Check contract is still alive - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive(); - assert!(bob_contract.is_some()) - } - ); + 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( + Origin::signed(ALICE), + 30_000, + 100_000, + code_hash.into(), + vec![], + )); + + // Check creation + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, >::max_value()); + + // Advance blocks + System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // Trigger rent through call + assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + + // Check contract is still alive + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive(); + assert!(bob_contract.is_some()) + }); } const CODE_RESTORATION: &str = r#" @@ -1347,122 +1301,119 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: let (restoration_wasm, restoration_code_hash) = compile_module::(CODE_RESTORATION).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - 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)); - - // 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(balances::RawEvent::NewAccount(1, 1_000_000)), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(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( + 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)); + + // 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(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(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( + Origin::signed(ALICE), + 30_000, + 100_000, + set_rent_code_hash.into(), + ::Balance::from(0u32).encode() + )); + + // Check if `BOB` was created successfully and that the rent allowance is + // set to 0. + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, 0); + + if test_different_storage { + assert_ok!(Contract::call( Origin::signed(ALICE), - 30_000, - 100_000, - set_rent_code_hash.into(), - ::Balance::from(0u32).encode() - )); - - // Check if `BOB` was created successfully and that the rent allowance is - // set to 0. - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 0); - - if test_different_storage { - assert_ok!(Contract::call( - Origin::signed(ALICE), - BOB, 0, 100_000, - call::set_storage_4_byte()) - ); - } - - // Advance 4 blocks, to the 5th. - System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - - // 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()), - "contract has been evicted" + BOB, 0, 100_000, + call::set_storage_4_byte()) ); - assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - - /// Create another account with the address `DJANGO` with `CODE_RESTORATION`. - /// - /// 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( - Origin::signed(CHARLIE), - 30_000, - 100_000, - restoration_code_hash.into(), - ::Balance::from(0u32).encode() - )); - - // Before performing a call to `DJANGO` save its original trie id. - let django_trie_id = ContractInfoOf::::get(DJANGO).unwrap() - .get_alive().unwrap().trie_id; + } - if !test_restore_to_with_dirty_storage { - // Advance 1 block, to the 6th. - System::initialize(&6, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); - } + // Advance 4 blocks, to the 5th. + System::initialize(&5, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + + // 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()), + "contract has been evicted" + ); + assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + + /// Create another account with the address `DJANGO` with `CODE_RESTORATION`. + /// + /// 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( + Origin::signed(CHARLIE), + 30_000, + 100_000, + restoration_code_hash.into(), + ::Balance::from(0u32).encode() + )); + + // Before performing a call to `DJANGO` save its original trie id. + let django_trie_id = ContractInfoOf::::get(DJANGO).unwrap() + .get_alive().unwrap().trie_id; + + if !test_restore_to_with_dirty_storage { + // Advance 1 block, to the 6th. + System::initialize(&6, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + } - // Perform a call to `DJANGO`. This should either perform restoration successfully or - // fail depending on the test parameters. - assert_ok!(Contract::call( - Origin::signed(ALICE), - DJANGO, - 0, - 100_000, - vec![], - )); - - if test_different_storage || test_restore_to_with_dirty_storage { - // Parametrization of the test imply restoration failure. Check that `DJANGO` aka - // restoration contract is still in place and also that `BOB` doesn't exist. - assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); - let django_contract = ContractInfoOf::::get(DJANGO).unwrap() - .get_alive().unwrap(); - assert_eq!(django_contract.storage_size, 16); - assert_eq!(django_contract.trie_id, django_trie_id); - assert_eq!(django_contract.deduct_block, System::block_number()); - } else { - // Here we expect that the restoration is succeeded. Check that the restoration - // contract `DJANGO` ceased to exist and that `BOB` returned back. - println!("{:?}", ContractInfoOf::::get(BOB)); - let bob_contract = ContractInfoOf::::get(BOB).unwrap() - .get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, 50); - assert_eq!(bob_contract.storage_size, 12); - assert_eq!(bob_contract.trie_id, django_trie_id); - assert_eq!(bob_contract.deduct_block, System::block_number()); - assert!(ContractInfoOf::::get(DJANGO).is_none()); - } + // Perform a call to `DJANGO`. This should either perform restoration successfully or + // fail depending on the test parameters. + assert_ok!(Contract::call( + Origin::signed(ALICE), + DJANGO, + 0, + 100_000, + vec![], + )); + + if test_different_storage || test_restore_to_with_dirty_storage { + // Parametrization of the test imply restoration failure. Check that `DJANGO` aka + // restoration contract is still in place and also that `BOB` doesn't exist. + assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); + let django_contract = ContractInfoOf::::get(DJANGO).unwrap() + .get_alive().unwrap(); + assert_eq!(django_contract.storage_size, 16); + assert_eq!(django_contract.trie_id, django_trie_id); + assert_eq!(django_contract.deduct_block, System::block_number()); + } else { + // Here we expect that the restoration is succeeded. Check that the restoration + // contract `DJANGO` ceased to exist and that `BOB` returned back. + println!("{:?}", ContractInfoOf::::get(BOB)); + let bob_contract = ContractInfoOf::::get(BOB).unwrap() + .get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, 50); + assert_eq!(bob_contract.storage_size, 12); + assert_eq!(bob_contract.trie_id, django_trie_id); + assert_eq!(bob_contract.deduct_block, System::block_number()); + assert!(ContractInfoOf::::get(DJANGO).is_none()); } - ); + }); } const CODE_STORAGE_SIZE: &str = r#" @@ -1533,46 +1484,43 @@ const CODE_STORAGE_SIZE: &str = r#" fn storage_max_value_limit() { let (wasm, code_hash) = compile_module::(CODE_STORAGE_SIZE).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // Create - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 30_000, - 100_000, - code_hash.into(), - vec![], - )); - - // Check creation - let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); - assert_eq!(bob_contract.rent_allowance, >::max_value()); - - // Call contract with allowed storage value. - assert_ok!(Contract::call( + 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( + Origin::signed(ALICE), + 30_000, + 100_000, + code_hash.into(), + vec![], + )); + + // Check creation + let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, >::max_value()); + + // Call contract with allowed storage value. + assert_ok!(Contract::call( + Origin::signed(ALICE), + BOB, + 0, + 100_000, + Encode::encode(&self::MaxValueSize::get()), + )); + + // Call contract with too large a storage value. + assert_err!( + Contract::call( Origin::signed(ALICE), BOB, 0, 100_000, - Encode::encode(&self::MaxValueSize::get()), - )); - - // Call contract with too large a storage value. - assert_err!( - Contract::call( - Origin::signed(ALICE), - BOB, - 0, - 100_000, - Encode::encode(&(self::MaxValueSize::get() + 1)), - ), - "during execution" - ); - } - ); + Encode::encode(&(self::MaxValueSize::get() + 1)), + ), + "during execution" + ); + }); } const CODE_RETURN_WITH_DATA: &str = r#" @@ -1900,33 +1848,30 @@ fn deploy_and_call_other_contract() { let (callee_wasm, callee_code_hash) = compile_module::(CODE_RETURN_WITH_DATA).unwrap(); let (caller_wasm, caller_code_hash) = compile_module::(CODE_CALLER_CONTRACT).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // 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!(Contract::instantiate( - Origin::signed(ALICE), - 100_000, - 100_000, - caller_code_hash.into(), - vec![], - )); - - // 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( - Origin::signed(ALICE), - BOB, - 0, - 200_000, - callee_code_hash.as_ref().to_vec(), - )); - } - ); + 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!(Contract::instantiate( + Origin::signed(ALICE), + 100_000, + 100_000, + caller_code_hash.into(), + vec![], + )); + + // 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( + Origin::signed(ALICE), + BOB, + 0, + 200_000, + callee_code_hash.as_ref().to_vec(), + )); + }); } const CODE_SELF_DESTRUCT: &str = r#" @@ -2031,86 +1976,80 @@ const CODE_SELF_DESTRUCT: &str = r#" #[test] fn self_destruct_by_draining_balance() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - - // Instantiate the BOB contract. - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 100_000, - 100_000, - code_hash.into(), - vec![], - )); - - // Check that the BOB contract has been instantiated. - assert_matches!( - ContractInfoOf::::get(BOB), - Some(ContractInfo::Alive(_)) - ); - - // Call BOB with no input data, forcing it to self-destruct. - assert_ok!(Contract::call( - Origin::signed(ALICE), - BOB, - 0, - 100_000, - vec![], - )); - - // Check that BOB is now dead. - assert!(ContractInfoOf::::get(BOB).is_none()); - } - ); + 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)); + + // Instantiate the BOB contract. + assert_ok!(Contract::instantiate( + Origin::signed(ALICE), + 100_000, + 100_000, + code_hash.into(), + vec![], + )); + + // Check that the BOB contract has been instantiated. + assert_matches!( + ContractInfoOf::::get(BOB), + Some(ContractInfo::Alive(_)) + ); + + // Call BOB with no input data, forcing it to self-destruct. + assert_ok!(Contract::call( + Origin::signed(ALICE), + BOB, + 0, + 100_000, + vec![], + )); + + // Check that BOB is now dead. + assert!(ContractInfoOf::::get(BOB).is_none()); + }); } #[test] fn cannot_self_destruct_while_live() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - - // Instantiate the BOB contract. - assert_ok!(Contract::instantiate( + 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)); + + // Instantiate the BOB contract. + assert_ok!(Contract::instantiate( + Origin::signed(ALICE), + 100_000, + 100_000, + code_hash.into(), + vec![], + )); + + // Check that the BOB contract has been instantiated. + assert_matches!( + ContractInfoOf::::get(BOB), + Some(ContractInfo::Alive(_)) + ); + + // Call BOB with input data, forcing it make a recursive call to itself to + // self-destruct, resulting in a trap. + assert_err!( + Contract::call( Origin::signed(ALICE), + BOB, + 0, 100_000, - 100_000, - code_hash.into(), - vec![], - )); - - // Check that the BOB contract has been instantiated. - assert_matches!( - ContractInfoOf::::get(BOB), - Some(ContractInfo::Alive(_)) - ); - - // Call BOB with input data, forcing it make a recursive call to itself to - // self-destruct, resulting in a trap. - assert_err!( - Contract::call( - Origin::signed(ALICE), - BOB, - 0, - 100_000, - vec![0], - ), - "during execution" - ); - - // Check that BOB is still alive. - assert_matches!( - ContractInfoOf::::get(BOB), - Some(ContractInfo::Alive(_)) - ); - } - ); + vec![0], + ), + "during execution" + ); + + // Check that BOB is still alive. + assert_matches!( + ContractInfoOf::::get(BOB), + Some(ContractInfo::Alive(_)) + ); + }); } const CODE_DESTROY_AND_TRANSFER: &str = r#" @@ -2272,43 +2211,40 @@ fn destroy_contract_and_transfer_funds() { let (callee_wasm, callee_code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); let (caller_wasm, caller_code_hash) = compile_module::(CODE_DESTROY_AND_TRANSFER).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - // 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)); - - // This deploys the BOB contract, which in turn deploys the CHARLIE contract during - // construction. - assert_ok!(Contract::instantiate( - Origin::signed(ALICE), - 200_000, - 100_000, - caller_code_hash.into(), - callee_code_hash.as_ref().to_vec(), - )); - - // Check that the CHARLIE contract has been instantiated. - assert_matches!( - ContractInfoOf::::get(CHARLIE), - Some(ContractInfo::Alive(_)) - ); - - // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. - assert_ok!(Contract::call( - Origin::signed(ALICE), - BOB, - 0, - 100_000, - CHARLIE.encode(), - )); - - // Check that CHARLIE has moved on to the great beyond (ie. died). - assert!(ContractInfoOf::::get(CHARLIE).is_none()); - } - ); + 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)); + + // This deploys the BOB contract, which in turn deploys the CHARLIE contract during + // construction. + assert_ok!(Contract::instantiate( + Origin::signed(ALICE), + 200_000, + 100_000, + caller_code_hash.into(), + callee_code_hash.as_ref().to_vec(), + )); + + // Check that the CHARLIE contract has been instantiated. + assert_matches!( + ContractInfoOf::::get(CHARLIE), + Some(ContractInfo::Alive(_)) + ); + + // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. + assert_ok!(Contract::call( + Origin::signed(ALICE), + BOB, + 0, + 100_000, + CHARLIE.encode(), + )); + + // Check that CHARLIE has moved on to the great beyond (ie. died). + assert!(ContractInfoOf::::get(CHARLIE).is_none()); + }); } const CODE_SELF_DESTRUCTING_CONSTRUCTOR: &str = r#" @@ -2371,43 +2307,37 @@ const CODE_SELF_DESTRUCTING_CONSTRUCTOR: &str = r#" #[test] fn cannot_self_destruct_in_constructor() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCTING_CONSTRUCTOR).unwrap(); - with_externalities( - &mut ExtBuilder::default().existential_deposit(50).build(), - || { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::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( - Origin::signed(ALICE), - 100_000, - 100_000, - code_hash.into(), - vec![], - ), - "insufficient remaining balance" - ); - } - ); + 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)); + + // Fail to instantiate the BOB contract since its final balance is below existential + // deposit. + assert_err!( + Contract::instantiate( + Origin::signed(ALICE), + 100_000, + 100_000, + code_hash.into(), + vec![], + ), + "insufficient remaining balance" + ); + }); } #[test] fn check_block_gas_limit_works() { - with_externalities( - &mut ExtBuilder::default().block_gas_limit(50).build(), - || { - let info = DispatchInfo { weight: 100, class: DispatchClass::Normal }; - let check = CheckBlockGasLimit::(Default::default()); - let call: Call = crate::Call::put_code(1000, vec![]).into(); + ExtBuilder::default().block_gas_limit(50).build().execute_with(|| { + let info = DispatchInfo { weight: 100, class: DispatchClass::Normal }; + let check = CheckBlockGasLimit::(Default::default()); + let call: Call = crate::Call::put_code(1000, vec![]).into(); - assert_eq!( - check.validate(&0, &call, info, 0), InvalidTransaction::ExhaustsResources.into(), - ); + assert_eq!( + check.validate(&0, &call, info, 0), InvalidTransaction::ExhaustsResources.into(), + ); - let call: Call = crate::Call::update_schedule(Default::default()).into(); - assert_eq!(check.validate(&0, &call, info, 0), Ok(Default::default())); - } - ); + let call: Call = crate::Call::update_schedule(Default::default()).into(); + assert_eq!(check.validate(&0, &call, info, 0), Ok(Default::default())); + }); } diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs deleted file mode 100644 index aa27bcde21f6aafd990b9660142bd7b249339ff1..0000000000000000000000000000000000000000 --- a/srml/council/src/lib.rs +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2017-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 . - -//! Council system: Handles the voting in and maintenance of council members. - -#![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit="128"] - -pub mod motions; -pub mod seats; - -pub use crate::seats::{Trait, Module, RawEvent, Event, VoteIndex}; - -/// Trait for type that can handle incremental changes to a set of account IDs. -pub trait OnMembersChanged { - /// A number of members `new` just joined the set and replaced some `old` ones. - fn on_members_changed(new: &[AccountId], old: &[AccountId]); -} - -impl OnMembersChanged for () { - fn on_members_changed(_new: &[T], _old: &[T]) {} -} - -#[cfg(test)] -mod tests { - // These re-exports are here for a reason, edit with care - pub use super::*; - pub use runtime_io::with_externalities; - use support::{impl_outer_origin, impl_outer_event, impl_outer_dispatch, parameter_types}; - use support::traits::Get; - pub use primitives::{H256, Blake2Hasher, u32_trait::{_1, _2, _3, _4}}; - pub use sr_primitives::traits::{BlakeTwo256, IdentityLookup}; - pub use sr_primitives::testing::{Digest, DigestItem, Header}; - pub use sr_primitives::Perbill; - pub use {seats, motions}; - use std::cell::RefCell; - - impl_outer_origin! { - pub enum Origin for Test { - motions - } - } - - impl_outer_event! { - pub enum Event for Test { - balances, democracy, seats, motions, - } - } - - impl_outer_dispatch! { - pub enum Call for Test where origin: Origin { - type Error = Error; - - balances::Balances, - democracy::Democracy, - } - } - - thread_local! { - static VOTER_BOND: RefCell = RefCell::new(0); - static VOTING_FEE: RefCell = RefCell::new(0); - static PRESENT_SLASH_PER_VOTER: RefCell = RefCell::new(0); - static DECAY_RATIO: RefCell = RefCell::new(0); - } - - pub struct VotingBond; - impl Get for VotingBond { - fn get() -> u64 { VOTER_BOND.with(|v| *v.borrow()) } - } - - pub struct VotingFee; - impl Get for VotingFee { - fn get() -> u64 { VOTING_FEE.with(|v| *v.borrow()) } - } - - pub struct PresentSlashPerVoter; - impl Get for PresentSlashPerVoter { - fn get() -> u64 { PRESENT_SLASH_PER_VOTER.with(|v| *v.borrow()) } - } - - pub struct DecayRatio; - impl Get for DecayRatio { - fn get() -> u32 { DECAY_RATIO.with(|v| *v.borrow()) } - } - - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Eq, PartialEq, Debug)] - pub struct Test; - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: u32 = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); - } - impl system::Trait for Test { - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Call = (); - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type WeightMultiplierUpdate = (); - type Event = Event; - type Error = Error; - type BlockHashCount = BlockHashCount; - type MaximumBlockWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type Version = (); - } - parameter_types! { - pub const ExistentialDeposit: u64 = 0; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 1; - pub const TransactionByteFee: u64 = 0; - } - impl balances::Trait for Test { - type Balance = u64; - type OnNewAccount = (); - type OnFreeBalanceZero = (); - type Event = Event; - type TransactionPayment = (); - type TransferPayment = (); - type DustRemoval = (); - type Error = Error; - type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); - } - parameter_types! { - pub const LaunchPeriod: u64 = 1; - pub const VotingPeriod: u64 = 3; - pub const MinimumDeposit: u64 = 1; - pub const EnactmentPeriod: u64 = 0; - pub const CooloffPeriod: u64 = 2; - } - impl democracy::Trait for Test { - type Proposal = Call; - type Event = Event; - type Currency = balances::Module; - type EnactmentPeriod = EnactmentPeriod; - type LaunchPeriod = LaunchPeriod; - type EmergencyVotingPeriod = VotingPeriod; - type VotingPeriod = VotingPeriod; - type MinimumDeposit = MinimumDeposit; - type ExternalOrigin = motions::EnsureProportionAtLeast<_1, _2, u64>; - type ExternalMajorityOrigin = motions::EnsureProportionAtLeast<_2, _3, u64>; - type EmergencyOrigin = motions::EnsureProportionAtLeast<_1, _1, u64>; - type CancellationOrigin = motions::EnsureProportionAtLeast<_2, _3, u64>; - type VetoOrigin = motions::EnsureMember; - type CooloffPeriod = CooloffPeriod; - } - parameter_types! { - pub const CandidacyBond: u64 = 3; - pub const CarryCount: u32 = 2; - pub const InactiveGracePeriod: u32 = 1; - pub const CouncilVotingPeriod: u64 = 4; - } - impl seats::Trait for Test { - type Event = Event; - type BadPresentation = (); - type BadReaper = (); - type BadVoterIndex = (); - type LoserCandidate = (); - type OnMembersChanged = CouncilMotions; - type CandidacyBond = CandidacyBond; - type VotingBond = VotingBond; - type VotingFee = VotingFee; - type PresentSlashPerVoter = PresentSlashPerVoter; - type CarryCount = CarryCount; - type InactiveGracePeriod = InactiveGracePeriod; - type CouncilVotingPeriod = CouncilVotingPeriod; - type DecayRatio = DecayRatio; - } - impl motions::Trait for Test { - type Origin = Origin; - type Proposal = Call; - type Event = Event; - } - - pub struct ExtBuilder { - balance_factor: u64, - decay_ratio: u32, - voting_fee: u64, - voter_bond: u64, - bad_presentation_punishment: u64, - with_council: bool, - } - - impl Default for ExtBuilder { - fn default() -> Self { - Self { - balance_factor: 1, - decay_ratio: 24, - voting_fee: 0, - voter_bond: 0, - bad_presentation_punishment: 1, - with_council: false, - } - } - } - - impl ExtBuilder { - pub fn with_council(mut self, council: bool) -> Self { - self.with_council = council; - self - } - pub fn balance_factor(mut self, factor: u64) -> Self { - self.balance_factor = factor; - self - } - pub fn decay_ratio(mut self, ratio: u32) -> Self { - self.decay_ratio = ratio; - self - } - pub fn voting_fee(mut self, fee: u64) -> Self { - self.voting_fee = fee; - self - } - pub fn bad_presentation_punishment(mut self, fee: u64) -> Self { - self.bad_presentation_punishment = fee; - self - } - pub fn voter_bond(mut self, fee: u64) -> Self { - self.voter_bond = fee; - self - } - pub fn set_associated_consts(&self) { - VOTER_BOND.with(|v| *v.borrow_mut() = self.voter_bond); - VOTING_FEE.with(|v| *v.borrow_mut() = self.voting_fee); - PRESENT_SLASH_PER_VOTER.with(|v| *v.borrow_mut() = self.bad_presentation_punishment); - DECAY_RATIO.with(|v| *v.borrow_mut() = self.decay_ratio); - } - pub fn build(self) -> runtime_io::TestExternalities { - self.set_associated_consts(); - let mut t = system::GenesisConfig::default().build_storage::().unwrap(); - 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![], - }.assimilate_storage(&mut t).unwrap(); - seats::GenesisConfig:: { - active_council: if self.with_council { vec![ - (1, 10), - (2, 10), - (3, 10) - ] } else { vec![] }, - desired_seats: 2, - presentation_duration: 2, - term_duration: 5, - }.assimilate_storage(&mut t).unwrap(); - runtime_io::TestExternalities::new(t) - } - } - - pub type System = system::Module; - pub type Balances = balances::Module; - pub type Democracy = democracy::Module; - pub type Council = seats::Module; - pub type CouncilMotions = motions::Module; -} diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index b93f6f893aef9d0f44e2316b23d127556420f1c7..545666e493026202199aff88ef74d717f3c3ab52 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -21,6 +21,7 @@ use rstd::prelude::*; use rstd::{result, convert::TryFrom}; use sr_primitives::{ + RuntimeDebug, traits::{Zero, Bounded, CheckedMul, CheckedDiv, EnsureOrigin, Hash, Dispatchable}, weights::SimpleDispatchInfo, }; @@ -48,8 +49,7 @@ pub type PropIndex = u32; pub type ReferendumIndex = u32; /// A value denoting the strength of conviction of a vote. -#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug)] pub enum Conviction { /// 0.1x votes, unlocked. None, @@ -148,8 +148,7 @@ impl Bounded for Conviction { const MAX_RECURSION_LIMIT: u32 = 16; /// A number of lock periods, plus a vote, one way or the other. -#[derive(Copy, Clone, Eq, PartialEq, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Copy, Clone, Eq, PartialEq, Default, RuntimeDebug)] pub struct Vote { pub aye: bool, pub conviction: Conviction, @@ -231,8 +230,7 @@ pub trait Trait: system::Trait + Sized { } /// Info regarding an ongoing referendum. -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct ReferendumInfo { /// When voting on this referendum will end. end: BlockNumber, @@ -259,38 +257,38 @@ impl ReferendumInfo as Democracy { /// The number of (public) proposals that have been made so far. - pub PublicPropCount get(public_prop_count) build(|_| 0 as PropIndex) : PropIndex; + pub PublicPropCount get(fn public_prop_count) build(|_| 0 as PropIndex) : PropIndex; /// The public proposals. Unsorted. - pub PublicProps get(public_props): Vec<(PropIndex, T::Proposal, T::AccountId)>; + pub PublicProps get(fn public_props): Vec<(PropIndex, T::Proposal, T::AccountId)>; /// Those who have locked a deposit. - pub DepositOf get(deposit_of): map PropIndex => Option<(BalanceOf, Vec)>; + pub DepositOf get(fn deposit_of): map PropIndex => Option<(BalanceOf, Vec)>; /// The next free referendum index, aka the number of referenda started so far. - pub ReferendumCount get(referendum_count) build(|_| 0 as ReferendumIndex): ReferendumIndex; + pub ReferendumCount get(fn referendum_count) build(|_| 0 as ReferendumIndex): ReferendumIndex; /// The next referendum index that should be tallied. - pub NextTally get(next_tally) build(|_| 0 as ReferendumIndex): ReferendumIndex; + pub NextTally get(fn next_tally) build(|_| 0 as ReferendumIndex): ReferendumIndex; /// Information concerning any given referendum. - pub ReferendumInfoOf get(referendum_info): + pub ReferendumInfoOf get(fn referendum_info): map ReferendumIndex => Option<(ReferendumInfo)>; /// Queue of successful referenda to be dispatched. - pub DispatchQueue get(dispatch_queue): + pub DispatchQueue get(fn dispatch_queue): map T::BlockNumber => Vec>; /// Get the voters for the current proposal. - pub VotersFor get(voters_for): map ReferendumIndex => Vec; + pub VotersFor get(fn voters_for): map 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(vote_of): map (ReferendumIndex, T::AccountId) => Vote; + pub VoteOf get(fn vote_of): map (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(proxy): map T::AccountId => Option; + pub Proxy get(fn proxy): map T::AccountId => Option; /// Get the account (and lock periods) to which another account is delegating vote. - pub Delegations get(delegations): linked_map T::AccountId => (T::AccountId, Conviction); + pub Delegations get(fn delegations): linked_map T::AccountId => (T::AccountId, Conviction); /// True if the last referendum tabled was submitted externally. False if it was a public /// proposal. @@ -304,7 +302,7 @@ 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(blacklist): map T::Hash => Option<(T::BlockNumber, Vec)>; + pub Blacklist get(fn blacklist): map T::Hash => Option<(T::BlockNumber, Vec)>; /// Record of all proposals that have been subject to emergency cancellation. pub Cancellations: map T::Hash => bool; @@ -488,9 +486,10 @@ decl_module! { /// but it is not a majority-carries referendum then it fails. /// /// - `proposal_hash`: The hash of the current external proposal. - /// - `voting_period`: The period that is allowed for voting on this proposal. + /// - `voting_period`: The period that is allowed for voting on this proposal. Increased to + /// `EmergencyVotingPeriod` if too low. /// - `delay`: The number of block after voting has ended in approval and this should be - /// enacted. Increased to `EmergencyVotingPeriod` if too low. + /// enacted. This doesn't have a minimum amount. #[weight = SimpleDispatchInfo::FixedNormal(200_000)] fn fast_track(origin, proposal_hash: T::Hash, @@ -971,14 +970,12 @@ impl OnFreeBalanceZero for Module { #[cfg(test)] mod tests { use super::*; - use runtime_io::with_externalities; use support::{ impl_outer_origin, impl_outer_dispatch, assert_noop, assert_ok, parameter_types, traits::Contains }; - use primitives::{H256, Blake2Hasher}; - use sr_primitives::{traits::{BlakeTwo256, IdentityLookup, Bounded}, testing::Header}; - use sr_primitives::Perbill; + use primitives::H256; + use sr_primitives::{traits::{BlakeTwo256, IdentityLookup, Bounded}, testing::Header, Perbill}; use balances::BalanceLock; use system::EnsureSignedBy; @@ -1017,7 +1014,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -1029,23 +1025,17 @@ mod tests { pub const ExistentialDeposit: u64 = 0; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const LaunchPeriod: u64 = 2; @@ -1084,7 +1074,7 @@ mod tests { type CooloffPeriod = CooloffPeriod; } - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); balances::GenesisConfig::{ balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], @@ -1100,7 +1090,7 @@ mod tests { #[test] fn params_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Democracy::referendum_count(), 0); assert_eq!(Balances::free_balance(&42), 0); assert_eq!(Balances::total_issuance(), 210); @@ -1132,7 +1122,7 @@ mod tests { #[test] fn external_and_public_interleaving_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(Democracy::external_propose( Origin::signed(2), @@ -1245,7 +1235,7 @@ mod tests { #[test] fn emergency_cancel_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); let r = Democracy::inject_referendum( 2, @@ -1274,7 +1264,7 @@ mod tests { #[test] fn veto_external_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(Democracy::external_propose( Origin::signed(2), @@ -1334,7 +1324,7 @@ mod tests { #[test] fn external_referendum_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_noop!(Democracy::external_propose( Origin::signed(1), @@ -1363,7 +1353,7 @@ mod tests { #[test] fn external_majority_referendum_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_noop!(Democracy::external_propose_majority( Origin::signed(1), @@ -1388,7 +1378,7 @@ mod tests { #[test] fn external_default_referendum_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_noop!(Democracy::external_propose_default( Origin::signed(3), @@ -1413,7 +1403,7 @@ mod tests { #[test] fn fast_track_referendum_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); assert_noop!(Democracy::fast_track(Origin::signed(5), h, 3, 2), "no proposal made"); @@ -1437,7 +1427,7 @@ mod tests { #[test] fn fast_track_referendum_fails_when_no_simple_majority() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); assert_ok!(Democracy::external_propose( @@ -1453,7 +1443,7 @@ mod tests { #[test] fn locked_for_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); assert_ok!(propose_set_balance(1, 2, 2)); assert_ok!(propose_set_balance(1, 4, 4)); @@ -1466,7 +1456,7 @@ mod tests { #[test] fn single_proposal_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); assert!(Democracy::referendum_info(0).is_none()); @@ -1513,7 +1503,7 @@ mod tests { #[test] fn cancel_queued_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1537,7 +1527,7 @@ mod tests { #[test] fn proxy_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Democracy::proxy(10), None); assert_ok!(Democracy::set_proxy(Origin::signed(1), 10)); assert_eq!(Democracy::proxy(10), Some(1)); @@ -1567,7 +1557,7 @@ mod tests { #[test] fn single_proposal_should_work_with_proxy() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1587,7 +1577,7 @@ mod tests { #[test] fn single_proposal_should_work_with_delegation() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1612,7 +1602,7 @@ mod tests { #[test] fn single_proposal_should_work_with_cyclic_delegation() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1639,7 +1629,7 @@ mod tests { #[test] /// If transactor already voted, delegated vote is overwriten. fn single_proposal_should_work_with_vote_and_delegation() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1665,7 +1655,7 @@ mod tests { #[test] fn single_proposal_should_work_with_undelegation() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1694,7 +1684,7 @@ mod tests { #[test] /// If transactor voted, delegated vote is overwriten. fn single_proposal_should_work_with_delegation_and_vote() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 1)); @@ -1725,7 +1715,7 @@ mod tests { #[test] fn deposit_for_proposals_should_be_taken() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); assert_ok!(propose_set_balance(1, 2, 5)); assert_ok!(Democracy::second(Origin::signed(2), 0)); @@ -1740,7 +1730,7 @@ mod tests { #[test] fn deposit_for_proposals_should_be_returned() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); assert_ok!(propose_set_balance(1, 2, 5)); assert_ok!(Democracy::second(Origin::signed(2), 0)); @@ -1756,7 +1746,7 @@ mod tests { #[test] fn proposal_with_deposit_below_minimum_should_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); assert_noop!(propose_set_balance(1, 2, 0), "value too low"); }); @@ -1764,7 +1754,7 @@ mod tests { #[test] fn poor_proposer_should_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); assert_noop!(propose_set_balance(1, 2, 11), "proposer\'s balance too low"); }); @@ -1772,7 +1762,7 @@ mod tests { #[test] fn poor_seconder_should_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); assert_ok!(propose_set_balance(2, 2, 11)); assert_noop!(Democracy::second(Origin::signed(1), 0), "seconder\'s balance too low"); @@ -1781,7 +1771,7 @@ mod tests { #[test] fn runners_up_should_come_after() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); assert_ok!(propose_set_balance(1, 2, 2)); assert_ok!(propose_set_balance(1, 4, 4)); @@ -1797,7 +1787,7 @@ mod tests { #[test] fn simple_passing_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, @@ -1820,7 +1810,7 @@ mod tests { #[test] fn cancel_referendum_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, @@ -1840,7 +1830,7 @@ mod tests { #[test] fn simple_failing_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, @@ -1863,7 +1853,7 @@ mod tests { #[test] fn controversial_voting_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, @@ -1889,7 +1879,7 @@ mod tests { #[test] fn delayed_enactment_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, @@ -1917,7 +1907,7 @@ mod tests { #[test] fn controversial_low_turnout_voting_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, @@ -1939,7 +1929,7 @@ mod tests { #[test] fn passing_low_turnout_voting_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Balances::free_balance(&42), 0); assert_eq!(Balances::total_issuance(), 210); @@ -1965,7 +1955,7 @@ mod tests { #[test] fn lock_voting_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(0); let r = Democracy::inject_referendum( 1, @@ -2025,7 +2015,7 @@ mod tests { #[test] fn lock_voting_should_work_with_delegation() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); let r = Democracy::inject_referendum( 1, diff --git a/srml/democracy/src/vote_threshold.rs b/srml/democracy/src/vote_threshold.rs index d304c36f32c95d7ba7523c0576f0bad4deed9d09..61e7e6535941ab6d797c6f9bbdcc14eefc63e0f4 100644 --- a/srml/democracy/src/vote_threshold.rs +++ b/srml/democracy/src/vote_threshold.rs @@ -23,8 +23,8 @@ use sr_primitives::traits::{Zero, IntegerSquareRoot}; use rstd::ops::{Add, Mul, Div, Rem}; /// A means of determining if a vote is past pass threshold. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, sr_primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum VoteThreshold { /// A supermajority of approvals is needed to pass this vote. SuperMajorityApprove, diff --git a/srml/elections-phragmen/Cargo.toml b/srml/elections-phragmen/Cargo.toml index 02898fc754f2ede30f3c6b13ac9624f769e1678e..85532849ecc00c84729b71c8d4a3445616fccbc7 100644 --- a/srml/elections-phragmen/Cargo.toml +++ b/srml/elections-phragmen/Cargo.toml @@ -5,10 +5,7 @@ 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"] } -primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } -runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } sr-primitives = { path = "../../core/sr-primitives", default-features = false } phragmen = { package = "substrate-phragmen", path = "../../core/phragmen", default-features = false } srml-support = { path = "../support", default-features = false } @@ -16,16 +13,16 @@ system = { package = "srml-system", path = "../system", default-features = false rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } [dev-dependencies] +runtime_io = { package = "sr-io", path = "../../core/sr-io" } hex-literal = "0.2.1" balances = { package = "srml-balances", path = "../balances" } +primitives = { package = "substrate-primitives", path = "../../core/primitives" } +serde = { version = "1.0.101" } [features] default = ["std"] std = [ "codec/std", - "primitives/std", - "serde", - "runtime_io/std", "srml-support/std", "sr-primitives/std", "phragmen/std", diff --git a/srml/elections-phragmen/src/lib.rs b/srml/elections-phragmen/src/lib.rs index b7cf1a4013d5048e2e302d38af9fef4e21abf5c7..9b277815f385a8452d5a1b4d1e5200f718e1e9e0 100644 --- a/srml/elections-phragmen/src/lib.rs +++ b/srml/elections-phragmen/src/lib.rs @@ -76,15 +76,19 @@ #![cfg_attr(not(feature = "std"), no_std)] +use rstd::prelude::*; +use codec::Decode; use sr_primitives::{print, traits::{Zero, StaticLookup, Bounded, Convert}}; use sr_primitives::weights::SimpleDispatchInfo; use srml_support::{ decl_storage, decl_event, ensure, decl_module, dispatch, + storage::unhashed, traits::{ Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons, - ChangeMembers, OnUnbalanced, + ChangeMembers, OnUnbalanced, WithdrawReason } }; +use phragmen::ExtendedBalance; use system::{self, ensure_signed, ensure_root}; const MODULE_ID: LockIdentifier = *b"phrelect"; @@ -126,35 +130,42 @@ pub trait Trait: system::Trait { /// Handler for the unbalanced reduction when a member has been kicked. type KickedMember: OnUnbalanced>; + + /// Number of members to elect. + type DesiredMembers: Get; + + /// Number of runners_up to keep. + type DesiredRunnersUp: Get; + + /// How long each seat is kept. This defines the next block number at which an election + /// round will happen. If set to zero, no elections are ever triggered and the module will + /// be in passive mode. + type TermDuration: Get; } decl_storage! { trait Store for Module as PhragmenElection { - // ---- parameters - /// Number of members to elect. - pub DesiredMembers get(desired_members) config(): u32; - /// Number of runners_up to keep. - pub DesiredRunnersUp get(desired_runners_up) config(): u32; - /// How long each seat is kept. This defines the next block number at which an election - /// round will happen. - pub TermDuration get(term_duration) config(): T::BlockNumber; - // ---- State /// The current elected membership. Sorted based on account id. - pub Members get(members) config(): Vec; + pub Members get(fn members): Vec<(T::AccountId, BalanceOf)>; /// The current runners_up. Sorted based on low to high merit (worse to best runner). - pub RunnersUp get(runners_up): Vec; + pub RunnersUp get(fn runners_up): Vec<(T::AccountId, BalanceOf)>; /// The total number of vote rounds that have happened, excluding the upcoming one. - pub ElectionRounds get(election_rounds): u32 = Zero::zero(); + pub ElectionRounds get(fn election_rounds): u32 = Zero::zero(); /// Votes of a particular voter, with the round index of the votes. - pub VotesOf get(votes_of): linked_map T::AccountId => Vec; + pub VotesOf get(fn votes_of): linked_map T::AccountId => Vec; /// Locked stake of a voter. - pub StakeOf get(stake_of): map T::AccountId => BalanceOf; + pub StakeOf get(fn stake_of): map T::AccountId => BalanceOf; /// The present candidate list. Sorted based on account id. A current member can never enter /// this vector and is always implicitly assumed to be a candidate. - pub Candidates get(candidates): Vec; + pub Candidates get(fn candidates): Vec; + + /// Has the storage format been updated? + /// NOTE: Only use and set to false if you have used an early version of this module. Should + /// be set to true otherwise. + DidMigrate: bool; } } @@ -164,6 +175,9 @@ decl_module! { const CandidacyBond: BalanceOf = T::CandidacyBond::get(); const VotingBond: BalanceOf = T::VotingBond::get(); + const DesiredMembers: u32 = T::DesiredMembers::get(); + const DesiredRunnersUp: u32 = T::DesiredRunnersUp::get(); + const TermDuration: T::BlockNumber = T::TermDuration::get(); /// Vote for a set of candidates for the upcoming round of election. /// @@ -181,7 +195,7 @@ decl_module! { /// Writes: O(V) given `V` votes. V is bounded by 16. /// # #[weight = SimpleDispatchInfo::FixedNormal(100_000)] - fn vote(origin, votes: Vec, value: BalanceOf) { + fn vote(origin, votes: Vec, #[compact] value: BalanceOf) { let who = ensure_signed(origin)?; let candidates_count = >::decode_len().unwrap_or(0) as usize; @@ -192,8 +206,8 @@ decl_module! { ensure!(!allowed_votes.is_zero(), "cannot vote when no candidates or members exist"); ensure!(votes.len() <= allowed_votes, "cannot vote more than candidates"); ensure!(votes.len() <= MAXIMUM_VOTE, "cannot vote more than maximum allowed"); - ensure!(!votes.is_empty(), "must vote for at least one candidate."); + ensure!( value > T::Currency::minimum_balance(), "cannot vote with stake less than minimum balance" @@ -213,7 +227,7 @@ decl_module! { &who, locked_balance, T::BlockNumber::max_value(), - WithdrawReasons::all(), + WithdrawReasons::except(WithdrawReason::TransactionPayment), ); >::insert(&who, locked_balance); >::insert(&who, votes); @@ -308,19 +322,6 @@ decl_module! { >::mutate(|c| c.insert(index, who)); } - /// Set the desired member count. Changes will be effective at the beginning of next round. - /// - /// # - /// #### State - /// Reads: O(1) - /// Writes: O(1) - /// # - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn set_desired_member_count(origin, #[compact] count: u32) { - ensure_root(origin)?; - DesiredMembers::put(count); - } - /// Remove a particular member from the set. This is effective immediately. /// /// If a runner-up is available, then the best runner-up will be removed and replaces the @@ -338,51 +339,51 @@ decl_module! { ensure_root(origin)?; let who = T::Lookup::lookup(who)?; - let mut members = Self::members(); - if let Ok(index) = members.binary_search(&who) { + let mut members_with_stake = Self::members(); + if let Ok(index) = members_with_stake.binary_search_by(|(ref m, ref _s)| m.cmp(&who)) { // remove, slash, emit event. - members.remove(index); + members_with_stake.remove(index); let (imbalance, _) = T::Currency::slash_reserved(&who, T::CandidacyBond::get()); T::KickedMember::on_unbalanced(imbalance); Self::deposit_event(RawEvent::MemberKicked(who.clone())); let mut runners_up = Self::runners_up(); - if let Some(replacement) = runners_up.pop() { + if let Some((replacement, stake)) = runners_up.pop() { // replace the outgoing with the best runner up. - if let Err(index) = members.binary_search(&replacement) { - members.insert(index, replacement.clone()); + if let Err(index) = members_with_stake + .binary_search_by(|(ref m, ref _s)| m.cmp(&replacement)) + { + members_with_stake.insert(index, (replacement.clone(), stake)); ElectionRounds::mutate(|v| *v += 1); - T::ChangeMembers::change_members_sorted(&[replacement], &[who], &members); + T::ChangeMembers::change_members_sorted( + &[replacement], + &[who], + &members_with_stake + .iter() + .map(|(m, _)| m.clone()) + .collect::>(), + ); } // else it would mean that the runner up was already a member. This cannot // happen. If it does, not much that we can do about it. - >::put(members); + >::put(members_with_stake); >::put(runners_up); } else { // update `Members` storage -- `do_phragmen` adds this to the candidate list. - >::put(members); + >::put(members_with_stake); // trigger a new phragmen. grab a cup of coffee. This might take a while. Self::do_phragmen(); } } } - /// Set the duration of each term. This will affect the next election's block number. - /// - /// # - /// #### State - /// Reads: O(1) - /// Writes: O(1) - /// # - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn set_term_duration(origin, #[compact] count: T::BlockNumber) { - ensure_root(origin)?; - >::put(count); - } - /// What to do at the end of each block. Checks if an election needs to happen or not. fn on_initialize(n: T::BlockNumber) { + if !DidMigrate::exists() { + DidMigrate::put(true); + Self::do_migrate(); + } if let Err(e) = Self::end_block(n) { print("Guru meditation"); print(e); @@ -392,10 +393,13 @@ decl_module! { } decl_event!( - pub enum Event where ::AccountId { + pub enum Event where + Balance = BalanceOf, + ::AccountId, + { /// A new term with new members. This indicates that enough candidates existed, not that /// enough have has been elected. The inner value must be examined for this purpose. - NewTerm(Vec), + NewTerm(Vec<(AccountId, Balance)>), /// No (or not enough) candidates existed for this round. EmptyTerm, /// A member has been removed. This should always be followed by either `NewTerm` ot @@ -427,7 +431,32 @@ impl Module { /// /// Limited number of members. Binary search. Constant time factor. O(1) fn is_member(who: &T::AccountId) -> bool { - Self::members().binary_search(who).is_ok() + Self::members_ids().binary_search(who).is_ok() + } + + /// Returns number of desired members. + fn desired_members() -> u32 { + T::DesiredMembers::get() + } + + /// Returns number of desired runners up. + fn desired_runners_up() -> u32 { + T::DesiredRunnersUp::get() + } + + /// Returns the term duration + fn term_duration() -> T::BlockNumber { + T::TermDuration::get() + } + + /// Get the members' account ids. + fn members_ids() -> Vec { + Self::members().into_iter().map(|(m, _)| m).collect::>() + } + + /// The the runners' up account ids. + fn runners_up_ids() -> Vec { + Self::runners_up().into_iter().map(|(r, _)| r).collect::>() } /// Check if `who` is a defunct voter. @@ -470,8 +499,10 @@ impl Module { /// Runs phragmen election and cleans all the previous candidate state. The voter state is NOT /// cleaned and voters must themselves submit a transaction to retract. fn end_block(block_number: T::BlockNumber) -> dispatch::Result { - if (block_number % Self::term_duration()).is_zero() { - Self::do_phragmen(); + if !Self::term_duration().is_zero() { + if (block_number % Self::term_duration()).is_zero() { + Self::do_phragmen(); + } } Ok(()) } @@ -496,11 +527,11 @@ impl Module { let mut exposed_candidates = candidates.clone(); // current members are always a candidate for the next round as well. // this is guaranteed to not create any duplicates. - candidates.append(&mut Self::members()); + candidates.append(&mut Self::members_ids()); // previous runners_up are also always candidates for the next round. - candidates.append(&mut Self::runners_up()); + candidates.append(&mut Self::runners_up_ids()); // and exposed to being an outgoing in case they are no longer elected. - exposed_candidates.append(&mut Self::runners_up()); + exposed_candidates.append(&mut Self::runners_up_ids()); let voters_and_votes = >::enumerate() .map(|(v, i)| (v, i)) @@ -528,14 +559,35 @@ impl Module { .filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } ) .collect::>(); + let support_map = phragmen::build_support_map::<_, _, _, T::CurrencyToVote>( + &new_set, + &phragmen_result.assignments, + Self::locked_stake_of, + false, + ); + + let to_balance = |e: ExtendedBalance| + >>::convert(e); + let new_set_with_stake = new_set + .into_iter() + .map(|ref m| { + let support = support_map.get(m) + .expect( + "entire new_set was given to build_support_map; en entry must be \ + created for each item; qed" + ); + (m.clone(), to_balance(support.total)) + }) + .collect::)>>(); + // split new set into winners and runner ups. - let split_point = desired_seats.min(new_set.len()); - let mut new_members = (&new_set[..split_point]).to_vec(); - let runners_up = &new_set[split_point..] + let split_point = desired_seats.min(new_set_with_stake.len()); + let mut new_members = (&new_set_with_stake[..split_point]).to_vec(); + let runners_up = &new_set_with_stake[split_point..] .into_iter() .cloned() .rev() - .collect::>(); + .collect::)>>(); // sort and save the members. new_members.sort(); @@ -546,13 +598,13 @@ impl Module { // report member changes. We compute diff because we need the outgoing list. let (incoming, outgoing) = T::ChangeMembers::compute_members_diff( - &new_members, - &old_members + &Self::members_ids(), + &old_members.into_iter().map(|(m, _)| m).collect::>(), ); T::ChangeMembers::change_members_sorted( &incoming, &outgoing.clone(), - &new_members + &Self::members_ids(), ); // unlike exposed_candidates, these are members who were in the list and no longer @@ -563,7 +615,8 @@ impl Module { // runner up list is not sorted. O(K*N) given K runner ups. Overall: O(NLogM + N*K) exposed_candidates.into_iter().for_each(|c| { // any candidate who is not a member and not a runner up. - if new_members.binary_search(&c).is_err() && !runners_up.contains(&c) + if new_members.binary_search_by_key(&c, |(m, _)| m.clone()).is_err() + && !Self::runners_up_ids().contains(&c) { let (imbalance, _) = T::Currency::slash_reserved(&c, T::CandidacyBond::get()); T::LoserCandidate::on_unbalanced(imbalance); @@ -584,6 +637,32 @@ impl Module { ElectionRounds::mutate(|v| *v += 1); } + + /// Perform the storage update needed to migrate the module from the initial version of the + /// storage. + /// + /// If decoding the old storage fails in any way, the consequence is that we start with an empty + /// set. + fn do_migrate() { + // old storage format. + let old_members: Vec = unhashed::get_raw(&>::hashed_key()) + .and_then(|bytes| Decode::decode(&mut &*bytes).ok()).unwrap_or_default(); + let old_runners: Vec = unhashed::get_raw(&>::hashed_key()) + .and_then(|bytes| Decode::decode(&mut &*bytes).ok()).unwrap_or_default(); + + // new storage format. + let new_runners: Vec<(T::AccountId, BalanceOf)> = old_runners + .into_iter() + .map(|r| (r, Zero::zero())) + .collect(); + let new_members: Vec<(T::AccountId, BalanceOf)> = old_members + .into_iter() + .map(|r| (r, Zero::zero())) + .collect(); + + >::put(new_members); + >::put(new_runners); + } } #[cfg(test)] @@ -591,10 +670,10 @@ mod tests { use super::*; use std::cell::RefCell; use srml_support::{assert_ok, assert_noop, parameter_types, assert_eq_uvec}; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; - use sr_primitives::{Perbill, testing::Header, BuildStorage, - traits::{BlakeTwo256, IdentityLookup, Block as BlockT} + use primitives::H256; + use sr_primitives::{ + Perbill, testing::Header, BuildStorage, + traits::{OnInitialize, BlakeTwo256, IdentityLookup, Block as BlockT}, }; use crate as elections; @@ -616,7 +695,6 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; @@ -628,8 +706,6 @@ mod tests { pub const ExistentialDeposit: u64 = 1; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { @@ -637,15 +713,11 @@ mod tests { type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = Event; - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { @@ -654,7 +726,9 @@ mod tests { thread_local! { static VOTING_BOND: RefCell = RefCell::new(2); - static MEMBERS: RefCell> = RefCell::new(vec![]); + static DESIRED_MEMBERS: RefCell = RefCell::new(2); + static DESIRED_RUNNERS_UP: RefCell = RefCell::new(2); + static TERM_DURATION: RefCell = RefCell::new(5); } pub struct VotingBond; @@ -662,6 +736,21 @@ mod tests { fn get() -> u64 { VOTING_BOND.with(|v| *v.borrow()) } } + pub struct DesiredMembers; + impl Get for DesiredMembers { + fn get() -> u32 { DESIRED_MEMBERS.with(|v| *v.borrow()) } + } + + pub struct DesiredRunnersUp; + impl Get for DesiredRunnersUp { + fn get() -> u32 { DESIRED_RUNNERS_UP.with(|v| *v.borrow()) } + } + + pub struct TermDuration; + impl Get for TermDuration { + fn get() -> u64 { TERM_DURATION.with(|v| *v.borrow()) } + } + pub struct TestChangeMembers; impl ChangeMembers for TestChangeMembers { fn change_members_sorted(_: &[u64], _: &[u64], _: &[u64]) {} @@ -685,6 +774,9 @@ mod tests { type ChangeMembers = TestChangeMembers; type CandidacyBond = CandidacyBond; type VotingBond = VotingBond; + type TermDuration = TermDuration; + type DesiredMembers = DesiredMembers; + type DesiredRunnersUp = DesiredRunnersUp; type LoserCandidate = (); type KickedMember = (); type BadReport = (); @@ -701,13 +793,14 @@ mod tests { { System: system::{Module, Call, Event}, Balances: balances::{Module, Call, Event, Config}, - Elections: elections::{Module, Call, Event, Config}, + Elections: elections::{Module, Call, Event}, } ); pub struct ExtBuilder { balance_factor: u64, voter_bond: u64, + term_duration: u64, desired_runners_up: u32, } @@ -717,6 +810,7 @@ mod tests { balance_factor: 1, voter_bond: 2, desired_runners_up: 0, + term_duration: 5, } } } @@ -730,8 +824,14 @@ mod tests { self.desired_runners_up = count; self } - pub fn build(self) -> runtime_io::TestExternalities { + pub fn term_duration(mut self, duration: u64) -> Self { + self.term_duration = duration; + self + } + pub fn build(self) -> runtime_io::TestExternalities { VOTING_BOND.with(|v| *v.borrow_mut() = self.voter_bond); + TERM_DURATION.with(|v| *v.borrow_mut() = self.term_duration); + DESIRED_RUNNERS_UP.with(|v| *v.borrow_mut() = self.desired_runners_up); GenesisConfig { balances: Some(balances::GenesisConfig::{ balances: vec![ @@ -744,12 +844,6 @@ mod tests { ], vesting: vec![], }), - elections: Some(elections::GenesisConfig::{ - members: vec![], - desired_members: 2, - desired_runners_up: self.desired_runners_up, - term_duration: 5, - }), }.build_storage().unwrap().into() } } @@ -768,9 +862,33 @@ mod tests { lock.amount } + #[test] + fn temp_migration_works() { + ExtBuilder::default().build().execute_with(|| { + use srml_support::storage::unhashed; + use codec::Encode; + + let old_members = vec![1u64, 2]; + let old_runners = vec![3u64]; + + let members_key = >::hashed_key(); + let runners_key = >::hashed_key(); + + unhashed::put_raw(&members_key, &old_members.encode()[..]); + unhashed::put_raw(&runners_key, &old_runners.encode()[..]); + + assert_eq!(DidMigrate::get(), false); + >::on_initialize(1); + assert_eq!(DidMigrate::get(), true); + + assert_eq!(Elections::members(), vec![(1, 0), (2, 0)]); + assert_eq!(Elections::runners_up(), vec![(3, 0)]); + }); + } + #[test] fn params_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::desired_members(), 2); assert_eq!(Elections::term_duration(), 5); @@ -788,9 +906,34 @@ mod tests { }); } + #[test] + fn term_duration_zero_is_passive() { + ExtBuilder::default() + .term_duration(0) + .build() + .execute_with(|| + { + System::set_block_number(1); + assert_eq!(Elections::term_duration(), 0); + assert_eq!(Elections::desired_members(), 2); + assert_eq!(Elections::election_rounds(), 0); + + assert_eq!(Elections::members_ids(), vec![]); + assert_eq!(Elections::runners_up(), vec![]); + assert_eq!(Elections::candidates(), vec![]); + + System::set_block_number(5); + assert_ok!(Elections::end_block(System::block_number())); + + assert_eq!(Elections::members_ids(), vec![]); + assert_eq!(Elections::runners_up(), vec![]); + assert_eq!(Elections::candidates(), vec![]); + }); + } + #[test] fn simple_candidate_submission_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::candidates(), Vec::::new()); assert!(Elections::is_candidate(&1).is_err()); assert!(Elections::is_candidate(&2).is_err()); @@ -817,7 +960,7 @@ mod tests { #[test] fn simple_candidate_submission_with_no_votes_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::candidates(), Vec::::new()); assert_ok!(Elections::submit_candidacy(Origin::signed(1))); @@ -827,7 +970,7 @@ mod tests { assert!(Elections::is_candidate(&2).is_ok()); assert_eq!(Elections::candidates(), vec![1, 2]); - assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::members_ids(), vec![]); assert_eq!(Elections::runners_up(), vec![]); System::set_block_number(5); @@ -837,14 +980,14 @@ mod tests { assert!(Elections::is_candidate(&2).is_err()); assert_eq!(Elections::candidates(), vec![]); - assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::members_ids(), vec![]); assert_eq!(Elections::runners_up(), vec![]); }); } #[test] fn dupe_candidate_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::candidates(), Vec::::new()); assert_ok!(Elections::submit_candidacy(Origin::signed(1))); assert_eq!(Elections::candidates(), vec![1]); @@ -858,14 +1001,14 @@ mod tests { #[test] fn member_candidacy_submission_should_not_work() { // critically important to make sure that outgoing candidates and losers are not mixed up. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::vote(Origin::signed(2), vec![5], 20)); System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![5]); + assert_eq!(Elections::members_ids(), vec![5]); assert_eq!(Elections::runners_up(), vec![]); assert_eq!(Elections::candidates(), vec![]); @@ -878,7 +1021,7 @@ mod tests { #[test] fn poor_candidate_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::candidates(), Vec::::new()); assert_noop!( Elections::submit_candidacy(Origin::signed(7)), @@ -889,7 +1032,7 @@ mod tests { #[test] fn simple_voting_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::candidates(), Vec::::new()); assert_eq!(balances(&2), (20, 0)); @@ -903,7 +1046,7 @@ mod tests { #[test] fn can_vote_with_custom_stake() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::candidates(), Vec::::new()); assert_eq!(balances(&2), (20, 0)); @@ -917,7 +1060,7 @@ mod tests { #[test] fn can_update_votes_and_stake() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(balances(&2), (20, 0)); assert_ok!(Elections::submit_candidacy(Origin::signed(5))); @@ -938,7 +1081,7 @@ mod tests { #[test] fn cannot_vote_for_no_candidate() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_noop!( Elections::vote(Origin::signed(2), vec![], 20), "cannot vote when no candidates or members exist" @@ -949,7 +1092,7 @@ mod tests { #[test] fn can_vote_for_old_members_even_when_no_new_candidates() { // let allowed_votes = candidates_count as usize + Self::members().len() - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -958,7 +1101,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::candidates(), vec![]); assert_ok!(Elections::vote(Origin::signed(3), vec![4, 5], 10)); @@ -967,7 +1110,7 @@ mod tests { #[test] fn cannot_vote_for_more_than_candidates() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -980,7 +1123,7 @@ mod tests { #[test] fn cannot_vote_for_less_than_ed() { - with_externalities(&mut ExtBuilder::default().voter_bond(8).build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -993,7 +1136,7 @@ mod tests { #[test] fn can_vote_for_more_than_total_balance_but_moot() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1006,7 +1149,7 @@ mod tests { #[test] fn remove_voter_should_work() { - with_externalities(&mut ExtBuilder::default().voter_bond(8).build(), || { + ExtBuilder::default().voter_bond(8).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::vote(Origin::signed(2), vec![5], 20)); @@ -1031,14 +1174,14 @@ mod tests { #[test] fn non_voter_remove_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_noop!(Elections::remove_voter(Origin::signed(3)), "must be a voter"); }); } #[test] fn dupe_remove_should_fail() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::vote(Origin::signed(2), vec![5], 20)); @@ -1051,7 +1194,7 @@ mod tests { #[test] fn removed_voter_should_not_be_counted() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1065,13 +1208,13 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![3, 5]); + assert_eq!(Elections::members_ids(), vec![3, 5]); }); } #[test] fn reporter_must_be_voter() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_noop!( Elections::report_defunct_voter(Origin::signed(1), 2), "reporter must be a voter", @@ -1081,7 +1224,7 @@ mod tests { #[test] fn can_detect_defunct_voter() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1094,7 +1237,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::candidates(), vec![]); // all of them have a member that they voted for. @@ -1116,7 +1259,7 @@ mod tests { #[test] fn report_voter_should_work_and_earn_reward() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1129,7 +1272,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::candidates(), vec![]); assert_eq!(balances(&3), (28, 2)); @@ -1148,8 +1291,7 @@ mod tests { #[test] fn report_voter_should_slash_when_bad_report() { - with_externalities(&mut ExtBuilder::default().build(), || { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1159,7 +1301,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::candidates(), vec![]); assert_eq!(balances(&4), (35, 5)); @@ -1174,13 +1316,12 @@ mod tests { assert_eq!(balances(&4), (35, 5)); assert_eq!(balances(&5), (45, 3)); }); - }); } #[test] fn simple_voting_rounds_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1203,7 +1344,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![3, 5]); + assert_eq!(Elections::members(), vec![(3, 30), (5, 20)]); assert_eq!(Elections::runners_up(), vec![]); assert_eq_uvec!(all_voters(), vec![2, 3, 4]); assert_eq!(Elections::candidates(), vec![]); @@ -1215,7 +1356,7 @@ mod tests { #[test] fn defunct_voter_will_be_counted() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); // This guy's vote is pointless for this round. @@ -1225,7 +1366,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![5]); + assert_eq!(Elections::members(), vec![(5, 50)]); assert_eq!(Elections::election_rounds(), 1); // but now it has a valid target. @@ -1235,7 +1376,7 @@ mod tests { assert_ok!(Elections::end_block(System::block_number())); // candidate 4 is affected by an old vote. - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members(), vec![(4, 30), (5, 50)]); assert_eq!(Elections::election_rounds(), 2); assert_eq_uvec!(all_voters(), vec![3, 5]); }); @@ -1243,7 +1384,7 @@ mod tests { #[test] fn only_desired_seats_are_chosen() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1258,13 +1399,13 @@ mod tests { assert_ok!(Elections::end_block(System::block_number())); assert_eq!(Elections::election_rounds(), 1); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); }); } #[test] fn phragmen_should_not_self_vote() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1273,13 +1414,13 @@ mod tests { assert_eq!(Elections::candidates(), vec![]); assert_eq!(Elections::election_rounds(), 1); - assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::members_ids(), vec![]); }); } #[test] fn runners_up_should_be_kept() { - with_externalities(&mut ExtBuilder::default().desired_runners_up(2).build(), || { + ExtBuilder::default().desired_runners_up(2).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1293,9 +1434,9 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); // sorted based on account id. - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); // sorted based on merit (least -> most) - assert_eq!(Elections::runners_up(), vec![3, 2]); + assert_eq!(Elections::runners_up_ids(), vec![3, 2]); // runner ups are still locked. assert_eq!(balances(&4), (35, 5)); @@ -1306,7 +1447,7 @@ mod tests { #[test] fn runners_up_should_be_next_candidates() { - with_externalities(&mut ExtBuilder::default().desired_runners_up(2).build(), || { + ExtBuilder::default().desired_runners_up(2).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1319,21 +1460,21 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); - assert_eq!(Elections::runners_up(), vec![2, 3]); + assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]); + assert_eq!(Elections::runners_up(), vec![(2, 20), (3, 30)]); assert_ok!(Elections::vote(Origin::signed(5), vec![5], 15)); System::set_block_number(10); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![3, 4]); - assert_eq!(Elections::runners_up(), vec![5, 2]); + assert_eq!(Elections::members(), vec![(3, 30), (4, 40)]); + assert_eq!(Elections::runners_up(), vec![(5, 15), (2, 20)]); }); } #[test] fn runners_up_lose_bond_once_outgoing() { - with_externalities(&mut ExtBuilder::default().desired_runners_up(1).build(), || { + ExtBuilder::default().desired_runners_up(1).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(2))); @@ -1344,9 +1485,9 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::runners_up(), vec![2]); + assert_eq!(Elections::runners_up_ids(), vec![2]); assert_eq!(balances(&2), (15, 5)); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1354,9 +1495,9 @@ mod tests { System::set_block_number(10); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::runners_up(), vec![3]); + assert_eq!(Elections::runners_up_ids(), vec![3]); assert_eq!(balances(&3), (25, 5)); assert_eq!(balances(&2), (15, 2)); }); @@ -1364,7 +1505,7 @@ mod tests { #[test] fn current_members_are_always_implicitly_next_candidate() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1374,7 +1515,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::election_rounds(), 1); assert_ok!(Elections::submit_candidacy(Origin::signed(2))); @@ -1392,7 +1533,7 @@ mod tests { assert_ok!(Elections::end_block(System::block_number())); // 4 removed; 5 and 3 are the new best. - assert_eq!(Elections::members(), vec![3, 5]); + assert_eq!(Elections::members_ids(), vec![3, 5]); }); } @@ -1400,7 +1541,7 @@ mod tests { fn election_state_is_uninterrupted() { // what I mean by uninterrupted: // given no input or stimulants the same members are re-elected. - with_externalities(&mut ExtBuilder::default().desired_runners_up(2).build(), || { + ExtBuilder::default().desired_runners_up(2).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1415,8 +1556,8 @@ mod tests { System::set_block_number(b.into()); assert_ok!(Elections::end_block(System::block_number())); // we keep re-electing the same folks. - assert_eq!(Elections::members(), vec![4, 5]); - assert_eq!(Elections::runners_up(), vec![2, 3]); + assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]); + assert_eq!(Elections::runners_up(), vec![(2, 20), (3, 30)]); // no new candidates but old members and runners-up are always added. assert_eq!(Elections::candidates(), vec![]); assert_eq!(Elections::election_rounds(), b / 5); @@ -1433,7 +1574,7 @@ mod tests { #[test] fn remove_members_triggers_election() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); @@ -1442,7 +1583,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::election_rounds(), 1); // a new candidate @@ -1453,13 +1594,13 @@ mod tests { assert_eq!(balances(&4), (35, 2)); // slashed assert_eq!(Elections::election_rounds(), 2); // new election round - assert_eq!(Elections::members(), vec![3, 5]); // new members + assert_eq!(Elections::members_ids(), vec![3, 5]); // new members }); } #[test] fn seats_should_be_released_when_no_vote() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1475,7 +1616,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![3, 5]); + assert_eq!(Elections::members_ids(), vec![3, 5]); assert_eq!(Elections::election_rounds(), 1); assert_ok!(Elections::remove_voter(Origin::signed(2))); @@ -1486,14 +1627,14 @@ mod tests { // meanwhile, no one cares to become a candidate again. System::set_block_number(10); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::members_ids(), vec![]); assert_eq!(Elections::election_rounds(), 2); }); } #[test] fn outgoing_will_get_the_bond_back() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(balances(&5), (50, 0)); assert_ok!(Elections::submit_candidacy(Origin::signed(5))); @@ -1504,14 +1645,14 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![5]); + assert_eq!(Elections::members_ids(), vec![5]); assert_ok!(Elections::remove_voter(Origin::signed(5))); assert_eq!(balances(&5), (47, 3)); System::set_block_number(10); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::members_ids(), vec![]); assert_eq!(balances(&5), (50, 0)); }); @@ -1519,7 +1660,7 @@ mod tests { #[test] fn losers_will_lose_the_bond() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1531,7 +1672,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![5]); + assert_eq!(Elections::members_ids(), vec![5]); // winner assert_eq!(balances(&5), (47, 3)); @@ -1542,7 +1683,7 @@ mod tests { #[test] fn incoming_outgoing_are_reported() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(5))); @@ -1551,7 +1692,7 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![4, 5]); + assert_eq!(Elections::members_ids(), vec![4, 5]); assert_ok!(Elections::submit_candidacy(Origin::signed(1))); assert_ok!(Elections::submit_candidacy(Origin::signed(2))); @@ -1571,7 +1712,7 @@ mod tests { assert_ok!(Elections::end_block(System::block_number())); // 3, 4 are new members, must still be bonded, nothing slashed. - assert_eq!(Elections::members(), vec![3, 4]); + assert_eq!(Elections::members(), vec![(3, 30), (4, 48)]); assert_eq!(balances(&3), (25, 5)); assert_eq!(balances(&4), (35, 5)); @@ -1581,13 +1722,16 @@ mod tests { // 5 is an outgoing loser, it will get their bond back. assert_eq!(balances(&5), (48, 2)); - assert_eq!(System::events()[0].event, Event::elections(RawEvent::NewTerm(vec![4, 5]))); + assert_eq!( + System::events()[0].event, + Event::elections(RawEvent::NewTerm(vec![(4, 40), (5, 50)])), + ); }) } #[test] fn invalid_votes_are_moot() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(4))); assert_ok!(Elections::submit_candidacy(Origin::signed(3))); @@ -1598,8 +1742,30 @@ mod tests { System::set_block_number(5); assert_ok!(Elections::end_block(System::block_number())); - assert_eq_uvec!(Elections::members(), vec![3, 4]); + assert_eq_uvec!(Elections::members_ids(), vec![3, 4]); assert_eq!(Elections::election_rounds(), 1); }); } + + #[test] + fn members_are_sorted_based_on_id_runners_on_merit() { + ExtBuilder::default().desired_runners_up(2).build().execute_with(|| { + assert_ok!(Elections::submit_candidacy(Origin::signed(5))); + assert_ok!(Elections::submit_candidacy(Origin::signed(4))); + assert_ok!(Elections::submit_candidacy(Origin::signed(3))); + assert_ok!(Elections::submit_candidacy(Origin::signed(2))); + + assert_ok!(Elections::vote(Origin::signed(2), vec![3], 20)); + assert_ok!(Elections::vote(Origin::signed(3), vec![2], 30)); + assert_ok!(Elections::vote(Origin::signed(4), vec![5], 40)); + assert_ok!(Elections::vote(Origin::signed(5), vec![4], 50)); + + System::set_block_number(5); + assert_ok!(Elections::end_block(System::block_number())); + // id: low -> high. + assert_eq!(Elections::members(), vec![(4, 50), (5, 40)]); + // merit: low -> high. + assert_eq!(Elections::runners_up(), vec![(3, 20), (2, 30)]); + }); + } } diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs index 2a683879f8770a9b828324628771c870f63a054a..1e2349440f1681abff31e6554d662cd4668a8af4 100644 --- a/srml/elections/src/lib.rs +++ b/srml/elections/src/lib.rs @@ -25,7 +25,10 @@ use rstd::prelude::*; use sr_primitives::{ - print, traits::{Zero, One, StaticLookup, Bounded, Saturating}, weights::SimpleDispatchInfo, + RuntimeDebug, + print, + traits::{Zero, One, StaticLookup, Bounded, Saturating}, + weights::SimpleDispatchInfo, }; use support::{ dispatch::Result, decl_storage, decl_event, ensure, decl_module, @@ -98,8 +101,7 @@ mod tests; // entries before they increase the capacity. /// The activity status of a voter. -#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Default, RuntimeDebug)] pub struct VoterInfo { /// Last VoteIndex in which this voter assigned (or initialized) approvals. last_active: VoteIndex, @@ -114,8 +116,7 @@ pub struct VoterInfo { } /// Used to demonstrate the status of a particular index in the global voter list. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, RuntimeDebug)] pub enum CellStatus { /// Any out of bound index. Means a push a must happen to the chunk pointed by `NextVoterSet`. /// Voting fee is applied in case a new chunk is created. @@ -213,11 +214,11 @@ decl_storage! { // ---- parameters /// How long to give each top candidate to present themselves after the vote ends. - pub PresentationDuration get(presentation_duration) config(): T::BlockNumber; + pub PresentationDuration get(fn presentation_duration) config(): T::BlockNumber; /// How long each position is active for. - pub TermDuration get(term_duration) config(): T::BlockNumber; + pub TermDuration get(fn term_duration) config(): T::BlockNumber; /// Number of accounts that should constitute the collective. - pub DesiredSeats get(desired_seats) config(): u32; + pub DesiredSeats get(fn desired_seats) config(): u32; // ---- permanent state (always relevant, changes only at the finalization of voting) @@ -225,9 +226,9 @@ decl_storage! { /// executive matters. The block number (second element in the tuple) is the block that /// their position is active until (calculated by the sum of the block number when the /// member was elected and their term duration). - pub Members get(members) config(): Vec<(T::AccountId, T::BlockNumber)>; + pub Members get(fn members) config(): Vec<(T::AccountId, T::BlockNumber)>; /// The total number of vote rounds that have happened or are in progress. - pub VoteCount get(vote_index): VoteIndex; + pub VoteCount get(fn vote_index): VoteIndex; // ---- persistent state (always relevant, changes constantly) @@ -235,35 +236,35 @@ 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(approvals_of): map (T::AccountId, SetIndex) => Vec; + pub ApprovalsOf get(fn approvals_of): map (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(candidate_reg_info): map T::AccountId => Option<(VoteIndex, u32)>; + pub RegisterInfoOf get(fn candidate_reg_info): map T::AccountId => Option<(VoteIndex, u32)>; /// Basic information about a voter. - pub VoterInfoOf get(voter_info): map T::AccountId => Option>>; + pub VoterInfoOf get(fn voter_info): map T::AccountId => Option>>; /// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]). - pub Voters get(voters): map SetIndex => Vec>; + pub Voters get(fn voters): map SetIndex => Vec>; /// the next free set to store a voter in. This will keep growing. - pub NextVoterSet get(next_nonfull_voter_set): SetIndex = 0; + pub NextVoterSet get(fn next_nonfull_voter_set): SetIndex = 0; /// Current number of Voters. - pub VoterCount get(voter_count): SetIndex = 0; + pub VoterCount get(fn voter_count): SetIndex = 0; /// The present candidate list. - pub Candidates get(candidates): Vec; // has holes + pub Candidates get(fn candidates): Vec; // has holes /// Current number of active candidates - pub CandidateCount get(candidate_count): u32; + pub CandidateCount get(fn candidate_count): u32; // ---- temporary state (only relevant during finalization/presentation) /// The accounts holding the seats that will become free on the next tally. - pub NextFinalize get(next_finalize): Option<(T::BlockNumber, u32, Vec)>; + pub NextFinalize get(fn next_finalize): Option<(T::BlockNumber, u32, Vec)>; /// Get the leaderboard if we're in the presentation phase. The first element is the weight /// of each entry; It may be the direct summed approval stakes, or a weighted version of it. /// Sorted from low to high. - pub Leaderboard get(leaderboard): Option, T::AccountId)> >; + pub Leaderboard get(fn leaderboard): Option, T::AccountId)> >; /// Who is able to vote for whom. Value is the fund-holding account, key is the /// vote-transaction-sending account. - pub Proxy get(proxy): map T::AccountId => Option; + pub Proxy get(fn proxy): map T::AccountId => Option; } } @@ -344,7 +345,7 @@ decl_module! { votes: Vec, #[compact] index: VoteIndex, hint: SetIndex, - value: BalanceOf + #[compact] value: BalanceOf ) -> Result { let who = ensure_signed(origin)?; Self::do_set_approvals(who, votes, index, hint, value) @@ -361,7 +362,7 @@ decl_module! { votes: Vec, #[compact] index: VoteIndex, hint: SetIndex, - value: BalanceOf + #[compact] value: BalanceOf ) -> Result { let who = Self::proxy(ensure_signed(origin)?).ok_or("not a proxy")?; Self::do_set_approvals(who, votes, index, hint, value) @@ -806,7 +807,7 @@ impl Module { let imbalance = T::Currency::withdraw( &who, T::VotingFee::get(), - WithdrawReason::Fee, + WithdrawReason::Fee.into(), ExistenceRequirement::KeepAlive, )?; T::BadVoterIndex::on_unbalanced(imbalance); diff --git a/srml/elections/src/mock.rs b/srml/elections/src/mock.rs index 161da35e6a6b1df81bdf7a79f3acd56c00bd4f39..77b13d74a139f42d7aab7ad6a76741cdd70c8342 100644 --- a/srml/elections/src/mock.rs +++ b/srml/elections/src/mock.rs @@ -23,12 +23,9 @@ use support::{ StorageValue, StorageMap, parameter_types, assert_ok, traits::{Get, ChangeMembers, Currency} }; -use runtime_io::with_externalities; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; use sr_primitives::{ - Perbill, BuildStorage, - testing::Header, - traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, + Perbill, BuildStorage, testing::Header, traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, }; use crate as elections; @@ -50,7 +47,6 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; @@ -62,23 +58,17 @@ parameter_types! { pub const ExistentialDeposit: u64 = 0; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = u64; type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = Event; - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { @@ -213,7 +203,7 @@ impl ExtBuilder { self.desired_seats = seats; self } - pub fn build(self) -> runtime_io::TestExternalities { + pub fn build(self) -> runtime_io::TestExternalities { VOTER_BOND.with(|v| *v.borrow_mut() = self.voter_bond); VOTING_FEE.with(|v| *v.borrow_mut() = self.voting_fee); PRESENT_SLASH_PER_VOTER.with(|v| *v.borrow_mut() = self.bad_presentation_punishment); @@ -283,9 +273,9 @@ pub(crate) fn locks(who: &u64) -> Vec { Balances::locks(who).iter().map(|l| l.amount).collect::>() } -pub(crate) fn new_test_ext_with_candidate_holes() -> runtime_io::TestExternalities { +pub(crate) fn new_test_ext_with_candidate_holes() -> runtime_io::TestExternalities { let mut t = ExtBuilder::default().build(); - with_externalities(&mut t, || { + t.execute_with(|| { >::put(vec![0, 0, 1]); elections::CandidateCount::put(1); >::insert(1, (0, 2)); diff --git a/srml/elections/src/tests.rs b/srml/elections/src/tests.rs index c6f4f9a0b375f813e4ce0b03756aad566b679aff..c9bb054ab23538edf62dfbcfb67b648f278c7a96 100644 --- a/srml/elections/src/tests.rs +++ b/srml/elections/src/tests.rs @@ -22,11 +22,10 @@ use crate::mock::*; use crate::*; use support::{assert_ok, assert_err, assert_noop}; -use runtime_io::with_externalities; #[test] fn params_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::next_vote_from(1), 4); assert_eq!(Elections::next_vote_from(4), 4); @@ -53,7 +52,7 @@ fn params_should_work() { #[test] fn chunking_bool_to_flag_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::bool_to_flag(vec![]), vec![]); assert_eq!(Elections::bool_to_flag(vec![false]), vec![0]); assert_eq!(Elections::bool_to_flag(vec![true]), vec![1]); @@ -98,7 +97,7 @@ fn chunking_bool_to_flag_should_work() { #[test] fn chunking_voter_set_growth_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); // create 65. 64 (set0) + 1 (set1) @@ -122,7 +121,7 @@ fn chunking_voter_set_growth_should_work() { #[test] fn chunking_voter_set_reclaim_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); (1..=129).for_each(|i| vote(i, 0)); @@ -159,7 +158,7 @@ fn chunking_voter_set_reclaim_should_work() { #[test] fn chunking_approvals_set_growth_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { // create candidates and voters. (1..=250).for_each(|i| create_candidate(i, (i-1) as u32)); (1..=250).for_each(|i| vote(i, i as usize)); @@ -221,7 +220,7 @@ fn chunking_approvals_set_growth_should_work() { #[test] fn chunking_cell_status_works() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); (1..=63).for_each(|i| vote(i, 0)); @@ -240,7 +239,7 @@ fn chunking_cell_status_works() { #[test] fn chunking_voter_index_does_not_take_holes_into_account() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); // create 65. 64 (set0) + 1 (set1) @@ -265,7 +264,7 @@ fn chunking_voter_index_does_not_take_holes_into_account() { #[test] fn chunking_approval_storage_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); @@ -285,7 +284,7 @@ fn chunking_approval_storage_should_work() { #[test] fn voting_initial_set_approvals_ignores_voter_index() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); // Last argument is essentially irrelevant. You might get or miss a tip. @@ -299,7 +298,7 @@ fn voting_initial_set_approvals_ignores_voter_index() { } #[test] fn voting_bad_approval_index_slashes_voters_and_bond_reduces_stake() { - with_externalities(&mut ExtBuilder::default().voting_fee(5).voter_bond(2).build(), || { + ExtBuilder::default().voting_fee(5).voter_bond(2).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); (1..=63).for_each(|i| vote(i, 0)); @@ -329,7 +328,7 @@ fn voting_bad_approval_index_slashes_voters_and_bond_reduces_stake() { #[test] fn voting_subsequent_set_approvals_checks_voter_index() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(3), vec![], 0, 0, 30)); @@ -353,7 +352,7 @@ fn voting_subsequent_set_approvals_checks_voter_index() { #[test] fn voting_cannot_lock_less_than_limit() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_noop!( @@ -366,7 +365,7 @@ fn voting_cannot_lock_less_than_limit() { #[test] fn voting_locking_more_than_total_balance_is_moot() { - with_externalities(&mut ExtBuilder::default().voter_bond(2).build(), || { + ExtBuilder::default().voter_bond(2).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_eq!(balances(&3), (30, 0)); @@ -382,7 +381,7 @@ fn voting_locking_more_than_total_balance_is_moot() { #[test] fn voting_locking_stake_and_reserving_bond_works() { - with_externalities(&mut ExtBuilder::default().voter_bond(2).build(), || { + ExtBuilder::default().voter_bond(2).build().execute_with(|| { assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); assert_eq!(balances(&2), (20, 0)); @@ -408,7 +407,7 @@ fn voting_locking_stake_and_reserving_bond_works() { #[test] fn voting_without_any_candidate_count_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates().len(), 0); @@ -422,7 +421,7 @@ fn voting_without_any_candidate_count_should_not_work() { #[test] fn voting_setting_an_approval_vote_count_more_than_candidate_count_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); @@ -437,7 +436,7 @@ fn voting_setting_an_approval_vote_count_more_than_candidate_count_should_not_wo #[test] fn voting_resubmitting_approvals_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); @@ -456,7 +455,7 @@ fn voting_resubmitting_approvals_should_work() { #[test] fn voting_retracting_voter_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); @@ -501,7 +500,7 @@ fn voting_retracting_voter_should_work() { #[test] fn voting_invalid_retraction_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); @@ -514,7 +513,7 @@ fn voting_invalid_retraction_index_should_not_work() { #[test] fn voting_overflow_retraction_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); @@ -525,7 +524,7 @@ fn voting_overflow_retraction_index_should_not_work() { #[test] fn voting_non_voter_retraction_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); @@ -536,7 +535,7 @@ fn voting_non_voter_retraction_should_not_work() { #[test] fn retracting_inactive_voter_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -570,7 +569,7 @@ fn retracting_inactive_voter_should_work() { #[test] fn retracting_inactive_voter_with_other_candidates_in_slots_should_work() { - with_externalities(&mut ExtBuilder::default().voter_bond(2).build(), || { + ExtBuilder::default().voter_bond(2).build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -605,7 +604,7 @@ fn retracting_inactive_voter_with_other_candidates_in_slots_should_work() { #[test] fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -634,7 +633,7 @@ fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { #[test] fn retracting_inactive_voter_with_bad_target_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -663,7 +662,7 @@ fn retracting_inactive_voter_with_bad_target_index_should_not_work() { #[test] fn retracting_active_voter_should_slash_reporter() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); @@ -711,7 +710,7 @@ fn retracting_active_voter_should_slash_reporter() { #[test] fn retracting_inactive_voter_by_nonvoter_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -740,7 +739,7 @@ fn retracting_inactive_voter_by_nonvoter_should_not_work() { #[test] fn candidacy_simple_candidate_submission_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), Vec::::new()); assert_eq!(Elections::candidate_reg_info(1), None); @@ -768,7 +767,7 @@ fn candidacy_simple_candidate_submission_should_work() { fn candidacy_submission_using_free_slot_should_work() { let mut t = new_test_ext_with_candidate_holes(); - with_externalities(&mut t, || { + t.execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), vec![0, 0, 1]); @@ -784,7 +783,7 @@ fn candidacy_submission_using_free_slot_should_work() { fn candidacy_submission_using_alternative_free_slot_should_work() { let mut t = new_test_ext_with_candidate_holes(); - with_externalities(&mut t, || { + t.execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), vec![0, 0, 1]); @@ -800,7 +799,7 @@ fn candidacy_submission_using_alternative_free_slot_should_work() { fn candidacy_submission_not_using_free_slot_should_not_work() { let mut t = new_test_ext_with_candidate_holes(); - with_externalities(&mut t, || { + t.execute_with(|| { System::set_block_number(1); assert_noop!( Elections::submit_candidacy(Origin::signed(4), 3), @@ -811,7 +810,7 @@ fn candidacy_submission_not_using_free_slot_should_not_work() { #[test] fn candidacy_bad_candidate_slot_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), Vec::::new()); assert_noop!( @@ -823,7 +822,7 @@ fn candidacy_bad_candidate_slot_submission_should_not_work() { #[test] fn candidacy_non_free_candidate_slot_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), Vec::::new()); assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); @@ -837,7 +836,7 @@ fn candidacy_non_free_candidate_slot_submission_should_not_work() { #[test] fn candidacy_dupe_candidate_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), Vec::::new()); assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); @@ -851,7 +850,7 @@ fn candidacy_dupe_candidate_submission_should_not_work() { #[test] fn candidacy_poor_candidate_submission_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_eq!(Elections::candidates(), Vec::::new()); assert_noop!( @@ -863,7 +862,7 @@ fn candidacy_poor_candidate_submission_should_not_work() { #[test] fn election_voting_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); @@ -892,7 +891,7 @@ fn election_voting_should_work() { #[test] fn election_proxy_voting_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); @@ -933,7 +932,7 @@ fn election_proxy_voting_should_work() { #[test] fn election_simple_tally_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); @@ -972,7 +971,7 @@ fn election_simple_tally_should_work() { #[test] fn election_seats_should_be_released() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); @@ -1006,7 +1005,7 @@ fn election_seats_should_be_released() { #[test] fn election_presentations_with_zero_staked_deposit_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -1022,7 +1021,7 @@ fn election_presentations_with_zero_staked_deposit_should_not_work() { #[test] fn election_double_presentations_should_be_punished() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert!(Balances::can_slash(&4, 10)); System::set_block_number(4); @@ -1045,7 +1044,7 @@ fn election_double_presentations_should_be_punished() { #[test] fn election_presenting_for_double_election_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_eq!(Elections::submit_candidacy(Origin::signed(2), 0), Ok(())); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20)); @@ -1072,7 +1071,7 @@ fn election_presenting_for_double_election_should_not_work() { #[test] fn election_presenting_loser_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60)); @@ -1105,7 +1104,7 @@ fn election_presenting_loser_should_not_work() { #[test] fn election_presenting_loser_first_should_not_matter() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60)); @@ -1137,7 +1136,7 @@ fn election_presenting_loser_first_should_not_matter() { #[test] fn election_present_outside_of_presentation_period_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); assert_noop!( @@ -1149,7 +1148,7 @@ fn election_present_outside_of_presentation_period_should_not_work() { #[test] fn election_present_with_invalid_vote_index_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); @@ -1165,33 +1164,35 @@ fn election_present_with_invalid_vote_index_should_not_work() { #[test] fn election_present_when_presenter_is_poor_should_not_work() { let test_present = |p| { - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .voting_fee(5) .voter_bond(2) .bad_presentation_punishment(p) - .build(), - || { - System::set_block_number(4); - let _ = Balances::make_free_balance_be(&1, 15); - assert!(!Elections::presentation_active()); - - assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); // -3 - assert_eq!(Balances::free_balance(&1), 12); - assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 15)); // -2 -5 - 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); - if p > 5 { - assert_noop!(Elections::present_winner( - Origin::signed(1), 1, 10, 0), - "presenter must have sufficient slashable funds" - ); - } else { - assert_ok!(Elections::present_winner(Origin::signed(1), 1, 10, 0)); - } - }); + .build() + .execute_with(|| { + System::set_block_number(4); + let _ = Balances::make_free_balance_be(&1, 15); + assert!(!Elections::presentation_active()); + + // -3 + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + 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); + if p > 5 { + assert_noop!(Elections::present_winner( + Origin::signed(1), 1, 10, 0), + "presenter must have sufficient slashable funds" + ); + } else { + assert_ok!(Elections::present_winner(Origin::signed(1), 1, 10, 0)); + } + }); }; test_present(4); test_present(6); @@ -1199,7 +1200,7 @@ fn election_present_when_presenter_is_poor_should_not_work() { #[test] fn election_invalid_present_tally_should_slash() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); assert_eq!(Balances::total_balance(&4), 40); @@ -1219,7 +1220,7 @@ fn election_invalid_present_tally_should_slash() { #[test] fn election_runners_up_should_be_kept() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); @@ -1280,7 +1281,7 @@ fn election_runners_up_should_be_kept() { #[test] fn election_second_tally_should_use_runners_up() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { System::set_block_number(4); assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60)); @@ -1335,7 +1336,7 @@ fn election_second_tally_should_use_runners_up() { #[test] fn election_loser_candidates_bond_gets_slashed() { - with_externalities(&mut ExtBuilder::default().desired_seats(1).build(), || { + ExtBuilder::default().desired_seats(1).build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); @@ -1374,7 +1375,7 @@ fn election_loser_candidates_bond_gets_slashed() { #[test] fn pot_accumulating_weight_and_decaying_should_work() { - with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { + ExtBuilder::default().balance_factor(10).build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); @@ -1502,7 +1503,7 @@ fn pot_accumulating_weight_and_decaying_should_work() { #[test] fn pot_winning_resets_accumulated_pot() { - with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { + ExtBuilder::default().balance_factor(10).build().execute_with(|| { System::set_block_number(4); assert!(!Elections::presentation_active()); @@ -1564,72 +1565,88 @@ fn pot_winning_resets_accumulated_pot() { #[test] fn pot_resubmitting_approvals_stores_pot() { - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .voter_bond(0) .voting_fee(0) .balance_factor(10) - .build(), - || { System::set_block_number(4); - assert!(!Elections::presentation_active()); + .build() + .execute_with(|| { + System::set_block_number(4); + assert!(!Elections::presentation_active()); - assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Elections::submit_candidacy(Origin::signed(1), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 2)); - assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0, 600)); - assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1, 500)); - assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2, 100)); + assert_ok!( + Elections::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0, 600), + ); + assert_ok!( + Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1, 500), + ); + assert_ok!( + Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2, 100), + ); - assert_ok!(Elections::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); - System::set_block_number(6); - assert!(Elections::presentation_active()); + System::set_block_number(6); + assert!(Elections::presentation_active()); - assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); - assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); - assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); - assert_ok!(Elections::end_block(System::block_number())); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); + assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); - System::set_block_number(12); - assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); - assert_ok!(Elections::retract_voter(Origin::signed(5), 1)); - assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0, 600)); - assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1, 500)); - // give 1 some new high balance - let _ = Balances::make_free_balance_be(&1, 997); - assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 1, 2, 1000)); - assert_eq!(Elections::voter_info(1).unwrap(), - VoterInfo { - stake: 1000, // 997 + 3 which is candidacy bond. - pot: Elections::get_offset(100, 1), - last_active: 1, - last_win: 1, - } - ); - assert_ok!(Elections::end_block(System::block_number())); + System::set_block_number(12); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!( + Elections::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0, 600), + ); + assert_ok!( + Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1, 500), + ); + // give 1 some new high balance + let _ = Balances::make_free_balance_be(&1, 997); + assert_ok!( + Elections::set_approvals(Origin::signed(1), vec![false, false, true], 1, 2, 1000), + ); + assert_eq!(Elections::voter_info(1).unwrap(), + VoterInfo { + stake: 1000, // 997 + 3 which is candidacy bond. + pot: Elections::get_offset(100, 1), + last_active: 1, + last_win: 1, + } + ); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); + assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); - System::set_block_number(14); - assert!(Elections::presentation_active()); - assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); - assert_eq!(Elections::present_winner(Origin::signed(1), 1, 1000 + 96 /* pot */, 1), Ok(())); - assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (500, 5), (600, 6), (1096, 1)])); - assert_ok!(Elections::end_block(System::block_number())); + System::set_block_number(14); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); + assert_eq!( + Elections::present_winner(Origin::signed(1), 1, 1000 + 96 /* pot */, 1), + Ok(()), + ); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (500, 5), (600, 6), (1096, 1)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Elections::members(), vec![(1, 19), (6, 19)]); - }) + assert_eq!(Elections::members(), vec![(1, 19), (6, 19)]); + }) } #[test] fn pot_get_offset_should_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Elections::get_offset(100, 0), 0); assert_eq!(Elections::get_offset(100, 1), 96); assert_eq!(Elections::get_offset(100, 2), 96 + 93); @@ -1653,7 +1670,7 @@ fn pot_get_offset_should_work() { #[test] fn pot_get_offset_with_zero_decay() { - with_externalities(&mut ExtBuilder::default().decay_ratio(0).build(), || { + ExtBuilder::default().decay_ratio(0).build().execute_with(|| { assert_eq!(Elections::get_offset(100, 0), 0); assert_eq!(Elections::get_offset(100, 1), 0); assert_eq!(Elections::get_offset(100, 2), 0); diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index fe16fb7d6eada831c5d973f3cdea1d6a093342bd..fb05e731bdd4dd9428f6199f315149a898495dbe 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -258,13 +258,51 @@ use support::{dispatch::Result, decl_module, decl_storage, decl_event}; use system::{ensure_signed, ensure_root}; use codec::{Encode, Decode}; use sr_primitives::{ - traits::{SignedExtension, Bounded}, weights::{SimpleDispatchInfo, DispatchInfo}, + traits::{SignedExtension, Bounded, SaturatedConversion}, + weights::{SimpleDispatchInfo, DispatchInfo, DispatchClass, ClassifyDispatch, WeighData, Weight}, transaction_validity::{ ValidTransaction, TransactionValidityError, InvalidTransaction, TransactionValidity, }, }; -/// Our module's configuration trait. All our types and consts go in here. If the +// A custom weight calculator tailored for the dispatch call `set_dummy()`. This actually examines +// the arguments and makes a decision based upon them. +// +// The `WeightData` trait has access to the arguments of the dispatch that it wants to assign a +// weight to. Nonetheless, the trait itself can not make any assumptions about what the generic type +// of the arguments (`T`) is. Based on our needs, we could replace `T` with a more concrete type +// while implementing the trait. The `decl_module!` expects whatever implements `WeighData` to +// replace `T` with a tuple of the dispatch arguments. This is exactly how we will craft the +// implementation below. +// +// The rules of `WeightForSetDummy` are as follows: +// - The final weight of each dispatch is calculated as the argument of the call multiplied by the +// parameter given to the `WeightForSetDummy`'s constructor. +// - assigns a dispatch class `operational` if the argument of the call is more than 1000. +struct WeightForSetDummy(BalanceOf); + +impl WeighData<(&BalanceOf,)> for WeightForSetDummy +{ + fn weigh_data(&self, target: (&BalanceOf,)) -> Weight { + let multiplier = self.0; + (*target.0 * multiplier).saturated_into::() + } +} + +impl ClassifyDispatch<(&BalanceOf,)> for WeightForSetDummy { + fn classify_dispatch(&self, target: (&BalanceOf,)) -> DispatchClass { + if *target.0 > >::from(1000u32) { + DispatchClass::Operational + } else { + DispatchClass::Normal + } + } +} + +/// A type alias for the balance type from this module's point of view. +type BalanceOf = ::Balance; + +/// Our module's configuration trait. All our types and constants go in here. If the /// module is dependent on specific other modules, then their configuration traits /// should be added to our implied traits list. /// @@ -280,7 +318,7 @@ decl_storage! { // keep things around between blocks. trait Store for Module as Example { // Any storage declarations of the form: - // `pub? Name get(getter_name)? [config()|config(myname)] [build(|_| {...})] : (= )?;` + // `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). @@ -293,7 +331,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(bar): map T::AccountId => Vec<(T::Balance, u64)>; + // e.g. pub Bar get(fn bar): map T::AccountId => Vec<(T::Balance, u64)>; // // For basic value items, you'll get a type which implements // `support::StorageValue`. For map items, you'll get a type which @@ -302,13 +340,13 @@ decl_storage! { // If they have a getter (`get(getter_name)`), then your module will come // equipped with `fn getter_name() -> Type` for basic value items or // `fn getter_name(key: KeyType) -> ValueType` for map items. - Dummy get(dummy) config(): Option; + Dummy get(fn dummy) config(): Option; // A map that has enumerable entries. - Bar get(bar) config(): linked_map T::AccountId => T::Balance; + Bar get(fn bar) config(): linked_map T::AccountId => T::Balance; // this one uses the default, we'll demonstrate the usage of 'mutate' API. - Foo get(foo) config(): T::Balance; + Foo get(fn foo) config(): T::Balance; } } @@ -411,9 +449,8 @@ decl_module! { // The _right-hand-side_ value of the `#[weight]` attribute can be any type that implements // a set of traits, namely [`WeighData`] and [`ClassifyDispatch`]. The former conveys the // weight (a numeric representation of pure execution time and difficulty) of the - // transaction and the latter demonstrates the `DispatchClass` of the call. A higher weight - // means a larger transaction (less of which can be placed in a single block). See the - // `CheckWeight` signed extension struct in the `system` module for more information. + // transaction and the latter demonstrates the [`DispatchClass`] of the call. A higher + // weight means a larger transaction (less of which can be placed in a single block). #[weight = SimpleDispatchInfo::FixedNormal(10_000)] fn accumulate_dummy(origin, increase_by: T::Balance) -> Result { // This is a public call, so we ensure that the origin is some signed account. @@ -454,6 +491,7 @@ decl_module! { // without worrying about gameability or attack scenarios. // If you not specify `Result` explicitly as return value, it will be added automatically // for you and `Ok(())` will be returned. + #[weight = WeightForSetDummy::(>::from(100u32))] fn set_dummy(origin, #[compact] new_value: T::Balance) { ensure_root(origin)?; // Put the new value into storage. @@ -545,10 +583,9 @@ impl Module { #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct WatchDummy(PhantomData); -#[cfg(feature = "std")] impl rstd::fmt::Debug for WatchDummy { fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { - write!(f, "WatchDummy") + write!(f, "WatchDummy") } } @@ -595,12 +632,12 @@ mod tests { use super::*; use support::{assert_ok, impl_outer_origin, parameter_types}; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; + use primitives::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 sr_primitives::{ - Perbill, traits::{BlakeTwo256, OnInitialize, OnFinalize, IdentityLookup}, testing::Header + Perbill, weights::GetDispatchInfo, testing::Header, + traits::{BlakeTwo256, OnInitialize, OnFinalize, IdentityLookup}, }; impl_outer_origin! { @@ -628,7 +665,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -640,23 +676,17 @@ mod tests { pub const ExistentialDeposit: u64 = 0; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } impl Trait for Test { type Event = (); @@ -665,7 +695,7 @@ mod tests { // This function basically just builds a genesis storage key/value store according to // our desired mockup. - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); // We use default for brevity, but you can configure as desired if needed. balances::GenesisConfig::::default().assimilate_storage(&mut t).unwrap(); @@ -680,7 +710,7 @@ mod tests { #[test] fn it_works_for_optional_value() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // Check that GenesisBuilder works properly. assert_eq!(Example::dummy(), Some(42)); @@ -701,7 +731,7 @@ mod tests { #[test] fn it_works_for_default_value() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Example::foo(), 24); assert_ok!(Example::accumulate_foo(Origin::signed(1), 1)); assert_eq!(Example::foo(), 25); @@ -710,7 +740,7 @@ mod tests { #[test] fn signed_ext_watch_dummy_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let call = >::set_dummy(10); let info = DispatchInfo::default(); @@ -726,4 +756,18 @@ mod tests { ); }) } + + #[test] + fn weights_work() { + // must have a default weight. + let default_call = >::accumulate_dummy(10); + let info = default_call.get_dispatch_info(); + // aka. `let info = as GetDispatchInfo>::get_dispatch_info(&default_call);` + assert_eq!(info.weight, 10_000); + + // must have a custom weight of `100 * arg = 2000` + let custom_call = >::set_dummy(20); + let info = custom_call.get_dispatch_info(); + assert_eq!(info.weight, 2000); + } } diff --git a/srml/executive/Cargo.toml b/srml/executive/Cargo.toml index a87eed40b75dfea6881fcd24b0444b74887ee66c..92191a7aac4db28dc3577a1379a0642afb8c2b00 100644 --- a/srml/executive/Cargo.toml +++ b/srml/executive/Cargo.toml @@ -18,6 +18,7 @@ hex-literal = "0.2.1" primitives = { package = "substrate-primitives", path = "../../core/primitives" } srml-indices = { path = "../indices" } balances = { package = "srml-balances", path = "../balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../transaction-payment" } [features] default = ["std"] diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index ad9cb7bf80742fa00f07384dfd3ca5c3670264b7..927a7fef255818ba4f8ee19fde7b609a2f4015ac 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -293,8 +293,7 @@ where #[cfg(test)] mod tests { use super::*; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; + use primitives::H256; use sr_primitives::{ generic::Era, Perbill, DispatchError, weights::Weight, testing::{Digest, Header, Block}, traits::{Bounded, Header as HeaderT, BlakeTwo256, IdentityLookup, ConvertInto}, @@ -348,7 +347,6 @@ mod tests { type Header = Header; type Event = MetaEvent; type BlockHashCount = BlockHashCount; - type WeightMultiplierUpdate = (); type MaximumBlockWeight = MaximumBlockWeight; type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; @@ -358,23 +356,30 @@ mod tests { pub const ExistentialDeposit: u64 = 0; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 10; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Runtime { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = MetaEvent; - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; + } + + parameter_types! { + pub const TransactionBaseFee: u64 = 10; + pub const TransactionByteFee: u64 = 0; + } + impl transaction_payment::Trait for Runtime { + type Currency = Balances; + type OnTransactionPayment = (); type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); } impl ValidateUnsigned for Runtime { @@ -392,7 +397,7 @@ mod tests { system::CheckEra, system::CheckNonce, system::CheckWeight, - balances::TakeFees + transaction_payment::ChargeTransactionPayment ); type TestXt = sr_primitives::testing::TestXt; type Executive = super::Executive, system::ChainContext, Runtime, ()>; @@ -402,7 +407,7 @@ mod tests { system::CheckEra::from(Era::Immortal), system::CheckNonce::from(nonce), system::CheckWeight::new(), - balances::TakeFees::from(fee) + transaction_payment::ChargeTransactionPayment::from(fee) ) } @@ -419,8 +424,8 @@ mod tests { }.assimilate_storage(&mut t).unwrap(); let xt = sr_primitives::testing::TestXt(sign_extra(1, 0, 0), Call::Balances(BalancesCall::transfer(2, 69))); let weight = xt.get_dispatch_info().weight as u64; - let mut t = runtime_io::TestExternalities::::new(t); - with_externalities(&mut t, || { + let mut t = runtime_io::TestExternalities::new(t); + t.execute_with(|| { Executive::initialize_block(&Header::new( 1, H256::default(), @@ -435,7 +440,7 @@ mod tests { }); } - fn new_test_ext(balance_factor: u64) -> runtime_io::TestExternalities { + fn new_test_ext(balance_factor: u64) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)], @@ -446,12 +451,12 @@ mod tests { #[test] fn block_import_works() { - with_externalities(&mut new_test_ext(1), || { + new_test_ext(1).execute_with(|| { Executive::execute_block(Block { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("3e51b47b6cc8449eece93eee4b01f03b00a0ca7981c0b6c0447b6e0d50ca886d").into(), + state_root: hex!("f0d1d66255c2e5b40580eb8b93ddbe732491478487f85e358e1d167d369e398e").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, @@ -463,7 +468,7 @@ mod tests { #[test] #[should_panic] fn block_import_of_bad_state_root_fails() { - with_externalities(&mut new_test_ext(1), || { + new_test_ext(1).execute_with(|| { Executive::execute_block(Block { header: Header { parent_hash: [69u8; 32].into(), @@ -480,7 +485,7 @@ mod tests { #[test] #[should_panic] fn block_import_of_bad_extrinsic_root_fails() { - with_externalities(&mut new_test_ext(1), || { + new_test_ext(1).execute_with(|| { Executive::execute_block(Block { header: Header { parent_hash: [69u8; 32].into(), @@ -499,7 +504,7 @@ mod tests { let mut t = new_test_ext(1); // bad nonce check! let xt = sr_primitives::testing::TestXt(sign_extra(1, 30, 0), Call::Balances(BalancesCall::transfer(33, 69))); - with_externalities(&mut t, || { + t.execute_with(|| { Executive::initialize_block(&Header::new( 1, H256::default(), @@ -521,7 +526,7 @@ mod tests { let encoded_len = encoded.len() as Weight; let limit = AvailableBlockRatio::get() * MaximumBlockWeight::get(); let num_to_exhaust_block = limit / encoded_len; - with_externalities(&mut t, || { + t.execute_with(|| { Executive::initialize_block(&Header::new( 1, H256::default(), @@ -557,7 +562,7 @@ mod tests { let x2 = sr_primitives::testing::TestXt(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); - with_externalities(&mut t, || { + t.execute_with(|| { assert_eq!(>::all_extrinsics_weight(), 0); assert_eq!(>::all_extrinsics_weight(), 0); @@ -566,7 +571,7 @@ mod tests { assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); // default weight for `TestXt` == encoded length. - assert_eq!(>::all_extrinsics_weight(), (3 * len).into()); + assert_eq!(>::all_extrinsics_weight(), (3 * len) as u32); assert_eq!(>::all_extrinsics_len(), 3 * len); let _ = >::finalize(); @@ -581,7 +586,7 @@ mod tests { let xt = sr_primitives::testing::TestXt(None, Call::Balances(BalancesCall::set_balance(33, 69, 69))); let mut t = new_test_ext(1); - with_externalities(&mut t, || { + t.execute_with(|| { assert_eq!(Executive::validate_transaction(xt.clone()), Ok(Default::default())); assert_eq!( Executive::apply_extrinsic(xt), @@ -599,7 +604,7 @@ mod tests { let id: LockIdentifier = *b"0 "; let execute_with_lock = |lock: WithdrawReasons| { let mut t = new_test_ext(1); - with_externalities(&mut t, || { + t.execute_with(|| { as LockableCurrency>::set_lock( id, &1, diff --git a/srml/finality-tracker/Cargo.toml b/srml/finality-tracker/Cargo.toml index 7e3439e0fecea9045761cf9bc1101e889d2cd9ff..a881b2906f0458cce7de49342012a5b80497f7d2 100644 --- a/srml/finality-tracker/Cargo.toml +++ b/srml/finality-tracker/Cargo.toml @@ -12,7 +12,7 @@ rstd = { package = "sr-std", path = "../../core/sr-std", default-features = fals sr-primitives = { path = "../../core/sr-primitives", default-features = false } support = { package = "srml-support", path = "../support", default-features = false } srml-system = { path = "../system", default-features = false } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [dev-dependencies] primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index 1bf9754b0a583bb428bdea6361645aa5fb45f609..5e973cd43235d3002e89b8badfe1c7cee38e5bd3 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -96,17 +96,17 @@ pub trait Trait: SystemTrait { decl_storage! { trait Store for Module as Timestamp { /// Recent hints. - RecentHints get(recent_hints) build(|_| vec![T::BlockNumber::zero()]): Vec; + RecentHints get(fn recent_hints) build(|_| vec![T::BlockNumber::zero()]): Vec; /// Ordered recent hints. - OrderedHints get(ordered_hints) build(|_| vec![T::BlockNumber::zero()]): Vec; + OrderedHints get(fn ordered_hints) build(|_| vec![T::BlockNumber::zero()]): Vec; /// The median. - Median get(median) build(|_| T::BlockNumber::zero()): T::BlockNumber; + Median get(fn median) build(|_| T::BlockNumber::zero()): T::BlockNumber; /// Final hint to apply in the block. `None` means "same as parent". Update: Option; // when initialized through config this is set in the beginning. - Initialized get(initialized) build(|_| false): bool; + Initialized get(fn initialized) build(|_| false): bool; } } @@ -247,11 +247,12 @@ impl ProvideInherent for Module { mod tests { use super::*; - use runtime_io::{with_externalities, TestExternalities}; + use runtime_io::TestExternalities; use primitives::H256; - use sr_primitives::traits::{BlakeTwo256, IdentityLookup, OnFinalize, Header as HeaderT}; - use sr_primitives::testing::Header; - use sr_primitives::Perbill; + use sr_primitives::{ + testing::Header, Perbill, + traits::{BlakeTwo256, IdentityLookup, OnFinalize, Header as HeaderT}, + }; use support::{assert_ok, impl_outer_origin, parameter_types}; use srml_system as system; use std::cell::RefCell; @@ -297,7 +298,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -321,7 +321,7 @@ mod tests { #[test] fn median_works() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - with_externalities(&mut TestExternalities::new(t), || { + TestExternalities::new(t).execute_with(|| { FinalityTracker::update_hint(Some(500)); assert_eq!(FinalityTracker::median(), 250); assert!(NOTIFICATIONS.with(|n| n.borrow().is_empty())); @@ -331,7 +331,7 @@ mod tests { #[test] fn notifies_when_stalled() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - with_externalities(&mut TestExternalities::new(t), || { + TestExternalities::new(t).execute_with(|| { let mut parent_hash = System::parent_hash(); for i in 2..106 { System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); @@ -350,7 +350,7 @@ mod tests { #[test] fn recent_notifications_prevent_stalling() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - with_externalities(&mut TestExternalities::new(t), || { + TestExternalities::new(t).execute_with(|| { let mut parent_hash = System::parent_hash(); for i in 2..106 { System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); diff --git a/srml/generic-asset/src/lib.rs b/srml/generic-asset/src/lib.rs index 6791ee578526380b4444b1a4c05a7c76a37d60c3..38bff08e12d0baec875a0d951dbc4ac5891e6ebd 100644 --- a/srml/generic-asset/src/lib.rs +++ b/srml/generic-asset/src/lib.rs @@ -128,7 +128,7 @@ //! T::Currency::withdraw( //! transactor, //! amount, -//! WithdrawReason::TransactionPayment, +//! WithdrawReason::TransactionPayment.into(), //! ExistenceRequirement::KeepAlive, //! )?; //! // ... @@ -153,12 +153,13 @@ use codec::{Decode, Encode, HasCompact, Input, Output, Error}; +use sr_primitives::RuntimeDebug; use sr_primitives::traits::{ - CheckedAdd, CheckedSub, MaybeSerializeDebug, Member, One, Saturating, SimpleArithmetic, Zero, Bounded + CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Saturating, SimpleArithmetic, Zero, Bounded }; use rstd::prelude::*; -use rstd::{cmp, result}; +use rstd::{cmp, result, fmt::Debug}; use support::dispatch::Result; use support::{ decl_event, decl_module, decl_storage, ensure, @@ -181,7 +182,8 @@ pub trait Trait: system::Trait { + SimpleArithmetic + Default + Copy - + MaybeSerializeDebug; + + MaybeSerializeDeserialize + + Debug; type AssetId: Parameter + Member + SimpleArithmetic + Default + Copy; type Event: From> + Into<::Event>; } @@ -192,7 +194,8 @@ pub trait Subtrait: system::Trait { + SimpleArithmetic + Default + Copy - + MaybeSerializeDebug; + + MaybeSerializeDeserialize + + Debug; type AssetId: Parameter + Member + SimpleArithmetic + Default + Copy; } @@ -202,8 +205,7 @@ impl Subtrait for T { } /// Asset creation options. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub struct AssetOptions { /// Initial issuance of this asset. All deposit to the creater of the asset. #[codec(compact)] @@ -213,8 +215,7 @@ pub struct AssetOptions { } /// Owner of an asset. -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub enum Owner { /// No owner. None, @@ -229,8 +230,7 @@ impl Default for Owner { } /// Asset permissions -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub struct PermissionsV1 { /// Who have permission to update asset permission pub update: Owner, @@ -240,16 +240,14 @@ pub struct PermissionsV1 { pub burn: Owner, } -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] #[repr(u8)] enum PermissionVersionNumber { V1 = 0, } /// Versioned asset permission -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, RuntimeDebug)] pub enum PermissionVersions { V1(PermissionsV1), } @@ -435,8 +433,7 @@ decl_module! { } } -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct BalanceLock { pub id: LockIdentifier, pub amount: Balance, @@ -447,7 +444,7 @@ pub struct BalanceLock { decl_storage! { trait Store for Module as GenericAsset { /// Total issuance of a given asset. - pub TotalIssuance get(total_issuance) build(|config: &GenesisConfig| { + 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; @@ -459,19 +456,19 @@ decl_storage! { pub ReservedBalance: double_map T::AssetId, twox_128(T::AccountId) => T::Balance; /// Next available ID for user-created asset. - pub NextAssetId get(next_asset_id) config(): T::AssetId; + pub NextAssetId get(fn next_asset_id) config(): T::AssetId; /// Permission options for a given asset. - pub Permissions get(get_permission): map T::AssetId => PermissionVersions; + pub Permissions get(fn get_permission): map T::AssetId => PermissionVersions; /// Any liquidity locks on some account balances. - pub Locks get(locks): map T::AccountId => Vec>; + pub Locks get(fn locks): map T::AccountId => Vec>; /// The identity of the asset which is the one that is designated for the chain's staking system. - pub StakingAssetId get(staking_asset_id) config(): T::AssetId; + pub StakingAssetId get(fn staking_asset_id) config(): T::AssetId; /// The identity of the asset which is the one that is designated for paying the chain's transaction fee. - pub SpendingAssetId get(spending_asset_id) config(): T::AssetId; + pub SpendingAssetId get(fn spending_asset_id) config(): T::AssetId; } add_extra_genesis { config(assets): Vec; @@ -566,11 +563,16 @@ impl Module { /// Transfer some liquid free balance from one account to another. /// This will not emit the `Transferred` event. - pub fn make_transfer(asset_id: &T::AssetId, from: &T::AccountId, to: &T::AccountId, amount: T::Balance) -> Result { + pub fn make_transfer( + asset_id: &T::AssetId, + from: &T::AccountId, + to: &T::AccountId, + amount: T::Balance + ) -> Result { let new_balance = Self::free_balance(asset_id, from) .checked_sub(&amount) .ok_or_else(|| "balance too low to send amount")?; - Self::ensure_can_withdraw(asset_id, from, amount, WithdrawReason::Transfer, new_balance)?; + Self::ensure_can_withdraw(asset_id, from, amount, WithdrawReason::Transfer.into(), new_balance)?; if from != to { >::mutate(asset_id, from, |balance| *balance -= amount); @@ -737,7 +739,7 @@ impl Module { asset_id: &T::AssetId, who: &T::AccountId, _amount: T::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, new_balance: T::Balance, ) -> Result { if asset_id != &Self::staking_asset_id() { @@ -751,7 +753,7 @@ impl Module { let now = >::block_number(); if Self::locks(who) .into_iter() - .all(|l| now >= l.until || new_balance >= l.amount || !l.reasons.contains(reason)) + .all(|l| now >= l.until || new_balance >= l.amount || !l.reasons.intersects(reasons)) { Ok(()) } else { @@ -1056,7 +1058,6 @@ impl system::Trait for ElevatedTrait { type MaximumBlockWeight = T::MaximumBlockWeight; type MaximumBlockLength = T::MaximumBlockLength; type AvailableBlockRatio = T::AvailableBlockRatio; - type WeightMultiplierUpdate = (); type BlockHashCount = T::BlockHashCount; type Version = T::Version; } @@ -1066,8 +1067,7 @@ impl Trait for ElevatedTrait { type Event = (); } -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct AssetCurrency(rstd::marker::PhantomData, rstd::marker::PhantomData); impl Currency for AssetCurrency @@ -1103,22 +1103,22 @@ where fn ensure_can_withdraw( who: &T::AccountId, amount: Self::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, new_balance: Self::Balance, ) -> Result { - >::ensure_can_withdraw(&U::asset_id(), who, amount, reason, new_balance) + >::ensure_can_withdraw(&U::asset_id(), who, amount, reasons, new_balance) } fn withdraw( who: &T::AccountId, value: Self::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, _: ExistenceRequirement, // no existential deposit policy for generic asset ) -> result::Result { let new_balance = Self::free_balance(who) .checked_sub(&value) .ok_or_else(|| "account has too few funds")?; - Self::ensure_can_withdraw(who, value, reason, new_balance)?; + Self::ensure_can_withdraw(who, value, reasons, new_balance)?; >::set_free_balance(&U::asset_id(), who, new_balance); Ok(NegativeImbalance::new(value)) } @@ -1201,7 +1201,9 @@ where Self::free_balance(who) .checked_sub(&value) .map_or(false, |new_balance| - >::ensure_can_withdraw(&U::asset_id(), who, value, WithdrawReason::Reserve, new_balance).is_ok() + >::ensure_can_withdraw( + &U::asset_id(), who, value, WithdrawReason::Reserve.into(), new_balance + ).is_ok() ) } @@ -1255,7 +1257,7 @@ impl AssetIdProvider for SpendingAssetIdProvider { impl LockableCurrency for AssetCurrency> where T: Trait, - T::Balance: MaybeSerializeDebug, + T::Balance: MaybeSerializeDeserialize + Debug, { type Moment = T::BlockNumber; diff --git a/srml/generic-asset/src/mock.rs b/srml/generic-asset/src/mock.rs index 7e47de0b36537d18076d86461432afe5db8e52a6..57b13760fa03d2ed90d59dd8a33dfb9abbc62b77 100644 --- a/srml/generic-asset/src/mock.rs +++ b/srml/generic-asset/src/mock.rs @@ -25,7 +25,7 @@ use sr_primitives::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; -use primitives::{Blake2Hasher, H256}; +use primitives::H256; use support::{parameter_types, impl_outer_event, impl_outer_origin}; use super::*; @@ -56,7 +56,6 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = TestEvent; - type WeightMultiplierUpdate = (); type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; @@ -118,7 +117,7 @@ impl ExtBuilder { } // builds genesis config - pub fn build(self) -> runtime_io::TestExternalities { + pub fn build(self) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig:: { @@ -137,7 +136,7 @@ impl ExtBuilder { // This function basically just builds a genesis storage key/value store according to // our desired mockup. -pub fn new_test_ext() -> runtime_io::TestExternalities { +pub fn new_test_ext() -> runtime_io::TestExternalities { system::GenesisConfig::default() .build_storage::() .unwrap() diff --git a/srml/generic-asset/src/tests.rs b/srml/generic-asset/src/tests.rs index 685e553c1c14d03687a3595ece8032746f273fea..165936d0215a6e86133f59b2ce652fb830b55f33 100644 --- a/srml/generic-asset/src/tests.rs +++ b/srml/generic-asset/src/tests.rs @@ -22,14 +22,13 @@ use super::*; use crate::mock::{new_test_ext, ExtBuilder, GenericAsset, Origin, System, Test, TestEvent}; -use runtime_io::with_externalities; use support::{assert_noop, assert_ok}; #[test] fn issuing_asset_units_to_issuer_should_work() { let balance = 100; - with_externalities(&mut ExtBuilder::default().free_balance((16000, 1, 100)).build(), || { + ExtBuilder::default().free_balance((16000, 1, 100)).build().execute_with(|| { let default_permission = PermissionLatest { update: Owner::Address(1), mint: Owner::Address(1), @@ -51,61 +50,55 @@ fn issuing_asset_units_to_issuer_should_work() { #[test] fn issuing_with_next_asset_id_overflow_should_not_work() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - NextAssetId::::put(u32::max_value()); - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_noop!( - GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 1, - permissions: default_permission - } - ), - "No new assets id available." - ); - assert_eq!(GenericAsset::next_asset_id(), u32::max_value()); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + NextAssetId::::put(u32::max_value()); + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_noop!( + GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: 1, + permissions: default_permission + } + ), + "No new assets id available." + ); + assert_eq!(GenericAsset::next_asset_id(), u32::max_value()); + }); } #[test] fn querying_total_supply_should_work() { let asset_id = 1000; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 50)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 50); - assert_ok!(GenericAsset::transfer(Origin::signed(2), asset_id, 3, 31)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 19); - assert_eq!(GenericAsset::free_balance(&asset_id, &3), 31); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 1)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: 100, + permissions: default_permission + } + )); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); + assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 50)); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); + assert_eq!(GenericAsset::free_balance(&asset_id, &2), 50); + assert_ok!(GenericAsset::transfer(Origin::signed(2), asset_id, 3, 31)); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); + assert_eq!(GenericAsset::free_balance(&asset_id, &2), 19); + assert_eq!(GenericAsset::free_balance(&asset_id, &3), 31); + assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 1)); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); + }); } // Given @@ -127,27 +120,24 @@ fn querying_total_supply_should_work() { fn transferring_amount_should_work() { let asset_id = 1000; let free_balance = 100; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: free_balance, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), free_balance); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 40)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 60); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 40); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: free_balance, + permissions: default_permission + } + )); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), free_balance); + assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 40)); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 60); + assert_eq!(GenericAsset::free_balance(&asset_id, &2), 40); + }); } // Given @@ -168,55 +158,49 @@ fn transferring_amount_should_work() { #[test] fn transferring_amount_should_fail_when_transferring_more_than_free_balance() { let asset_id = 1000; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 2000), - "balance too low to send amount" - ); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: 100, + permissions: default_permission + } + )); + assert_noop!( + GenericAsset::transfer(Origin::signed(1), asset_id, 2, 2000), + "balance too low to send amount" + ); + }); } #[test] fn transferring_less_than_one_unit_should_not_work() { let asset_id = 1000; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 0), - "cannot transfer zero amount" - ); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: 100, + permissions: default_permission + } + )); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); + assert_noop!( + GenericAsset::transfer(Origin::signed(1), asset_id, 2, 0), + "cannot transfer zero amount" + ); + }); } // Given @@ -233,60 +217,54 @@ fn self_transfer_should_fail() { let asset_id = 1000; let balance = 100; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: balance, - permissions: default_permission - } - )); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: balance, + permissions: default_permission + } + )); - let initial_free_balance = GenericAsset::free_balance(&asset_id, &1); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 10)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), initial_free_balance); - }, - ); + let initial_free_balance = GenericAsset::free_balance(&asset_id, &1); + assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 10)); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), initial_free_balance); + }); } #[test] fn transferring_more_units_than_total_supply_should_not_work() { let asset_id = 1000; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 101), - "balance too low to send amount" - ); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let default_permission = PermissionLatest { + update: Owner::Address(1), + mint: Owner::Address(1), + burn: Owner::Address(1), + }; + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: 100, + permissions: default_permission + } + )); + assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); + assert_noop!( + GenericAsset::transfer(Origin::signed(1), asset_id, 2, 101), + "balance too low to send amount" + ); + }); } // Ensures it uses fake money for staking asset id. #[test] fn staking_asset_id_should_return_0() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(GenericAsset::staking_asset_id(), 16000); }); } @@ -294,7 +272,7 @@ fn staking_asset_id_should_return_0() { // Ensures it uses fake money for spending asset id. #[test] fn spending_asset_id_should_return_10() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(GenericAsset::spending_asset_id(), 16001); }); } @@ -305,7 +283,7 @@ fn spending_asset_id_should_return_10() { // -Â total_balance should return 0 #[test] fn total_balance_should_be_zero() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(GenericAsset::total_balance(&0, &0), 0); }); } @@ -323,19 +301,16 @@ fn total_balance_should_be_equal_to_account_balance() { mint: Owner::Address(1), burn: Owner::Address(1), }; - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::total_balance(&1000, &1), 100); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + assert_ok!(GenericAsset::create( + Origin::signed(1), + AssetOptions { + initial_issuance: 100, + permissions: default_permission + } + )); + assert_eq!(GenericAsset::total_balance(&1000, &1), 100); + }); } // Given @@ -348,7 +323,7 @@ fn total_balance_should_be_equal_to_account_balance() { // -Â free_balance should return 50. #[test] fn free_balance_should_only_return_account_free_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 50)).build(), || { + ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 70); assert_eq!(GenericAsset::free_balance(&1, &0), 50); }); @@ -363,7 +338,7 @@ fn free_balance_should_only_return_account_free_balance() { // -Â total_balance should equals to account balance + free balance. #[test] fn total_balance_should_be_equal_to_sum_of_account_balance_and_free_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 50)).build(), || { + ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 70); assert_eq!(GenericAsset::total_balance(&1, &0), 120); }); @@ -378,7 +353,7 @@ fn total_balance_should_be_equal_to_sum_of_account_balance_and_free_balance() { // - reserved_balance should return 70. #[test] fn reserved_balance_should_only_return_account_reserved_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 50)).build(), || { + ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 70); assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); }); @@ -394,7 +369,7 @@ fn reserved_balance_should_only_return_account_reserved_balance() { // - reserved_balance = amount #[test] fn set_reserved_balance_should_add_balance_as_reserved() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 50); assert_eq!(GenericAsset::reserved_balance(&1, &0), 50); }); @@ -410,7 +385,7 @@ fn set_reserved_balance_should_add_balance_as_reserved() { // - New free_balance should replace older free_balance. #[test] fn set_free_balance_should_add_amount_as_free_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { GenericAsset::set_free_balance(&1, &0, 50); assert_eq!(GenericAsset::free_balance(&1, &0), 50); }); @@ -429,7 +404,7 @@ fn set_free_balance_should_add_amount_as_free_balance() { // - new reserved_balance = original free balance + reserved amount #[test] fn reserve_should_moves_amount_from_balance_to_reserved_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { assert_ok!(GenericAsset::reserve(&1, &0, 70)); assert_eq!(GenericAsset::free_balance(&1, &0), 30); assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); @@ -448,7 +423,7 @@ fn reserve_should_moves_amount_from_balance_to_reserved_balance() { // - Should throw an error. #[test] fn reserve_should_not_moves_amount_from_balance_to_reserved_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { assert_noop!(GenericAsset::reserve(&1, &0, 120), "not enough free funds"); assert_eq!(GenericAsset::free_balance(&1, &0), 100); assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); @@ -466,7 +441,7 @@ fn reserve_should_not_moves_amount_from_balance_to_reserved_balance() { // - unreserved should return 20. #[test] fn unreserve_should_return_substratced_value_from_unreserved_amount_by_actual_acount_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + 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); }); @@ -483,7 +458,7 @@ fn unreserve_should_return_substratced_value_from_unreserved_amount_by_actual_ac // - unreserved should return None. #[test] fn unreserve_should_return_none() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::unreserve(&1, &0, 50), 0); }); @@ -500,7 +475,7 @@ fn unreserve_should_return_none() { // - free_balance should be 200. #[test] fn unreserve_should_increase_free_balance_by_reserved_balance() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); GenericAsset::unreserve(&1, &0, 120); assert_eq!(GenericAsset::free_balance(&1, &0), 200); @@ -518,7 +493,7 @@ fn unreserve_should_increase_free_balance_by_reserved_balance() { // - reserved_balance should be 0. #[test] fn unreserve_should_deduct_reserved_balance_by_reserved_amount() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { GenericAsset::set_free_balance(&1, &0, 100); GenericAsset::unreserve(&1, &0, 120); assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); @@ -536,7 +511,7 @@ fn unreserve_should_deduct_reserved_balance_by_reserved_amount() { // - slash should return None. #[test] fn slash_should_return_slash_reserved_amount() { - with_externalities(&mut ExtBuilder::default().free_balance((1, 0, 100)).build(), || { + ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::slash(&1, &0, 70), None); }); @@ -550,7 +525,7 @@ fn slash_should_return_slash_reserved_amount() { // - Should return slashed_reserved - reserved_balance. #[test] fn slash_reserved_should_deducts_up_to_amount_from_reserved_balance() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::slash_reserved(&1, &0, 150), Some(50)); }); @@ -564,7 +539,7 @@ fn slash_reserved_should_deducts_up_to_amount_from_reserved_balance() { // - Should return None. #[test] fn slash_reserved_should_return_none() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::slash_reserved(&1, &0, 100), None); }); @@ -579,7 +554,7 @@ fn slash_reserved_should_return_none() { // - Should not return None. #[test] fn repatriate_reserved_return_amount_substracted_by_slash_amount() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130), 30); }); @@ -594,7 +569,7 @@ fn repatriate_reserved_return_amount_substracted_by_slash_amount() { // - Should return None. #[test] fn repatriate_reserved_return_none() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90), 0); }); @@ -608,7 +583,7 @@ fn repatriate_reserved_return_none() { // - Should create a new reserved asset. #[test] fn create_reserved_should_create_a_default_account_with_the_balance_given() { - with_externalities(&mut ExtBuilder::default().next_asset_id(10).build(), || { + ExtBuilder::default().next_asset_id(10).build().execute_with(|| { let default_permission = PermissionLatest { update: Owner::Address(1), mint: Owner::Address(1), @@ -643,7 +618,7 @@ fn create_reserved_should_create_a_default_account_with_the_balance_given() { // - Should throw a permission error #[test] fn mint_should_throw_permission_error() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { let origin = 1; let asset_id = 4; let to_account = 2; @@ -666,36 +641,33 @@ fn mint_should_throw_permission_error() { // - Should not change `origins` free_balance. #[test] fn mint_should_increase_asset() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 1000; - let to_account = 2; - let amount = 500; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 1000; + let to_account = 2; + let amount = 500; + let initial_issuance = 100; - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); - assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), amount); + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); - // Origin's free_balance should not change. - assert_eq!(GenericAsset::free_balance(&asset_id, &origin), initial_issuance); - }, - ); + assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); + assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), amount); + + // Origin's free_balance should not change. + assert_eq!(GenericAsset::free_balance(&asset_id, &origin), initial_issuance); + }); } // Given @@ -707,20 +679,17 @@ fn mint_should_increase_asset() { // - Should throw a permission error. #[test] fn burn_should_throw_permission_error() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 4; - let to_account = 2; - let amount = 10; - - assert_noop!( - GenericAsset::burn(Origin::signed(origin), asset_id, to_account, amount), - "The origin does not have permission to burn an asset." - ); - }, - ); + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 4; + let to_account = 2; + let amount = 10; + + assert_noop!( + GenericAsset::burn(Origin::signed(origin), asset_id, to_account, amount), + "The origin does not have permission to burn an asset." + ); + }); } // Given @@ -733,41 +702,38 @@ fn burn_should_throw_permission_error() { // - Should not change `origin`'s free_balance. #[test] fn burn_should_burn_an_asset() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 1000; - let to_account = 2; - let amount = 1000; - let initial_issuance = 100; - let burn_amount = 400; - let expected_amount = 600; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 1000; + let to_account = 2; + let amount = 1000; + let initial_issuance = 100; + let burn_amount = 400; + let expected_amount = 600; - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; - assert_ok!(GenericAsset::burn( - Origin::signed(origin), - asset_id, - to_account, - burn_amount - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), expected_amount); - }, - ); + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); + + assert_ok!(GenericAsset::burn( + Origin::signed(origin), + asset_id, + to_account, + burn_amount + )); + assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), expected_amount); + }); } // Given @@ -779,41 +745,29 @@ fn burn_should_burn_an_asset() { // - The account origin should have burn, mint and update permissions. #[test] fn check_permission_should_return_correct_permission() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 1000; + let initial_issuance = 100; - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::Address(origin), + }; - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn), - true - ); - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint), - true - ); - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update), - true - ); - }, - ); + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + }, + )); + + assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); + assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); + assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update)); + }); } // Given @@ -825,41 +779,29 @@ fn check_permission_should_return_correct_permission() { // - The account origin should not have burn, mint and update permissions. #[test] fn check_permission_should_return_false_for_no_permission() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - }; + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 1000; + let initial_issuance = 100; - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); + let default_permission = PermissionLatest { + update: Owner::None, + mint: Owner::None, + burn: Owner::None, + }; - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn), - false - ); - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint), - false - ); - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update), - false - ); - }, - ); + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + + assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); + assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); + assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update)); + }); } // Given @@ -871,48 +813,39 @@ fn check_permission_should_return_false_for_no_permission() { // - The account origin should have update and mint permissions. #[test] fn update_permission_should_change_permission() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::None, - burn: Owner::None, - }; - - let new_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::None, - }; + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 1000; + let initial_issuance = 100; - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); + let default_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::None, + burn: Owner::None, + }; - assert_ok!(GenericAsset::update_permission( - Origin::signed(origin), - asset_id, - new_permission - )); - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint), - true - ); - assert_eq!( - GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn), - false - ); - }, - ); + let new_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::None, + }; + + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + } + )); + + assert_ok!(GenericAsset::update_permission( + Origin::signed(origin), + asset_id, + new_permission, + )); + assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); + assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); + }); } // Given @@ -923,41 +856,38 @@ fn update_permission_should_change_permission() { // - Should throw an error stating "Origin does not have enough permission to update permissions." #[test] fn update_permission_should_throw_error_when_lack_of_permissions() { - with_externalities( - &mut ExtBuilder::default().free_balance((16000, 1, 100000)).build(), - || { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - }; - - let new_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::None, - }; - - let expected_error_message = "Origin does not have enough permission to update permissions."; + ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { + let origin = 1; + let asset_id = 1000; + let initial_issuance = 100; - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); + let default_permission = PermissionLatest { + update: Owner::None, + mint: Owner::None, + burn: Owner::None, + }; - assert_noop!( - GenericAsset::update_permission(Origin::signed(origin), asset_id, new_permission), - expected_error_message - ); - }, - ); + let new_permission = PermissionLatest { + update: Owner::Address(origin), + mint: Owner::Address(origin), + burn: Owner::None, + }; + + let expected_error_message = "Origin does not have enough permission to update permissions."; + + assert_ok!(GenericAsset::create( + Origin::signed(origin), + AssetOptions { + initial_issuance: initial_issuance, + permissions: default_permission + }, + )); + + assert_noop!( + GenericAsset::update_permission(Origin::signed(origin), asset_id, new_permission), + expected_error_message, + ); + }); } // Given @@ -974,7 +904,7 @@ fn update_permission_should_throw_error_when_lack_of_permissions() { // - Permissions must have burn, mint and updatePermission for the given asset_id. #[test] fn create_asset_works_with_given_asset_id_and_from_account() { - with_externalities(&mut ExtBuilder::default().next_asset_id(10).build(), || { + ExtBuilder::default().next_asset_id(10).build().execute_with(|| { let origin = 1; let from_account: Option<::AccountId> = Some(1); @@ -1011,7 +941,7 @@ fn create_asset_works_with_given_asset_id_and_from_account() { // - `create_asset` should not work. #[test] fn create_asset_with_non_reserved_asset_id_should_not_work() { - with_externalities(&mut ExtBuilder::default().next_asset_id(10).build(), || { + ExtBuilder::default().next_asset_id(10).build().execute_with(|| { let origin = 1; let from_account: Option<::AccountId> = Some(1); @@ -1045,7 +975,7 @@ fn create_asset_with_non_reserved_asset_id_should_not_work() { // - `create_asset` should not work. #[test] fn create_asset_with_a_taken_asset_id_should_not_work() { - with_externalities(&mut ExtBuilder::default().next_asset_id(10).build(), || { + ExtBuilder::default().next_asset_id(10).build().execute_with(|| { let origin = 1; let from_account: Option<::AccountId> = Some(1); @@ -1090,7 +1020,7 @@ fn create_asset_with_a_taken_asset_id_should_not_work() { // - Should create a reserved token. #[test] fn create_asset_should_create_a_reserved_asset_when_from_account_is_none() { - with_externalities(&mut ExtBuilder::default().next_asset_id(10).build(), || { + ExtBuilder::default().next_asset_id(10).build().execute_with(|| { let origin = 1; let from_account: Option<::AccountId> = None; @@ -1133,7 +1063,7 @@ fn create_asset_should_create_a_reserved_asset_when_from_account_is_none() { // - Should not create a `reserved_asset`. #[test] fn create_asset_should_create_a_user_asset() { - with_externalities(&mut ExtBuilder::default().next_asset_id(10).build(), || { + ExtBuilder::default().next_asset_id(10).build().execute_with(|| { let origin = 1; let from_account: Option<::AccountId> = None; @@ -1180,12 +1110,11 @@ fn update_permission_should_raise_event() { burn: Owner::Address(origin), }; - with_externalities( - &mut ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build(), - || { + ExtBuilder::default() + .next_asset_id(asset_id) + .free_balance((staking_asset_id, origin, initial_balance)) + .build() + .execute_with(|| { assert_ok!(GenericAsset::create( Origin::signed(origin), AssetOptions { @@ -1201,9 +1130,11 @@ fn update_permission_should_raise_event() { permissions.clone() )); + let expected_event = TestEvent::generic_asset( + RawEvent::PermissionUpdated(asset_id, permissions.clone()), + ); // Assert - assert!(System::events().iter().any(|record| record.event - == TestEvent::generic_asset(RawEvent::PermissionUpdated(asset_id, permissions.clone())))); + assert!(System::events().iter().any(|record| record.event == expected_event)); }, ); } @@ -1223,27 +1154,26 @@ fn mint_should_raise_event() { let to = 2; let amount = 100; - with_externalities( - &mut ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build(), - || { + ExtBuilder::default() + .next_asset_id(asset_id) + .free_balance((staking_asset_id, origin, initial_balance)) + .build() + .execute_with(|| { assert_ok!(GenericAsset::create( Origin::signed(origin), AssetOptions { initial_issuance: 0, permissions: permissions.clone(), - } + }, )); // Act assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to, amount)); + let expected_event = TestEvent::generic_asset(RawEvent::Minted(asset_id, to, amount)); + // Assert - assert!(System::events() - .iter() - .any(|record| record.event == TestEvent::generic_asset(RawEvent::Minted(asset_id, to, amount)))); + assert!(System::events().iter().any(|record| record.event == expected_event)); }, ); } @@ -1262,27 +1192,26 @@ fn burn_should_raise_event() { }; let amount = 100; - with_externalities( - &mut ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build(), - || { + ExtBuilder::default() + .next_asset_id(asset_id) + .free_balance((staking_asset_id, origin, initial_balance)) + .build() + .execute_with(|| { assert_ok!(GenericAsset::create( Origin::signed(origin), AssetOptions { initial_issuance: amount, permissions: permissions.clone(), - } + }, )); // Act assert_ok!(GenericAsset::burn(Origin::signed(origin), asset_id, origin, amount)); + let expected_event = TestEvent::generic_asset(RawEvent::Burned(asset_id, origin, amount)); + // Assert - assert!(System::events() - .iter() - .any(|record| record.event == TestEvent::generic_asset(RawEvent::Burned(asset_id, origin, amount)))); + assert!(System::events().iter().any(|record| record.event == expected_event)); }, ); } diff --git a/srml/grandpa/src/lib.rs b/srml/grandpa/src/lib.rs index 610bd18fb3f8a3b5fb7219f55a701b13e1f5542b..f3e876f2c4e0e681cac3cc06b0ce5df3c12d48f8 100644 --- a/srml/grandpa/src/lib.rs +++ b/srml/grandpa/src/lib.rs @@ -36,8 +36,7 @@ use support::{ decl_event, decl_storage, decl_module, dispatch::Result, }; use sr_primitives::{ - generic::{DigestItem, OpaqueDigestItemId}, traits::Zero, - Perbill, + generic::{DigestItem, OpaqueDigestItemId}, traits::Zero, Perbill, }; use sr_staking_primitives::{ SessionIndex, @@ -138,26 +137,26 @@ decl_event!( decl_storage! { trait Store for Module as GrandpaFinality { /// The current authority set. - Authorities get(authorities): Vec<(AuthorityId, AuthorityWeight)>; + Authorities get(fn authorities): Vec<(AuthorityId, AuthorityWeight)>; /// State of the current authority set. - State get(state): StoredState = StoredState::Live; + State get(fn state): StoredState = StoredState::Live; /// Pending change: (signaled at, scheduled change). PendingChange: Option>; /// next block number where we can force a change. - NextForced get(next_forced): Option; + NextForced get(fn next_forced): Option; /// `true` if we are currently stalled. - Stalled get(stalled): Option<(T::BlockNumber, T::BlockNumber)>; + Stalled get(fn stalled): Option<(T::BlockNumber, T::BlockNumber)>; /// The number of changes (both in terms of keys and underlying economic responsibilities) /// in the "set" of Grandpa validators from genesis. - CurrentSetId get(current_set_id) build(|_| fg_primitives::SetId::default()): SetId; + 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(session_for_set): map SetId => Option; + SetIdSession get(fn session_for_set): map SetId => Option; } add_extra_genesis { config(authorities): Vec<(AuthorityId, AuthorityWeight)>; @@ -374,6 +373,10 @@ impl Module { } } +impl sr_primitives::BoundToRuntimeAppPublic for Module { + type Public = AuthorityId; +} + impl session::OneSessionHandler for Module where T: session::Trait { diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index af2fedf42f8f953c9526267eee317f1f230660f5..fcacbade20490e19c961ddcfa7d94ba1b216322a 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -21,7 +21,7 @@ use sr_primitives::{Perbill, DigestItem, traits::IdentityLookup, testing::{Header, UintAuthorityId}}; use runtime_io; use support::{impl_outer_origin, impl_outer_event, parameter_types}; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; use codec::{Encode, Decode}; use crate::{AuthorityId, GenesisConfig, Trait, Module, ConsensusLog}; use substrate_finality_grandpa_primitives::GRANDPA_ENGINE_ID; @@ -57,7 +57,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = TestEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -82,7 +81,7 @@ pub fn to_authorities(vec: Vec<(u64, u64)>) -> Vec<(AuthorityId, u64)> { .collect() } -pub fn new_test_ext(authorities: Vec<(u64, u64)>) -> runtime_io::TestExternalities { +pub fn new_test_ext(authorities: Vec<(u64, u64)>) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig { authorities: to_authorities(authorities), diff --git a/srml/grandpa/src/tests.rs b/srml/grandpa/src/tests.rs index 41229a5136165d5f4970ce603153ae7c881cd831..2efeb4b5bf3cfc87f69763ef1e07b5a56b3421f2 100644 --- a/srml/grandpa/src/tests.rs +++ b/srml/grandpa/src/tests.rs @@ -18,9 +18,7 @@ #![cfg(test)] -use sr_primitives::testing::Digest; -use sr_primitives::traits::{Header, OnFinalize}; -use runtime_io::with_externalities; +use sr_primitives::{testing::Digest, traits::{Header, OnFinalize}}; use crate::mock::*; use system::{EventRecord, Phase}; use codec::{Decode, Encode}; @@ -29,7 +27,7 @@ use super::*; #[test] fn authorities_change_logged() { - with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 0, None).unwrap(); @@ -57,7 +55,7 @@ fn authorities_change_logged() { #[test] fn authorities_change_logged_after_delay() { - with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 1, None).unwrap(); Grandpa::on_finalize(1); @@ -90,7 +88,7 @@ fn authorities_change_logged_after_delay() { #[test] fn cannot_schedule_change_when_one_pending() { - with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 1, None).unwrap(); assert!(>::exists()); @@ -133,7 +131,7 @@ fn new_decodes_from_old() { #[test] fn dispatch_forced_change() { - with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); Grandpa::schedule_change( to_authorities(vec![(4, 1), (5, 1), (6, 1)]), @@ -205,7 +203,7 @@ fn dispatch_forced_change() { #[test] fn schedule_pause_only_when_live() { - with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { // we schedule a pause at block 1 with delay of 1 System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); Grandpa::schedule_pause(1).unwrap(); @@ -240,7 +238,7 @@ fn schedule_pause_only_when_live() { #[test] fn schedule_resume_only_when_paused() { - with_externalities(&mut new_test_ext(vec![(1, 1), (2, 1), (3, 1)]), || { + new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| { System::initialize(&1, &Default::default(), &Default::default(), &Default::default()); // the set is currently live, resuming it is an error diff --git a/srml/im-online/Cargo.toml b/srml/im-online/Cargo.toml index b62cc2a8f96158219c9bd2e092e7625e54203a63..4c7ccf1487d9b853495b4339ce1d9c471c310850 100644 --- a/srml/im-online/Cargo.toml +++ b/srml/im-online/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] app-crypto = { package = "substrate-application-crypto", path = "../../core/application-crypto", default-features = false } +authorship = { package = "srml-authorship", path = "../authorship", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } primitives = { package="substrate-primitives", path = "../../core/primitives", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } @@ -24,6 +25,7 @@ offchain = { package = "substrate-offchain", path = "../../core/offchain" } default = ["std", "session/historical"] std = [ "app-crypto/std", + "authorship/std", "codec/std", "primitives/std", "rstd/std", diff --git a/srml/im-online/src/lib.rs b/srml/im-online/src/lib.rs index 84c95fee47fb75a4343ffa51762009bb8aa3c6b4..fa970f4997891b1487a5c6694cdbf97374dcdf63 100644 --- a/srml/im-online/src/lib.rs +++ b/srml/im-online/src/lib.rs @@ -37,7 +37,7 @@ //! //! ### Public Functions //! -//! - `is_online_in_current_session` - True if the validator sent a heartbeat in the current session. +//! - `is_online` - True if the validator sent a heartbeat in the current session. //! //! ## Usage //! @@ -52,7 +52,7 @@ //! pub struct Module for enum Call where origin: T::Origin { //! pub fn is_online(origin, authority_index: u32) -> Result { //! let _sender = ensure_signed(origin)?; -//! let _is_online = >::is_online_in_current_session(authority_index); +//! let _is_online = >::is_online(authority_index); //! Ok(()) //! } //! } @@ -76,9 +76,11 @@ use primitives::offchain::{OpaqueNetworkState, StorageKind}; use rstd::prelude::*; use session::historical::IdentificationTuple; use sr_primitives::{ + RuntimeDebug, traits::{Convert, Member, Printable, Saturating}, Perbill, transaction_validity::{ TransactionValidity, TransactionLongevity, ValidTransaction, InvalidTransaction, + TransactionPriority, }, }; use sr_staking_primitives::{ @@ -86,7 +88,7 @@ use sr_staking_primitives::{ offence::{ReportOffence, Offence, Kind}, }; use support::{ - decl_module, decl_event, decl_storage, print, ensure, Parameter + decl_module, decl_event, decl_storage, print, ensure, Parameter, debug }; use system::ensure_none; use system::offchain::SubmitUnsignedTransaction; @@ -95,12 +97,6 @@ pub mod sr25519 { mod app_sr25519 { use app_crypto::{app_crypto, key_types::IM_ONLINE, sr25519}; app_crypto!(sr25519, IM_ONLINE); - - impl From for sr_primitives::AnySignature { - fn from(sig: Signature) -> Self { - sr25519::Signature::from(sig).into() - } - } } /// An i'm online keypair using sr25519 as its crypto. @@ -118,12 +114,6 @@ pub mod ed25519 { mod app_ed25519 { use app_crypto::{app_crypto, key_types::IM_ONLINE, ed25519}; app_crypto!(ed25519, IM_ONLINE); - - impl From for sr_primitives::AnySignature { - fn from(sig: Signature) -> Self { - ed25519::Signature::from(sig).into() - } - } } /// An i'm online keypair using ed25519 as its crypto. @@ -146,15 +136,14 @@ const DB_KEY: &[u8] = b"srml/im-online-worker-status"; /// 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. -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] struct WorkerStatus { done: bool, gossipping_at: BlockNumber, } /// Error which may occur while executing the off-chain code. -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(RuntimeDebug)] enum OffchainErr { DecodeWorkerStatus, FailedSigning, @@ -176,8 +165,7 @@ impl Printable for OffchainErr { pub type AuthIndex = u32; /// Heartbeat which is sent/received. -#[derive(Encode, Decode, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct Heartbeat where BlockNumber: PartialEq + Eq + Decode + Encode, { @@ -212,24 +200,34 @@ pub trait Trait: system::Trait + session::historical::Trait { decl_event!( pub enum Event where ::AuthorityId, + IdentificationTuple = IdentificationTuple, { /// A new heartbeat was received from `AuthorityId` HeartbeatReceived(AuthorityId), + /// At the end of the session, no offence was committed. + AllGood, + /// At the end of the session, at least once validator was found to be offline. + SomeOffline(Vec), } ); decl_storage! { trait Store for Module as ImOnline { /// The block number when we should gossip. - GossipAt get(gossip_at): T::BlockNumber; + GossipAt get(fn gossip_at): T::BlockNumber; /// The current set of keys that may issue a heartbeat. - Keys get(keys): Vec; + Keys get(fn keys): Vec; - /// For each session index we keep a mapping of `AuthorityId` + /// For each session index, we keep a mapping of `AuthIndex` /// to `offchain::OpaqueNetworkState`. - ReceivedHeartbeats get(received_heartbeats): double_map SessionIndex, - blake2_256(AuthIndex) => Vec; + ReceivedHeartbeats get(fn received_heartbeats): double_map SessionIndex, + 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, + blake2_256(T::ValidatorId) => u32; } add_extra_genesis { config(keys): Vec; @@ -256,8 +254,8 @@ decl_module! { &heartbeat.authority_index ); let keys = Keys::::get(); - let public = keys.get(heartbeat.authority_index as usize); - if let (true, Some(public)) = (!exists, public) { + let maybe_public = keys.get(heartbeat.authority_index as usize); + if let (false, Some(public)) = (exists, maybe_public) { let signature_valid = heartbeat.using_encoded(|encoded_heartbeat| { public.verify(&encoded_heartbeat, &signature) }); @@ -280,6 +278,8 @@ decl_module! { // Runs after every block. fn offchain_worker(now: T::BlockNumber) { + debug::RuntimeLogger::init(); + // Only send messages if we are a potential validator. if runtime_io::is_validator() { Self::offchain(now); @@ -288,14 +288,63 @@ decl_module! { } } +/// Keep track of number of authored blocks per authority, uncles are counted as +/// well since they're a valid proof of onlineness. +impl authorship::EventHandler for Module { + fn note_author(author: T::ValidatorId) { + Self::note_authorship(author); + } + + fn note_uncle(author: T::ValidatorId, _age: T::BlockNumber) { + Self::note_authorship(author); + } +} + impl Module { + /// Returns `true` if a heartbeat has been received for the authority at + /// `authority_index` in the authorities series or if the authority has + /// authored at least one block, during the current session. Otherwise + /// `false`. + pub fn is_online(authority_index: AuthIndex) -> bool { + let current_validators = >::validators(); + + if authority_index >= current_validators.len() as u32 { + return false; + } + + let authority = ¤t_validators[authority_index as usize]; + + Self::is_online_aux(authority_index, authority) + } + + fn is_online_aux(authority_index: AuthIndex, authority: &T::ValidatorId) -> bool { + let current_session = >::current_index(); + + ::exists(¤t_session, &authority_index) || + >::get( + ¤t_session, + authority, + ) != 0 + } + /// Returns `true` if a heartbeat has been received for the authority at `authority_index` in /// the authorities series, during the current session. Otherwise `false`. - pub fn is_online_in_current_session(authority_index: AuthIndex) -> bool { + pub fn received_heartbeat_in_current_session(authority_index: AuthIndex) -> bool { let current_session = >::current_index(); ::exists(¤t_session, &authority_index) } + /// Note that the given authority has authored a block in the current session. + fn note_authorship(author: T::ValidatorId) { + let current_session = >::current_index(); + + >::mutate( + ¤t_session, + author, + |authored| *authored += 1, + ); + } + pub(crate) fn offchain(now: T::BlockNumber) { let next_gossip = >::get(); let check = Self::check_not_yet_gossipped(now, next_gossip); @@ -319,6 +368,14 @@ impl Module { Ok(_) => {}, Err(err) => print(err), } + } else { + debug::native::trace!( + target: "imonline", + "Skipping gossip at: {:?} >= {:?} || {:?}", + next_gossip, + now, + if not_yet_gossipped { "not gossipped" } else { "gossipped" } + ); } } @@ -346,6 +403,13 @@ impl Module { 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 + ); T::SubmitTransaction::submit_unsigned(call) .map_err(|_| OffchainErr::SubmitTransaction)?; @@ -423,8 +487,11 @@ impl Module { } } -impl session::OneSessionHandler for Module { +impl sr_primitives::BoundToRuntimeAppPublic for Module { + type Public = T::AuthorityId; +} +impl session::OneSessionHandler for Module { type Key = T::AuthorityId; fn on_genesis_session<'a, I: 'a>(validators: I) @@ -445,46 +512,32 @@ impl session::OneSessionHandler for Module { } fn on_before_session_ending() { - let mut unresponsive = vec![]; - - let current_session = >::current_index(); - + let session_index = >::current_index(); let keys = Keys::::get(); let current_validators = >::validators(); - for (auth_idx, validator_id) in current_validators.into_iter().enumerate() { - let auth_idx = auth_idx as u32; - let exists = ::exists(¤t_session, &auth_idx); - if !exists { - let full_identification = T::FullIdentificationOf::convert(validator_id.clone()) - .expect( - "we got the validator_id from current_validators; - current_validators is set of currently acting validators; - the mapping between the validator id and its full identification should be valid; - thus `FullIdentificationOf::convert` can't return `None`; - qed", - ); - - unresponsive.push((validator_id, full_identification)); - } - } - - if unresponsive.is_empty() { - return; - } + let offenders = current_validators.into_iter().enumerate() + .filter(|(index, id)| + !Self::is_online_aux(*index as u32, id) + ).filter_map(|(_, id)| + T::FullIdentificationOf::convert(id.clone()).map(|full_id| (id, full_id)) + ).collect::>>(); - let validator_set_count = keys.len() as u32; - let offence = UnresponsivenessOffence { - session_index: current_session, - validator_set_count, - offenders: unresponsive, - }; + // Remove all received heartbeats and number of authored blocks from the + // current session, they have already been processed and won't be needed + // anymore. + ::remove_prefix(&>::current_index()); + >::remove_prefix(&>::current_index()); - T::ReportUnresponsiveness::report_offence(vec![], offence); + if offenders.is_empty() { + Self::deposit_event(RawEvent::AllGood); + } else { + Self::deposit_event(RawEvent::SomeOffline(offenders.clone())); - // Remove all received heartbeats from the current session, they have - // already been processed and won't be needed anymore. - ::remove_prefix(&>::current_index()); + let validator_set_count = keys.len() as u32; + let offence = UnresponsivenessOffence { session_index, validator_set_count, offenders }; + T::ReportUnresponsiveness::report_offence(vec![], offence); + } } fn on_disabled(_i: usize) { @@ -497,7 +550,7 @@ impl support::unsigned::ValidateUnsigned for Module { fn validate_unsigned(call: &Self::Call) -> TransactionValidity { if let Call::heartbeat(heartbeat, signature) = call { - if >::is_online_in_current_session(heartbeat.authority_index) { + if >::is_online(heartbeat.authority_index) { // we already received a heartbeat for this authority return InvalidTransaction::Stale.into(); } @@ -525,7 +578,7 @@ impl support::unsigned::ValidateUnsigned for Module { } Ok(ValidTransaction { - priority: 0, + priority: TransactionPriority::max_value(), requires: vec![], provides: vec![(current_session, authority_id).encode()], longevity: TransactionLongevity::max_value(), @@ -538,7 +591,8 @@ impl support::unsigned::ValidateUnsigned for Module { } /// An offence that is filed if a validator didn't send a heartbeat message. -#[cfg_attr(feature = "std", derive(Clone, Debug, PartialEq, Eq))] +#[derive(RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Clone, PartialEq, Eq))] pub struct UnresponsivenessOffence { /// The current session index in which we report the unresponsive validators. /// diff --git a/srml/im-online/src/mock.rs b/srml/im-online/src/mock.rs index a7b669ddb8b91290ca466338a1e10c995ccac938..233e055f887f15023b1023c783ff6476c181ddc6 100644 --- a/srml/im-online/src/mock.rs +++ b/srml/im-online/src/mock.rs @@ -25,7 +25,7 @@ use sr_primitives::Perbill; use sr_staking_primitives::{SessionIndex, offence::ReportOffence}; use sr_primitives::testing::{Header, UintAuthorityId, TestXt}; use sr_primitives::traits::{IdentityLookup, BlakeTwo256, ConvertInto}; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; use support::{impl_outer_origin, impl_outer_dispatch, parameter_types}; use {runtime_io, system}; @@ -85,7 +85,7 @@ impl ReportOffence for OffenceHandler { } } -pub fn new_test_ext() -> runtime_io::TestExternalities { +pub fn new_test_ext() -> runtime_io::TestExternalities { let t = system::GenesisConfig::default().build_storage::().unwrap(); t.into() } @@ -111,7 +111,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -146,6 +145,17 @@ impl session::historical::Trait for Runtime { type FullIdentificationOf = ConvertInto; } +parameter_types! { + pub const UncleGenerations: u32 = 5; +} + +impl authorship::Trait for Runtime { + type FindAuthor = (); + type UncleGenerations = UncleGenerations; + type FilterUncle = (); + type EventHandler = ImOnline; +} + impl Trait for Runtime { type AuthorityId = UintAuthorityId; type Event = (); diff --git a/srml/im-online/src/tests.rs b/srml/im-online/src/tests.rs index c6405c34fa645b71f9d84797206734b83d6372c5..57f2e4008b61c98e4a3151703c366addcbac940c 100644 --- a/srml/im-online/src/tests.rs +++ b/srml/im-online/src/tests.rs @@ -21,12 +21,10 @@ use super::*; use crate::mock::*; use offchain::testing::TestOffchainExt; -use primitives::offchain::OpaquePeerId; -use runtime_io::with_externalities; +use primitives::offchain::{OpaquePeerId, OffchainExt}; use support::{dispatch, assert_noop}; use sr_primitives::testing::UintAuthorityId; - #[test] fn test_unresponsiveness_slash_fraction() { // A single case of unresponsiveness is not slashed. @@ -49,7 +47,7 @@ fn test_unresponsiveness_slash_fraction() { #[test] fn should_report_offline_validators() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let block = 1; System::set_block_number(block); @@ -125,7 +123,7 @@ fn heartbeat( #[test] fn should_mark_online_validator_when_heartbeat_is_received() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { advance_session(); // given VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); @@ -136,31 +134,31 @@ fn should_mark_online_validator_when_heartbeat_is_received() { assert_eq!(Session::current_index(), 2); assert_eq!(Session::validators(), vec![1, 2, 3]); - assert!(!ImOnline::is_online_in_current_session(0)); - assert!(!ImOnline::is_online_in_current_session(1)); - assert!(!ImOnline::is_online_in_current_session(2)); + assert!(!ImOnline::is_online(0)); + assert!(!ImOnline::is_online(1)); + assert!(!ImOnline::is_online(2)); // when let _ = heartbeat(1, 2, 0, 1.into()).unwrap(); // then - assert!(ImOnline::is_online_in_current_session(0)); - assert!(!ImOnline::is_online_in_current_session(1)); - assert!(!ImOnline::is_online_in_current_session(2)); + assert!(ImOnline::is_online(0)); + assert!(!ImOnline::is_online(1)); + assert!(!ImOnline::is_online(2)); // and when let _ = heartbeat(1, 2, 2, 3.into()).unwrap(); // then - assert!(ImOnline::is_online_in_current_session(0)); - assert!(!ImOnline::is_online_in_current_session(1)); - assert!(ImOnline::is_online_in_current_session(2)); + assert!(ImOnline::is_online(0)); + assert!(!ImOnline::is_online(1)); + assert!(ImOnline::is_online(2)); }); } #[test] fn late_heartbeat_should_fail() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { advance_session(); // given VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 4, 4, 5, 6])); @@ -181,9 +179,9 @@ fn late_heartbeat_should_fail() { fn should_generate_heartbeats() { let mut ext = new_test_ext(); let (offchain, state) = TestOffchainExt::new(); - ext.set_offchain_externalities(offchain); + ext.register_extension(OffchainExt::new(offchain)); - with_externalities(&mut ext, || { + ext.execute_with(|| { // given let block = 1; System::set_block_number(block); @@ -219,7 +217,7 @@ fn should_generate_heartbeats() { #[test] fn should_cleanup_received_heartbeats_on_session_end() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { advance_session(); VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3])); @@ -235,13 +233,43 @@ fn should_cleanup_received_heartbeats_on_session_end() { let _ = heartbeat(1, 2, 0, 1.into()).unwrap(); // the heartbeat is stored - assert!(!ImOnline::received_heartbeats(&2, &0).is_empty()); + assert!(!ImOnline::received_heartbeats(&2, &0).is_none()); advance_session(); // after the session has ended we have already processed the heartbeat // message, so any messages received on the previous session should have // been pruned. - assert!(ImOnline::received_heartbeats(&2, &0).is_empty()); + assert!(ImOnline::received_heartbeats(&2, &0).is_none()); + }); +} + +#[test] +fn should_mark_online_validator_when_block_is_authored() { + use authorship::EventHandler; + + new_test_ext().execute_with(|| { + advance_session(); + // given + VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6])); + assert_eq!(Session::validators(), Vec::::new()); + // enact the change and buffer another one + advance_session(); + + assert_eq!(Session::current_index(), 2); + assert_eq!(Session::validators(), vec![1, 2, 3]); + + for i in 0..3 { + assert!(!ImOnline::is_online(i)); + } + + // when + ImOnline::note_author(1); + ImOnline::note_uncle(2, 0); + + // then + assert!(ImOnline::is_online(0)); + assert!(ImOnline::is_online(1)); + assert!(!ImOnline::is_online(2)); }); } diff --git a/srml/indices/src/address.rs b/srml/indices/src/address.rs index caef4728fbf3fdb245fd5e099708604c31889bed..21ee654cf2e3f362182a99f12bb8ca8f4cc60f10 100644 --- a/srml/indices/src/address.rs +++ b/srml/indices/src/address.rs @@ -24,8 +24,8 @@ use codec::{Encode, Decode, Input, Output, Error}; /// An indices-aware address, which can be either a direct `AccountId` or /// an index. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug, Hash))] +#[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] pub enum Address where AccountId: Member, AccountIndex: Member, diff --git a/srml/indices/src/lib.rs b/srml/indices/src/lib.rs index be5016c40ef08baeb5ec68bcf16e9eb184966182..31aa57276bde95444d9320a09639480b560da3ec 100644 --- a/srml/indices/src/lib.rs +++ b/srml/indices/src/lib.rs @@ -93,12 +93,12 @@ decl_event!( decl_storage! { trait Store for Module as Indices { /// The next free enumeration set. - pub NextEnumSet get(next_enum_set) build(|config: &GenesisConfig| { + 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(enum_set) build(|config: &GenesisConfig| { + 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(), diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index 65a0193b5af4902ee40794d642cbbf1c63257d7b..427fd87c47e7d14d9383e9708ea96f8869891649 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -22,7 +22,7 @@ use std::collections::HashSet; use ref_thread_local::{ref_thread_local, RefThreadLocal}; use sr_primitives::testing::Header; use sr_primitives::Perbill; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; use support::{impl_outer_origin, parameter_types}; use {runtime_io, system}; use crate::{GenesisConfig, Module, Trait, IsDeadAccount, OnNewAccount, ResolveHint}; @@ -81,7 +81,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = Indices; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -96,7 +95,7 @@ impl Trait for Runtime { type Event = (); } -pub fn new_test_ext() -> runtime_io::TestExternalities { +pub fn new_test_ext() -> runtime_io::TestExternalities { { let mut h = ALIVE.borrow_mut(); h.clear(); diff --git a/srml/indices/src/tests.rs b/srml/indices/src/tests.rs index 7b60e305278c58be97d156b0bbffc892afe7b750..3bcf0157130ce9ad10a5ed0fd8396359bb658814 100644 --- a/srml/indices/src/tests.rs +++ b/srml/indices/src/tests.rs @@ -20,61 +20,48 @@ use super::*; use crate::mock::{Indices, new_test_ext, make_account, kill_account, TestIsDeadAccount}; -use runtime_io::with_externalities; #[test] fn indexing_lookup_should_work() { - with_externalities( - &mut new_test_ext(), - || { - 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); - }, - ); + 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); + }); } #[test] fn default_indexing_on_new_accounts_should_work() { - with_externalities( - &mut new_test_ext(), - || { - assert_eq!(Indices::lookup_index(4), None); - make_account(5); - assert_eq!(Indices::lookup_index(4), Some(5)); - }, - ); + new_test_ext().execute_with(|| { + assert_eq!(Indices::lookup_index(4), None); + make_account(5); + assert_eq!(Indices::lookup_index(4), Some(5)); + }); } #[test] fn reclaim_indexing_on_new_accounts_should_work() { - with_externalities( - &mut new_test_ext(), - || { - assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(4), None); + new_test_ext().execute_with(|| { + assert_eq!(Indices::lookup_index(1), Some(2)); + assert_eq!(Indices::lookup_index(4), None); - kill_account(2); // index 1 no longer locked to id 2 + kill_account(2); // index 1 no longer locked to id 2 - make_account(1 + 256); // id 257 takes index 1. - assert_eq!(Indices::lookup_index(1), Some(257)); - }, - ); + make_account(1 + 256); // id 257 takes index 1. + assert_eq!(Indices::lookup_index(1), Some(257)); + }); } #[test] fn alive_account_should_prevent_reclaim() { - with_externalities( - &mut new_test_ext(), - || { - assert!(!TestIsDeadAccount::is_dead_account(&2)); - assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(4), None); + 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); - make_account(1 + 256); // id 257 takes index 1. - assert_eq!(Indices::lookup_index(4), Some(257)); - }, - ); + make_account(1 + 256); // id 257 takes index 1. + assert_eq!(Indices::lookup_index(4), Some(257)); + }); } diff --git a/srml/membership/src/lib.rs b/srml/membership/src/lib.rs index 87e9268bd910e54afb723b9b899cb06ed629186a..6cd2a914e6bffea346b79ad1b3c33b763b0c1e48 100644 --- a/srml/membership/src/lib.rs +++ b/srml/membership/src/lib.rs @@ -57,7 +57,7 @@ pub trait Trait: system::Trait { decl_storage! { trait Store for Module, I: Instance=DefaultInstance> as Membership { /// The current membership, stored as an ordered Vec. - Members get(members): Vec; + Members get(fn members): Vec; } add_extra_genesis { config(members): Vec; @@ -193,13 +193,10 @@ mod tests { use std::cell::RefCell; use support::{assert_ok, assert_noop, impl_outer_origin, parameter_types}; - use runtime_io::with_externalities; - use primitives::{H256, Blake2Hasher}; + use primitives::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried. - use sr_primitives::{ - Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header - }; + use sr_primitives::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; use system::EnsureSignedBy; impl_outer_origin! { @@ -227,7 +224,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -281,7 +277,7 @@ mod tests { // This function basically just builds a genesis storage key/value store according to // our desired mockup. - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); // We use default for brevity, but you can configure as desired if needed. GenesisConfig::{ @@ -293,7 +289,7 @@ mod tests { #[test] fn query_membership_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Membership::members(), vec![10, 20, 30]); assert_eq!(MEMBERS.with(|m| m.borrow().clone()), vec![10, 20, 30]); }); @@ -301,7 +297,7 @@ mod tests { #[test] fn add_member_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Membership::add_member(Origin::signed(5), 15), "bad origin"); assert_noop!(Membership::add_member(Origin::signed(1), 10), "already a member"); assert_ok!(Membership::add_member(Origin::signed(1), 15)); @@ -312,7 +308,7 @@ mod tests { #[test] fn remove_member_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Membership::remove_member(Origin::signed(5), 20), "bad origin"); assert_noop!(Membership::remove_member(Origin::signed(2), 15), "not a member"); assert_ok!(Membership::remove_member(Origin::signed(2), 20)); @@ -323,7 +319,7 @@ mod tests { #[test] fn swap_member_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Membership::swap_member(Origin::signed(5), 10, 25), "bad origin"); assert_noop!(Membership::swap_member(Origin::signed(3), 15, 25), "not a member"); assert_noop!(Membership::swap_member(Origin::signed(3), 10, 30), "already a member"); @@ -337,7 +333,7 @@ mod tests { #[test] fn reset_members_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Membership::reset_members(Origin::signed(1), vec![20, 40, 30]), "bad origin"); assert_ok!(Membership::reset_members(Origin::signed(4), vec![20, 40, 30])); assert_eq!(Membership::members(), vec![20, 30, 40]); diff --git a/srml/metadata/src/lib.rs b/srml/metadata/src/lib.rs index bf7c379000d760cffb39c950c0fc6eace93e2934..d85a6837fc6e015d7d1c2068a685e1eaa9283fea 100644 --- a/srml/metadata/src/lib.rs +++ b/srml/metadata/src/lib.rs @@ -28,6 +28,7 @@ use serde::Serialize; use codec::{Decode, Input, Error}; use codec::{Encode, Output}; use rstd::vec::Vec; +use primitives::RuntimeDebug; #[cfg(feature = "std")] type StringBuf = String; @@ -84,13 +85,12 @@ impl Eq for DecodeDifferent where B: Encode + Eq + PartialEq + 'static, O: Encode + Eq + PartialEq + 'static {} -#[cfg(feature = "std")] -impl std::fmt::Debug for DecodeDifferent +impl rstd::fmt::Debug for DecodeDifferent where - B: std::fmt::Debug + Eq + 'static, - O: std::fmt::Debug + Eq + 'static, + B: rstd::fmt::Debug + Eq + 'static, + O: rstd::fmt::Debug + Eq + 'static, { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { match self { DecodeDifferent::Encode(b) => b.fmt(f), DecodeDifferent::Decoded(o) => o.fmt(f), @@ -114,14 +114,11 @@ impl serde::Serialize for DecodeDifferent pub type DecodeDifferentArray = DecodeDifferent<&'static [B], Vec>; -#[cfg(feature = "std")] -type DecodeDifferentStr = DecodeDifferent<&'static str, StringBuf>; -#[cfg(not(feature = "std"))] type DecodeDifferentStr = DecodeDifferent<&'static str, StringBuf>; /// All the metadata about a function. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct FunctionMetadata { pub name: DecodeDifferentStr, pub arguments: DecodeDifferentArray, @@ -129,8 +126,8 @@ pub struct FunctionMetadata { } /// All the metadata about a function argument. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct FunctionArgumentMetadata { pub name: DecodeDifferentStr, pub ty: DecodeDifferentStr, @@ -154,9 +151,8 @@ impl PartialEq for FnEncode { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for FnEncode { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl rstd::fmt::Debug for FnEncode { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { self.0().fmt(f) } } @@ -169,8 +165,8 @@ impl serde::Serialize for FnEncode { } /// All the metadata about an outer event. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct OuterEventMetadata { pub name: DecodeDifferentStr, pub events: DecodeDifferentArray< @@ -179,9 +175,9 @@ pub struct OuterEventMetadata { >, } -/// All the metadata about a event. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +/// All the metadata about an event. +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct EventMetadata { pub name: DecodeDifferentStr, pub arguments: DecodeDifferentArray<&'static str, StringBuf>, @@ -189,8 +185,8 @@ pub struct EventMetadata { } /// All the metadata about one storage entry. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct StorageEntryMetadata { pub name: DecodeDifferentStr, pub modifier: StorageEntryModifier, @@ -200,8 +196,8 @@ pub struct StorageEntryMetadata { } /// All the metadata about one module constant. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct ModuleConstantMetadata { pub name: DecodeDifferentStr, pub ty: DecodeDifferentStr, @@ -209,6 +205,25 @@ pub struct ModuleConstantMetadata { pub documentation: DecodeDifferentArray<&'static str, StringBuf>, } +/// All the metadata about a module error. +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] +pub struct ErrorMetadata { + pub name: DecodeDifferentStr, + pub documentation: DecodeDifferentArray<&'static str, StringBuf>, +} + +/// All the metadata about errors in a module. +pub trait ModuleErrorMetadata { + fn metadata() -> &'static [ErrorMetadata]; +} + +impl ModuleErrorMetadata for &'static str { + fn metadata() -> &'static [ErrorMetadata] { + &[] + } +} + /// A technical trait to store lazy initiated vec value as static dyn pointer. pub trait DefaultByte: Send + Sync { fn default_byte(&self) -> Vec; @@ -246,16 +261,15 @@ impl serde::Serialize for DefaultByteGetter { } } -#[cfg(feature = "std")] -impl std::fmt::Debug for DefaultByteGetter { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl rstd::fmt::Debug for DefaultByteGetter { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { self.0.default_byte().fmt(f) } } /// Hasher used by storage maps -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub enum StorageHasher { Blake2_128, Blake2_256, @@ -265,8 +279,8 @@ pub enum StorageHasher { } /// A storage entry type. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub enum StorageEntryType { Plain(DecodeDifferentStr), Map { @@ -285,32 +299,32 @@ pub enum StorageEntryType { } /// A storage entry modifier. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub enum StorageEntryModifier { Optional, Default, } /// All metadata of the storage. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct StorageMetadata { /// The common prefix used by all storage entries. pub prefix: DecodeDifferent<&'static str, StringBuf>, pub entries: DecodeDifferent<&'static [StorageEntryMetadata], Vec>, } -#[derive(Eq, Encode, PartialEq)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[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); /// The metadata of a runtime. /// The version ID encoded/decoded through /// the enum nature of `RuntimeMetadata`. -#[derive(Eq, Encode, PartialEq)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Eq, Encode, PartialEq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub enum RuntimeMetadata { /// Unused; enum filler. V0(RuntimeMetadataDeprecated), @@ -326,13 +340,15 @@ pub enum RuntimeMetadata { V5(RuntimeMetadataDeprecated), /// Version 6 for runtime metadata. No longer used. V6(RuntimeMetadataDeprecated), - /// Version 7 for runtime metadata. - V7(RuntimeMetadataV7), + /// Version 7 for runtime metadata. No longer used. + V7(RuntimeMetadataDeprecated), + /// Version 8 for runtime metadata. + V8(RuntimeMetadataV8), } /// Enum that should fail. -#[derive(Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(Debug, Serialize))] +#[derive(Eq, PartialEq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize))] pub enum RuntimeMetadataDeprecated { } impl Encode for RuntimeMetadataDeprecated { @@ -349,24 +365,25 @@ impl Decode for RuntimeMetadataDeprecated { } /// The metadata of a runtime. -#[derive(Eq, Encode, PartialEq)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] -pub struct RuntimeMetadataV7 { +#[derive(Eq, Encode, PartialEq, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] +pub struct RuntimeMetadataV8 { pub modules: DecodeDifferentArray, } /// The latest version of the metadata. -pub type RuntimeMetadataLastVersion = RuntimeMetadataV7; +pub type RuntimeMetadataLastVersion = RuntimeMetadataV8; /// All metadata about an runtime module. -#[derive(Clone, PartialEq, Eq, Encode)] -#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))] +#[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode, Serialize))] pub struct ModuleMetadata { pub name: DecodeDifferentStr, pub storage: Option, StorageMetadata>>, pub calls: ODFnA, pub event: ODFnA, pub constants: DFnA, + pub errors: DFnA, } type ODFnA = Option>; @@ -380,6 +397,6 @@ impl Into for RuntimeMetadataPrefixed { impl Into for RuntimeMetadataLastVersion { fn into(self) -> RuntimeMetadataPrefixed { - RuntimeMetadataPrefixed(META_RESERVED, RuntimeMetadata::V7(self)) + RuntimeMetadataPrefixed(META_RESERVED, RuntimeMetadata::V8(self)) } } diff --git a/srml/nicks/Cargo.toml b/srml/nicks/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4b8fabe9effe55febc9dff3b0360e44f95b03705 --- /dev/null +++ b/srml/nicks/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "srml-nicks" +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"] } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +runtime-io = { package = "sr-io", path = "../../core/sr-io", default-features = false } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +support = { package = "srml-support", path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } + +[dev-dependencies] +primitives = { package = "substrate-primitives", path = "../../core/primitives" } +balances = { package = "srml-balances", path = "../balances", default-features = false } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "rstd/std", + "runtime-io/std", + "sr-primitives/std", + "support/std", + "system/std", +] diff --git a/srml/nicks/src/lib.rs b/srml/nicks/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..6b914ca5dc220c27ea7139a231a4d2d0b2046cf1 --- /dev/null +++ b/srml/nicks/src/lib.rs @@ -0,0 +1,385 @@ +// 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 . + +//! # Nicks Module +//! +//! - [`nicks::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! +//! ## Overview +//! +//! Nicks is a trivial module for keeping track of account names on-chain. It makes no effort to +//! create a name hierarchy, be a DNS replacement or provide reverse lookups. +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! * `set_name` - Set the associated name of an account; a small deposit is reserved if not already +//! taken. +//! * `clear_name` - Remove an account's associated name; the deposit is returned. +//! * `kill_name` - Forcibly remove the associated name; the deposit is lost. +//! +//! [`Call`]: ./enum.Call.html +//! [`Trait`]: ./trait.Trait.html + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use sr_primitives::{ + traits::{StaticLookup, EnsureOrigin, Zero}, weights::SimpleDispatchInfo +}; +use support::{ + decl_module, decl_event, decl_storage, ensure, traits::{ + Currency, ReservableCurrency, OnUnbalanced, Get + }, +}; +use system::{ensure_signed, ensure_root}; + +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; + +pub trait Trait: system::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; + + /// The currency trait. + type Currency: ReservableCurrency; + + /// Reservation fee. + type ReservationFee: Get>; + + /// What to do with slashed funds. + type Slashed: OnUnbalanced>; + + /// The origin which may forcibly set or remove a name. Root can always do this. + type ForceOrigin: EnsureOrigin; + + /// The minimum length a name may be. + type MinLength: Get; + + /// The maximum length a name may be. + type MaxLength: Get; +} + +decl_storage! { + trait Store for Module as Sudo { + /// The lookup table for names. + NameOf: map T::AccountId => Option<(Vec, BalanceOf)>; + } +} + +decl_event!( + pub enum Event where AccountId = ::AccountId, Balance = BalanceOf { + /// A name was set. + NameSet(AccountId), + /// A name was forcibly set. + NameForced(AccountId), + /// A name was changed. + NameChanged(AccountId), + /// A name was cleared, and the given balance returned. + NameCleared(AccountId, Balance), + /// A name was removed and the given balance slashed. + NameKilled(AccountId, Balance), + } +); + +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 { + fn deposit_event() = default; + + /// Reservation fee. + const ReservationFee: BalanceOf = T::ReservationFee::get(); + + /// The minimum length a name may be. + const MinLength: u32 = T::MinLength::get() as u32; + + /// The maximum length a name may be. + const MaxLength: u32 = T::MaxLength::get() as u32; + + /// Set an account's name. The name should be a UTF-8-encoded string by convention, though + /// we don't check it. + /// + /// The name may not be more than `T::MaxLength` bytes, nor less than `T::MinLength` bytes. + /// + /// If the account doesn't already have a name, then a fee of `ReservationFee` is reserved + /// in the account. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// # + /// - O(1). + /// - At most one balance operation. + /// - One storage read/write. + /// - One event. + /// # + #[weight = SimpleDispatchInfo::FixedNormal(50_000)] + fn set_name(origin, name: Vec) { + let sender = ensure_signed(origin)?; + + ensure!(name.len() >= T::MinLength::get(), "Name too short"); + ensure!(name.len() <= T::MaxLength::get(), "Name too long"); + + let deposit = if let Some((_, deposit)) = >::get(&sender) { + Self::deposit_event(RawEvent::NameSet(sender.clone())); + deposit + } else { + let deposit = T::ReservationFee::get(); + T::Currency::reserve(&sender, deposit.clone())?; + Self::deposit_event(RawEvent::NameChanged(sender.clone())); + deposit + }; + + >::insert(&sender, (name, deposit)); + } + + /// Clear an account's name and return the deposit. Fails if the account was not named. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// # + /// - O(1). + /// - One balance operation. + /// - One storage read/write. + /// - One event. + /// # + fn clear_name(origin) { + let sender = ensure_signed(origin)?; + + let deposit = >::take(&sender).ok_or("Not named")?.1; + + let _ = T::Currency::unreserve(&sender, deposit.clone()); + + Self::deposit_event(RawEvent::NameCleared(sender, deposit)); + } + + /// Remove an account's name and take charge of the deposit. + /// + /// Fails if `who` has not been named. The deposit is dealt with through `T::Slashed` + /// imbalance handler. + /// + /// The dispatch origin for this call must be _Root_ or match `T::ForceOrigin`. + /// + /// # + /// - O(1). + /// - One unbalanced handler (probably a balance transfer) + /// - One storage read/write. + /// - One event. + /// # + #[weight = SimpleDispatchInfo::FreeOperational] + fn kill_name(origin, target: ::Source) { + T::ForceOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root) + .map_err(|_| "bad origin")?; + + // Figure out who we're meant to be clearing. + let target = T::Lookup::lookup(target)?; + // Grab their deposit (and check that they have one). + let deposit = >::take(&target).ok_or("Not named")?.1; + // Slash their deposit from them. + T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit.clone()).0); + + Self::deposit_event(RawEvent::NameKilled(target, deposit)); + } + + /// Set a third-party account's name with no deposit. + /// + /// No length checking is done on the name. + /// + /// The dispatch origin for this call must be _Root_ or match `T::ForceOrigin`. + /// + /// # + /// - O(1). + /// - At most one balance operation. + /// - One storage read/write. + /// - One event. + /// # + #[weight = SimpleDispatchInfo::FreeOperational] + fn force_name(origin, target: ::Source, name: Vec) { + T::ForceOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root) + .map_err(|_| "bad origin")?; + + let target = T::Lookup::lookup(target)?; + let deposit = >::get(&target).map(|x| x.1).unwrap_or_else(Zero::zero); + >::insert(&target, (name, deposit)); + + Self::deposit_event(RawEvent::NameForced(target)); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use support::{assert_ok, assert_noop, impl_outer_origin, parameter_types}; + use primitives::H256; + use system::EnsureSignedBy; + // 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 sr_primitives::{ + Perbill, testing::Header, traits::{BlakeTwo256, IdentityLookup}, + }; + + 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: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + impl 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 = (); + } + parameter_types! { + pub const ExistentialDeposit: u64 = 0; + pub const TransferFee: u64 = 0; + pub const CreationFee: u64 = 0; + } + impl 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; + } + parameter_types! { + pub const ReservationFee: u64 = 2; + pub const MinLength: usize = 3; + pub const MaxLength: usize = 16; + pub const One: u64 = 1; + } + impl Trait for Test { + type Event = (); + type Currency = Balances; + type ReservationFee = ReservationFee; + type Slashed = (); + type ForceOrigin = EnsureSignedBy; + type MinLength = MinLength; + type MaxLength = MaxLength; + } + type Balances = balances::Module; + type Nicks = Module; + + // This function basically just builds a genesis storage key/value store according to + // our desired mockup. + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + // We use default for brevity, but you can configure as desired if needed. + balances::GenesisConfig:: { + balances: vec![ + (1, 10), + (2, 10), + ], + vesting: vec![], + }.assimilate_storage(&mut t).unwrap(); + t.into() + } + + #[test] + fn kill_name_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Nicks::set_name(Origin::signed(2), b"Dave".to_vec())); + assert_eq!(Balances::total_balance(&2), 10); + assert_ok!(Nicks::kill_name(Origin::signed(1), 2)); + assert_eq!(Balances::total_balance(&2), 8); + assert_eq!(>::get(2), None); + }); + } + + #[test] + fn force_name_should_work() { + new_test_ext().execute_with(|| { + assert_noop!( + Nicks::set_name(Origin::signed(2), b"Dr. David Brubeck, III".to_vec()), + "Name too long" + ); + + assert_ok!(Nicks::set_name(Origin::signed(2), b"Dave".to_vec())); + 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!(>::get(2).unwrap(), (b"Dr. David Brubeck, III".to_vec(), 2)); + }); + } + + #[test] + 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!(>::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!(>::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); + }); + } + + #[test] + fn error_catching_should_work() { + new_test_ext().execute_with(|| { + assert_noop!(Nicks::clear_name(Origin::signed(1)), "Not named"); + + assert_noop!(Nicks::set_name(Origin::signed(3), b"Dave".to_vec()), "not enough free funds"); + + assert_noop!(Nicks::set_name(Origin::signed(1), b"Ga".to_vec()), "Name too short"); + assert_noop!( + Nicks::set_name(Origin::signed(1), b"Gavin James Wood, Esquire".to_vec()), + "Name too long" + ); + assert_ok!(Nicks::set_name(Origin::signed(1), b"Dave".to_vec())); + assert_noop!(Nicks::kill_name(Origin::signed(2), 1), "bad origin"); + assert_noop!(Nicks::force_name(Origin::signed(2), 1, b"Whatever".to_vec()), "bad origin"); + }); + } +} diff --git a/srml/offences/src/lib.rs b/srml/offences/src/lib.rs index 801b8b5fca90b0d7d8dcf0771ea825291ea08899..a6cf4d795646771e2dc424d2f5cd2190be6eef70 100644 --- a/srml/offences/src/lib.rs +++ b/srml/offences/src/lib.rs @@ -59,7 +59,7 @@ pub trait Trait: system::Trait { decl_storage! { trait Store for Module as Offences { /// The primary structure that holds all offence records keyed by report identifiers. - Reports get(reports): map ReportIdOf => Option>; + Reports get(fn reports): map ReportIdOf => Option>; /// A vector of reports of the same kind that happened at the same time slot. ConcurrentReportsIndex: double_map Kind, blake2_256(OpaqueTimeSlot) => Vec>; diff --git a/srml/offences/src/mock.rs b/srml/offences/src/mock.rs index e7280c34e92431265b60bedda712de52e254679e..f9c79390819e0e83e4cfb48618c271a6150467ea 100644 --- a/srml/offences/src/mock.rs +++ b/srml/offences/src/mock.rs @@ -28,7 +28,7 @@ use sr_staking_primitives::{ }; use sr_primitives::testing::Header; use sr_primitives::traits::{IdentityLookup, BlakeTwo256}; -use substrate_primitives::{H256, Blake2Hasher}; +use substrate_primitives::H256; use support::{impl_outer_origin, impl_outer_event, parameter_types, StorageMap, StorageDoubleMap}; use {runtime_io, system}; @@ -78,7 +78,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = TestEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -103,7 +102,7 @@ impl_outer_event! { } } -pub fn new_test_ext() -> runtime_io::TestExternalities { +pub fn new_test_ext() -> runtime_io::TestExternalities { let t = system::GenesisConfig::default().build_storage::().unwrap(); t.into() } diff --git a/srml/offences/src/tests.rs b/srml/offences/src/tests.rs index 17f933b8e8d12bcd6dc9d8f26613e0cca753fccf..28e655d16bfdd2e93fa69ce211adca389f65bccb 100644 --- a/srml/offences/src/tests.rs +++ b/srml/offences/src/tests.rs @@ -24,11 +24,10 @@ use crate::mock::{ offence_reports, }; use system::{EventRecord, Phase}; -use runtime_io::with_externalities; #[test] fn should_report_an_authority_and_trigger_on_offence() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); @@ -51,7 +50,7 @@ fn should_report_an_authority_and_trigger_on_offence() { #[test] fn should_calculate_the_fraction_correctly() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); @@ -83,7 +82,7 @@ fn should_calculate_the_fraction_correctly() { #[test] fn should_not_report_the_same_authority_twice_in_the_same_slot() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); @@ -113,7 +112,7 @@ fn should_not_report_the_same_authority_twice_in_the_same_slot() { #[test] fn should_report_in_different_time_slot() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); @@ -143,7 +142,7 @@ fn should_report_in_different_time_slot() { #[test] fn should_deposit_event() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); @@ -171,7 +170,7 @@ fn should_deposit_event() { #[test] fn doesnt_deposit_event_for_dups() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); @@ -208,7 +207,7 @@ fn doesnt_deposit_event_for_dups() { fn should_properly_count_offences() { // We report two different authorities for the same issue. Ultimately, the 1st authority // should have `count` equal 2 and the count of the 2nd one should be equal to 1. - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); diff --git a/srml/randomness-collective-flip/Cargo.toml b/srml/randomness-collective-flip/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5be9aad50f0b3003f82265d725e97fdb5cba8a2c --- /dev/null +++ b/srml/randomness-collective-flip/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "srml-randomness-collective-flip" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +safe-mix = { version = "1.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +support = { package = "srml-support", path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } + +[dev-dependencies] +primitives = { package = "substrate-primitives", path = "../../core/primitives" } +runtime-io = { package = "sr-io", path = "../../core/sr-io" } + +[features] +default = ["std"] +std = [ + "safe-mix/std", + "system/std", + "codec/std", + "support/std", + "sr-primitives/std", + "rstd/std", +] diff --git a/srml/randomness-collective-flip/src/lib.rs b/srml/randomness-collective-flip/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..3644b8898c2ab3ebd8646078f39001c75737ec0c --- /dev/null +++ b/srml/randomness-collective-flip/src/lib.rs @@ -0,0 +1,280 @@ +// 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 . + +//! # Randomness Module +//! +//! The Randomness Collective Flip module provides a [`random`](./struct.Module.html#method.random) +//! function that generates low-influence random values based on the block hashes from the previous +//! `81` blocks. Low-influence randomness can be useful when defending against relatively weak +//! adversaries. +//! +//! ## Public Functions +//! +//! See the [`Module`](./struct.Module.html) struct for details of publicly available functions. +//! +//! ## Usage +//! +//! ### Prerequisites +//! +//! Import the Randomness Collective Flip module and derive your module's configuration trait from +//! the system trait. +//! +//! ### Example - Get random seed for the current block +//! +//! ``` +//! use support::{decl_module, dispatch::Result, traits::Randomness}; +//! +//! pub trait Trait: system::Trait {} +//! +//! decl_module! { +//! pub struct Module for enum Call where origin: T::Origin { +//! pub fn random_module_example(origin) -> Result { +//! let _random_seed = >::random_seed(); +//! Ok(()) +//! } +//! } +//! } +//! # fn main() { } +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::{prelude::*, convert::TryInto}; +use sr_primitives::traits::Hash; +use support::{decl_module, decl_storage, traits::Randomness}; +use safe_mix::TripletMix; +use codec::Encode; +use system::Trait; + +const RANDOM_MATERIAL_LEN: u32 = 81; + +fn block_number_to_index(block_number: T::BlockNumber) -> usize { + // on_initialize is called on the first block after genesis + let index = (block_number - 1.into()) % RANDOM_MATERIAL_LEN.into(); + index.try_into().ok().expect("Something % 81 is always smaller than usize; qed") +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn on_initialize(block_number: T::BlockNumber) { + let parent_hash = >::parent_hash(); + + >::mutate(|ref mut values| if values.len() < RANDOM_MATERIAL_LEN as usize { + values.push(parent_hash) + } else { + let index = block_number_to_index::(block_number); + values[index] = parent_hash; + }); + } + } +} + +decl_storage! { + trait Store for Module as RandomnessCollectiveFlip { + /// Series of block headers from the last 81 blocks that acts as random seed material. This + /// is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of + /// the oldest hash. + RandomMaterial get(fn random_material): Vec; + } +} + +impl Randomness for Module { + /// Get a low-influence "random" value. + /// + /// Being a deterministic block chain, 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 + /// different result to other callers of this function; use it like + /// `random(&b"my context"[..])`. This is initially implemented through a low-influence + /// "triplet mix" convolution of previous block hash values. In the future it will be generated + /// from a secure verifiable random function (VRF). + /// + /// ### Security Notes + /// + /// This randomness uses a low-influence function, drawing upon the block hashes from the + /// previous 81 blocks. Its result for any given subject will be known far in advance by anyone + /// observing the chain. Any block producer has significant influence over their block hashes + /// bounded only by their computational resources. Our low-influence function reduces the actual + /// block producer's influence over the randomness, but increases the influence of small + /// colluding groups of recent block producers. + /// + /// Some BABE blocks have VRF outputs where the block producer has exactly one bit of influence, + /// either they make the block or they do not make the block and thus someone else makes the + /// next block. Yet, this randomness is not fresh in all BABE blocks. + /// + /// If that is an insufficient security guarantee then two things can be used to improve this + /// randomness: + /// + /// - Name, in advance, the block number whose random value will be used; ensure your module + /// retains a buffer of previous random values for its subject and then index into these in + /// order to obviate the ability of your user to look up the parent hash and choose when to + /// transact based upon it. + /// - Require your user to first commit to an additional value by first posting its hash. + /// Require them to reveal the value to determine the final result, hashing it with the + /// output of this random function. This reduces the ability of a cabal of block producers + /// from conspiring against individuals. + /// + /// WARNING: Hashing the result of this function will remove any low-influence properties it has + /// and mean that all bits of the resulting value are entirely manipulatable by the author of + /// the parent block, who can determine the value of `parent_hash`. + fn random(subject: &[u8]) -> T::Hash { + let block_number = >::block_number(); + let index = block_number_to_index::(block_number); + + let hash_series = >::get(); + if !hash_series.is_empty() { + // Always the case after block 1 is initialised. + hash_series.iter() + .cycle() + .skip(index) + .take(RANDOM_MATERIAL_LEN as usize) + .enumerate() + .map(|(i, h)| (i as i8, subject, h).using_encoded(T::Hashing::hash)) + .triplet_mix() + } else { + T::Hash::default() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use primitives::H256; + use sr_primitives::{ + Perbill, traits::{BlakeTwo256, OnInitialize, Header as _, IdentityLookup}, testing::Header, + }; + use support::{impl_outer_origin, parameter_types, traits::Randomness}; + + #[derive(Clone, PartialEq, Eq)] + pub struct Test; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = (); + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type AvailableBlockRatio = AvailableBlockRatio; + type MaximumBlockLength = MaximumBlockLength; + type Version = (); + } + + type System = system::Module; + type CollectiveFlip = Module; + + fn new_test_ext() -> runtime_io::TestExternalities { + let t = system::GenesisConfig::default().build_storage::().unwrap(); + t.into() + } + + #[test] + fn test_block_number_to_index() { + for i in 1 .. 1000 { + assert_eq!((i - 1) as usize % 81, block_number_to_index::(i)); + } + } + + fn setup_blocks(blocks: u64) { + let mut parent_hash = System::parent_hash(); + + for i in 1 .. (blocks + 1) { + System::initialize(&i, &parent_hash, &Default::default(), &Default::default()); + CollectiveFlip::on_initialize(i); + + let header = System::finalize(); + parent_hash = header.hash(); + System::set_block_number(*header.number()); + } + } + + #[test] + fn test_random_material_parital() { + new_test_ext().execute_with(|| { + let genesis_hash = System::parent_hash(); + + setup_blocks(38); + + let random_material = CollectiveFlip::random_material(); + + assert_eq!(random_material.len(), 38); + assert_eq!(random_material[0], genesis_hash); + }); + } + + #[test] + fn test_random_material_filled() { + new_test_ext().execute_with(|| { + let genesis_hash = System::parent_hash(); + + setup_blocks(81); + + let random_material = CollectiveFlip::random_material(); + + assert_eq!(random_material.len(), 81); + assert_ne!(random_material[0], random_material[1]); + assert_eq!(random_material[0], genesis_hash); + }); + } + + #[test] + fn test_random_material_filled_twice() { + new_test_ext().execute_with(|| { + let genesis_hash = System::parent_hash(); + + setup_blocks(162); + + let random_material = CollectiveFlip::random_material(); + + assert_eq!(random_material.len(), 81); + assert_ne!(random_material[0], random_material[1]); + assert_ne!(random_material[0], genesis_hash); + }); + } + + #[test] + fn test_random() { + new_test_ext().execute_with(|| { + setup_blocks(162); + + assert_eq!(System::block_number(), 162); + assert_eq!(CollectiveFlip::random_seed(), CollectiveFlip::random_seed()); + assert_ne!(CollectiveFlip::random(b"random_1"), CollectiveFlip::random(b"random_2")); + + let random = CollectiveFlip::random_seed(); + + assert_ne!(random, H256::zero()); + assert!(!CollectiveFlip::random_material().contains(&random)); + }); + } +} diff --git a/srml/scored-pool/src/lib.rs b/srml/scored-pool/src/lib.rs index 4739e165d175243b49620475fbea0a1d58951c6a..5fde1e9c450cf986decd23accb2e47643bd4091a 100644 --- a/srml/scored-pool/src/lib.rs +++ b/srml/scored-pool/src/lib.rs @@ -89,14 +89,17 @@ mod mock; mod tests; use codec::FullCodec; -use rstd::prelude::*; +use rstd::{ + fmt::Debug, + prelude::*, +}; use support::{ - StorageValue, decl_module, decl_storage, decl_event, ensure, + decl_module, decl_storage, decl_event, ensure, traits::{ChangeMembers, InitializeMembers, Currency, Get, ReservableCurrency}, }; use system::{self, ensure_root, ensure_signed}; use sr_primitives::{ - traits::{EnsureOrigin, SimpleArithmetic, MaybeSerializeDebug, Zero, StaticLookup}, + traits::{EnsureOrigin, SimpleArithmetic, MaybeSerializeDeserialize, Zero, StaticLookup}, }; type BalanceOf = <>::Currency as Currency<::AccountId>>::Balance; @@ -117,7 +120,8 @@ pub trait Trait: system::Trait { type Currency: Currency + ReservableCurrency; /// The score attributed to a member or candidate. - type Score: SimpleArithmetic + Clone + Copy + Default + FullCodec + MaybeSerializeDebug; + type Score: + SimpleArithmetic + Clone + Copy + Default + FullCodec + MaybeSerializeDeserialize + Debug; /// The overarching event type. type Event: From> + Into<::Event>; @@ -154,20 +158,20 @@ decl_storage! { trait Store for Module, I: Instance=DefaultInstance> as ScoredPool { /// The current pool of candidates, stored as an ordered Vec /// (ordered descending by score, `None` last, highest first). - Pool get(pool) config(): PoolT; + Pool get(fn pool) config(): PoolT; /// A Map of the candidates. The information in this Map is redundant /// to the information in the `Pool`. But the Map enables us to easily /// 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(candidate_exists): map T::AccountId => bool; + CandidateExists get(fn candidate_exists): map T::AccountId => bool; /// The current membership, stored as an ordered Vec. - Members get(members): Vec; + Members get(fn members): Vec; /// Size of the `Members` set. - MemberCount get(member_count) config(): u32; + MemberCount get(fn member_count) config(): u32; } add_extra_genesis { config(members): Vec; diff --git a/srml/scored-pool/src/mock.rs b/srml/scored-pool/src/mock.rs index fd14abd6175a3d5e8f8bd97586c8dcae16081c20..394f486a8bec383bf28e52eef7a0a1c4c7a47aec 100644 --- a/srml/scored-pool/src/mock.rs +++ b/srml/scored-pool/src/mock.rs @@ -20,7 +20,7 @@ use super::*; use std::cell::RefCell; use support::{impl_outer_origin, parameter_types}; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried. use sr_primitives::{ @@ -52,8 +52,6 @@ parameter_types! { pub const ExistentialDeposit: u64 = 0; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl system::Trait for Test { @@ -66,7 +64,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -80,15 +77,11 @@ impl balances::Trait for Test { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } thread_local! { @@ -132,7 +125,7 @@ impl Trait for Test { // This function basically just builds a genesis storage key/value store according to // our desired mockup. -pub fn new_test_ext() -> runtime_io::TestExternalities { +pub fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); // We use default for brevity, but you can configure as desired if needed. balances::GenesisConfig:: { diff --git a/srml/scored-pool/src/tests.rs b/srml/scored-pool/src/tests.rs index 740e707ce0529e6afabddd3d713a03bbfcf11170..2f47b5da6fe26c5a8c0443155b370fd38e2aa5b9 100644 --- a/srml/scored-pool/src/tests.rs +++ b/srml/scored-pool/src/tests.rs @@ -20,7 +20,6 @@ use super::*; use mock::*; use support::{assert_ok, assert_noop}; -use runtime_io::with_externalities; use sr_primitives::traits::OnInitialize; type ScoredPool = Module; @@ -32,7 +31,7 @@ const INDEX_ERR: &str = "index does not match requested account"; #[test] fn query_membership_works() { - with_externalities(&mut new_test_ext(), || { + 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()); @@ -42,7 +41,7 @@ fn query_membership_works() { #[test] fn submit_candidacy_must_not_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!( ScoredPool::submit_candidacy(Origin::signed(99)), "balance too low to submit candidacy" @@ -56,7 +55,7 @@ fn submit_candidacy_must_not_work() { #[test] fn submit_candidacy_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 15; @@ -71,7 +70,7 @@ fn submit_candidacy_works() { #[test] fn scoring_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 15; let score = 99; @@ -89,7 +88,7 @@ fn scoring_works() { #[test] fn scoring_same_element_with_same_score_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 31; let index = find_in_pool(who).expect("entity must be in pool") as u32; @@ -109,7 +108,7 @@ fn scoring_same_element_with_same_score_works() { #[test] fn kicking_works_only_for_authorized() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let who = 40; let index = find_in_pool(who).expect("entity must be in pool") as u32; assert_noop!(ScoredPool::kick(Origin::signed(99), who, index), "bad origin"); @@ -118,7 +117,7 @@ fn kicking_works_only_for_authorized() { #[test] fn kicking_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 40; assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get()); @@ -138,7 +137,7 @@ fn kicking_works() { #[test] fn unscored_entities_must_not_be_used_for_filling_members() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given // we submit a candidacy, score will be `None` assert_ok!(ScoredPool::submit_candidacy(Origin::signed(15))); @@ -163,7 +162,7 @@ fn unscored_entities_must_not_be_used_for_filling_members() { #[test] fn refreshing_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 15; assert_ok!(ScoredPool::submit_candidacy(Origin::signed(who))); @@ -181,7 +180,7 @@ fn refreshing_works() { #[test] fn refreshing_happens_every_period() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given System::set_block_number(1); assert_ok!(ScoredPool::submit_candidacy(Origin::signed(15))); @@ -201,7 +200,7 @@ fn refreshing_happens_every_period() { #[test] fn withdraw_candidacy_must_only_work_for_members() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let who = 77; let index = 0; assert_noop!( ScoredPool::withdraw_candidacy(Origin::signed(who), index), INDEX_ERR); @@ -210,7 +209,7 @@ fn withdraw_candidacy_must_only_work_for_members() { #[test] fn oob_index_should_abort() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let who = 40; let oob_index = ScoredPool::pool().len() as u32; assert_noop!(ScoredPool::withdraw_candidacy(Origin::signed(who), oob_index), OOB_ERR); @@ -221,7 +220,7 @@ fn oob_index_should_abort() { #[test] fn index_mismatches_should_abort() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let who = 40; let index = 3; assert_noop!(ScoredPool::withdraw_candidacy(Origin::signed(who), index), INDEX_ERR); @@ -232,7 +231,7 @@ fn index_mismatches_should_abort() { #[test] fn withdraw_unscored_candidacy_must_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 5; @@ -247,7 +246,7 @@ fn withdraw_unscored_candidacy_must_work() { #[test] fn withdraw_scored_candidacy_must_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 40; assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get()); @@ -265,7 +264,7 @@ fn withdraw_scored_candidacy_must_work() { #[test] fn candidacy_resubmitting_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // given let who = 15; diff --git a/srml/session/Cargo.toml b/srml/session/Cargo.toml index b114755ad91c203f9c1664bb32568edb9084c572..134686791031376c22fd2c80a703717cbfd63a32 100644 --- a/srml/session/Cargo.toml +++ b/srml/session/Cargo.toml @@ -16,7 +16,7 @@ system = { package = "srml-system", path = "../system", default-features = false timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false } substrate-trie = { path = "../../core/trie", default-features = false, optional = true } runtime-io ={ package = "sr-io", path = "../../core/sr-io", default-features = false } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [dev-dependencies] primitives = { package = "substrate-primitives", path = "../../core/primitives" } diff --git a/srml/session/src/historical.rs b/srml/session/src/historical.rs index 24821a03932600308557f3c3ad951babf61a0600..7975da49983249041edc78b667cb249e4535527a 100644 --- a/srml/session/src/historical.rs +++ b/srml/session/src/historical.rs @@ -56,9 +56,9 @@ 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(historical_root): map SessionIndex => Option<(T::Hash, ValidatorCount)>; + HistoricalSessions get(fn historical_root): map SessionIndex => Option<(T::Hash, ValidatorCount)>; /// Queued full identifications for queued sessions whose validators have become obsolete. - CachedObsolete get(cached_obsolete): map SessionIndex + CachedObsolete get(fn cached_obsolete): map SessionIndex => Option>; /// The range of historical sessions we store. [first, last) StoredRange: Option<(SessionIndex, SessionIndex)>; @@ -182,11 +182,9 @@ impl ProvingTrie { // map each key to the owner index. for key_id in T::Keys::key_ids() { - let key = keys.get_raw(key_id); + let key = keys.get_raw(*key_id); let res = (key_id, key).using_encoded(|k| - i.using_encoded(|v| - trie.insert(k, v) - ) + i.using_encoded(|v| trie.insert(k, v)) ); let _ = res.map_err(|_| "failed to insert into trie")?; @@ -312,8 +310,7 @@ impl> support::traits::KeyOwnerProofSystem<(KeyTypeId, #[cfg(test)] mod tests { use super::*; - use runtime_io::with_externalities; - use primitives::{Blake2Hasher, crypto::key_types::DUMMY}; + use primitives::crypto::key_types::DUMMY; use sr_primitives::{traits::OnInitialize, testing::UintAuthorityId}; use crate::mock::{ NEXT_VALIDATORS, force_new_session, @@ -323,7 +320,7 @@ mod tests { type Historical = Module; - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); crate::GenesisConfig:: { keys: NEXT_VALIDATORS.with(|l| @@ -335,7 +332,7 @@ mod tests { #[test] fn generated_proof_is_good() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { set_next_validators(vec![1, 2]); force_new_session(); @@ -376,7 +373,7 @@ mod tests { #[test] fn prune_up_to_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { for i in 1..101u64 { set_next_validators(vec![i]); force_new_session(); diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index 208be4664aa6e03d6f35d326a5b41825138f9c44..42324d387f69e2e73ba1a48c356f9e45b2b98db2 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -121,7 +121,7 @@ use rstd::{prelude::*, marker::PhantomData, ops::{Sub, Rem}}; use codec::Decode; -use sr_primitives::{KeyTypeId, Perbill, RuntimeAppPublic}; +use sr_primitives::{KeyTypeId, Perbill, RuntimeAppPublic, BoundToRuntimeAppPublic}; use sr_primitives::weights::SimpleDispatchInfo; use sr_primitives::traits::{Convert, Zero, Member, OpaqueKeys}; use sr_staking_primitives::SessionIndex; @@ -192,6 +192,12 @@ impl OnSessionEnding for () { /// Handler for session lifecycle events. pub trait SessionHandler { + /// All the key type ids this session handler can process. + /// + /// The order must be the same as it expects them in + /// [`on_new_session`](Self::on_new_session) and [`on_genesis_session`](Self::on_genesis_session). + const KEY_TYPE_IDS: &'static [KeyTypeId]; + /// The given validator set will be used for the genesis session. /// It is guaranteed that the given validator set will also be used /// for the second session, therefore the first call to `on_new_session` @@ -220,7 +226,7 @@ pub trait SessionHandler { } /// A session handler for specific key type. -pub trait OneSessionHandler { +pub trait OneSessionHandler: BoundToRuntimeAppPublic { /// The key type expected. type Key: Decode + Default + RuntimeAppPublic; @@ -253,11 +259,15 @@ pub trait OneSessionHandler { fn on_disabled(_validator_index: usize); } -#[impl_trait_for_tuples::impl_for_tuples(30)] +#[impl_trait_for_tuples::impl_for_tuples(1, 30)] #[tuple_types_no_default_trait_bound] impl SessionHandler for Tuple { for_tuples!( where #( Tuple: OneSessionHandler )* ); + for_tuples!( + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[ #( ::ID ),* ]; + ); + fn on_genesis_session(validators: &[(AId, Ks)]) { for_tuples!( #( @@ -297,6 +307,20 @@ impl SessionHandler for Tuple { } } +/// `SessionHandler` for tests that use `UintAuthorityId` as `Keys`. +pub struct TestSessionHandler; +impl SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[sr_primitives::key_types::DUMMY]; + + fn on_genesis_session(_: &[(AId, Ks)]) {} + + fn on_new_session(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {} + + fn on_before_session_ending() {} + + fn on_disabled(_: usize) {} +} + /// Handler for selecting the genesis validator set. pub trait SelectInitialValidators { /// Returns the initial validator set. If `None` is returned @@ -349,10 +373,10 @@ const DEDUP_KEY_PREFIX: &[u8] = b":session:keys"; decl_storage! { trait Store for Module as Session { /// The current set of validators. - Validators get(validators): Vec; + Validators get(fn validators): Vec; /// Current index of the session. - CurrentIndex get(current_index): SessionIndex; + CurrentIndex get(fn current_index): SessionIndex; /// True if the underlying economic identities or weighting behind the validators /// has changed in the queued validator set. @@ -360,12 +384,12 @@ decl_storage! { /// The queued keys for the next session. When the next session begins, these keys /// will be used to determine the validator's session keys. - QueuedKeys get(queued_keys): Vec<(T::ValidatorId, T::Keys)>; + QueuedKeys get(fn queued_keys): Vec<(T::ValidatorId, T::Keys)>; /// Indices of disabled validators. /// /// The set is cleared when `on_session_ending` returns a new set of identities. - DisabledValidators get(disabled_validators): Vec; + DisabledValidators get(fn disabled_validators): Vec; /// The next session keys for a validator. /// @@ -382,6 +406,20 @@ decl_storage! { add_extra_genesis { config(keys): Vec<(T::ValidatorId, T::Keys)>; build(|config: &GenesisConfig| { + if T::SessionHandler::KEY_TYPE_IDS.len() != T::Keys::key_ids().len() { + panic!("Number of keys in session handler and session keys does not match"); + } + + T::SessionHandler::KEY_TYPE_IDS.iter().zip(T::Keys::key_ids()).enumerate() + .for_each(|(i, (sk, kk))| { + if sk != kk { + panic!( + "Session handler and session key expect different key type at index: {}", + i, + ); + } + }); + for (who, keys) in config.keys.iter().cloned() { assert!( >::load_keys(&who).is_none(), @@ -594,23 +632,23 @@ impl Module { let old_keys = Self::load_keys(&who); for id in T::Keys::key_ids() { - let key = keys.get_raw(id); + let key = keys.get_raw(*id); // ensure keys are without duplication. ensure!( - Self::key_owner(id, key).map_or(true, |owner| &owner == who), + Self::key_owner(*id, key).map_or(true, |owner| &owner == who), "registered duplicate key" ); - if let Some(old) = old_keys.as_ref().map(|k| k.get_raw(id)) { + if let Some(old) = old_keys.as_ref().map(|k| k.get_raw(*id)) { if key == old { continue; } - Self::clear_key_owner(id, old); + Self::clear_key_owner(*id, old); } - Self::put_key_owner(id, key, &who); + Self::put_key_owner(*id, key, &who); } Self::put_keys(&who, &keys); @@ -621,8 +659,8 @@ impl Module { fn prune_dead_keys(who: &T::ValidatorId) { if let Some(old_keys) = Self::take_keys(who) { for id in T::Keys::key_ids() { - let key_data = old_keys.get_raw(id); - Self::clear_key_owner(id, key_data); + let key_data = old_keys.get_raw(*id); + Self::clear_key_owner(*id, key_data); } } } @@ -680,19 +718,15 @@ impl> FindAuthor mod tests { use super::*; use support::assert_ok; - use runtime_io::with_externalities; - use primitives::{Blake2Hasher, crypto::key_types::DUMMY}; - use sr_primitives::{ - traits::OnInitialize, - testing::UintAuthorityId, - }; + use primitives::crypto::key_types::DUMMY; + use sr_primitives::{traits::OnInitialize, testing::UintAuthorityId}; use mock::{ NEXT_VALIDATORS, SESSION_CHANGED, TEST_SESSION_CHANGED, authorities, force_new_session, set_next_validators, set_session_length, session_changed, Test, Origin, System, Session, reset_before_session_end_called, before_session_end_called, }; - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig:: { keys: NEXT_VALIDATORS.with(|l| @@ -710,7 +744,7 @@ mod tests { #[test] fn simple_setup_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); assert_eq!(Session::validators(), vec![1, 2, 3]); }); @@ -718,7 +752,7 @@ mod tests { #[test] fn put_get_keys() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Session::put_keys(&10, &UintAuthorityId(10).into()); assert_eq!(Session::load_keys(&10), Some(UintAuthorityId(10).into())); }) @@ -727,7 +761,7 @@ mod tests { #[test] fn keys_cleared_on_kill() { let mut ext = new_test_ext(); - with_externalities(&mut ext, || { + ext.execute_with(|| { assert_eq!(Session::validators(), vec![1, 2, 3]); assert_eq!(Session::load_keys(&1), Some(UintAuthorityId(1).into())); @@ -744,7 +778,7 @@ mod tests { fn authorities_should_track_validators() { reset_before_session_end_called(); - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { set_next_validators(vec![1, 2]); force_new_session(); initialize_block(1); @@ -795,7 +829,7 @@ mod tests { #[test] fn should_work_with_early_exit() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { set_session_length(10); initialize_block(1); @@ -818,7 +852,7 @@ mod tests { #[test] fn session_change_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // Block 1: No change initialize_block(1); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); @@ -848,7 +882,7 @@ mod tests { #[test] fn duplicates_are_not_allowed() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::set_block_number(1); Session::on_initialize(1); assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_err()); @@ -863,7 +897,7 @@ mod tests { fn session_changed_flag_works() { reset_before_session_end_called(); - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { TEST_SESSION_CHANGED.with(|l| *l.borrow_mut() = true); force_new_session(); @@ -952,7 +986,7 @@ mod tests { #[test] fn session_keys_generate_output_works_as_set_keys_input() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let new_keys = mock::MockSessionKeys::generate(None); assert_ok!( Session::set_keys( @@ -966,7 +1000,7 @@ mod tests { #[test] fn return_true_if_more_than_third_is_disabled() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { set_next_validators(vec![1, 2, 3, 4, 5, 6, 7]); force_new_session(); initialize_block(1); @@ -979,6 +1013,5 @@ mod tests { assert_eq!(Session::disable_index(2), true); assert_eq!(Session::disable_index(3), true); }); - } } diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs index 736d3fa4da920fbc9dbafe8349a0bc352b098a7a..cb95a35570bc1e37455446fff6404e825f83c32c 100644 --- a/srml/session/src/mock.rs +++ b/srml/session/src/mock.rs @@ -28,7 +28,6 @@ use sr_staking_primitives::SessionIndex; impl_opaque_keys! { pub struct MockSessionKeys { - #[id(DUMMY)] pub dummy: UintAuthorityId, } } @@ -67,6 +66,7 @@ impl ShouldEndSession for TestShouldEndSession { pub struct TestSessionHandler; impl SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [sr_primitives::KeyTypeId] = &[UintAuthorityId::ID]; fn on_genesis_session(_validators: &[(u64, T)]) {} fn on_new_session( changed: bool, @@ -168,7 +168,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 0a2f37ccc4425bc16c817a60616d43c7af94796d..23f0d7715cc2ee997dbbbb134de9da21e04e6e2c 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -263,6 +263,7 @@ use support::{ use session::{historical::OnSessionEnding, SelectInitialValidators}; use sr_primitives::{ Perbill, + RuntimeDebug, curve::PiecewiseLinear, weights::SimpleDispatchInfo, traits::{ @@ -277,7 +278,7 @@ use sr_staking_primitives::{ use sr_primitives::{Serialize, Deserialize}; use system::{ensure_signed, ensure_root}; -use phragmen::{elect, equalize, ExtendedBalance, Support, SupportMap, PhragmenStakedAssignment}; +use phragmen::{elect, equalize, build_support_map, ExtendedBalance, PhragmenStakedAssignment}; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const MAX_NOMINATIONS: usize = 16; @@ -313,7 +314,8 @@ impl EraPoints { } /// Indicates the initial status of the staker. -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum StakerStatus { /// Chilling. Idle, @@ -324,8 +326,7 @@ pub enum StakerStatus { } /// A destination account for payment. -#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug)] pub enum RewardDestination { /// Pay into the stash account, increasing the amount at stake accordingly. Staked, @@ -342,8 +343,7 @@ impl Default for RewardDestination { } /// Preference of what happens on a slash event. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct ValidatorPrefs { /// Reward that validator takes up-front; only the rest is split between themselves and /// nominators. @@ -360,8 +360,7 @@ impl Default for ValidatorPrefs { } /// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct UnlockChunk { /// Amount of funds to be unlocked. #[codec(compact)] @@ -372,8 +371,7 @@ pub struct UnlockChunk { } /// The ledger of a (bonded) stash. -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct StakingLedger { /// The stash account whose balance is actually locked and at stake. pub stash: AccountId, @@ -411,8 +409,7 @@ impl< } /// The amount of exposure (to slashing) than an individual nominator has. -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug)] pub struct IndividualExposure { /// The stash account of the nominator in question. who: AccountId, @@ -422,8 +419,7 @@ pub struct IndividualExposure { } /// A snapshot of the stake backing a single validator in the system. -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default, RuntimeDebug)] pub struct Exposure { /// The total balance backing this validator. #[codec(compact)] @@ -436,8 +432,7 @@ pub struct Exposure { } /// A slashing event occurred, slashing a validator for a given amount of balance. -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default, RuntimeDebug)] pub struct SlashJournalEntry { who: AccountId, amount: Balance, @@ -532,8 +527,8 @@ pub trait Trait: system::Trait { } /// Mode of era-forcing. -#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Forcing { /// Not forcing anything - just let whatever happen. NotForcing, @@ -541,6 +536,8 @@ pub enum Forcing { ForceNew, /// Avoid a new era indefinitely. ForceNone, + /// Force a new era at the end of all sessions indefinitely. + ForceAlways, } impl Default for Forcing { @@ -551,72 +548,72 @@ decl_storage! { trait Store for Module as Staking { /// The ideal number of staking participants. - pub ValidatorCount get(validator_count) config(): u32; + pub ValidatorCount get(fn validator_count) config(): u32; /// Minimum number of staking participants before emergency conditions are imposed. - pub MinimumValidatorCount get(minimum_validator_count) config(): + pub MinimumValidatorCount get(fn minimum_validator_count) config(): u32 = DEFAULT_MINIMUM_VALIDATOR_COUNT; /// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're /// easy to initialize and the performance hit is minimal (we expect no more than four /// invulnerables) and restricted to testnets. - pub Invulnerables get(invulnerables) config(): Vec; + pub Invulnerables get(fn invulnerables) config(): Vec; /// Map from all locked "stash" accounts to the controller account. - pub Bonded get(bonded): map T::AccountId => Option; + pub Bonded get(fn bonded): map T::AccountId => Option; /// Map from all (unlocked) "controller" accounts to the info regarding the staking. - pub Ledger get(ledger): + pub Ledger get(fn ledger): map T::AccountId => Option>>; /// Where the reward payment should be made. Keyed by stash. - pub Payee get(payee): map T::AccountId => RewardDestination; + pub Payee get(fn payee): map T::AccountId => RewardDestination; /// The map from (wannabe) validator stash key to the preferences of that validator. - pub Validators get(validators): linked_map T::AccountId => ValidatorPrefs>; + pub Validators get(fn validators): linked_map T::AccountId => ValidatorPrefs>; /// The map from nominator stash key to the set of stash keys of all validators to nominate. - pub Nominators get(nominators): linked_map T::AccountId => Vec; + pub Nominators get(fn nominators): linked_map T::AccountId => Vec; /// 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(stakers): map T::AccountId => Exposure>; + pub Stakers get(fn stakers): map T::AccountId => Exposure>; /// The currently elected validator set keyed by stash account ID. - pub CurrentElected get(current_elected): Vec; + pub CurrentElected get(fn current_elected): Vec; /// The current era index. - pub CurrentEra get(current_era) config(): EraIndex; + pub CurrentEra get(fn current_era) config(): EraIndex; /// The start of the current era. - pub CurrentEraStart get(current_era_start): MomentOf; + pub CurrentEraStart get(fn current_era_start): MomentOf; /// The session index at which the current era started. - pub CurrentEraStartSessionIndex get(current_era_start_session_index): SessionIndex; + pub CurrentEraStartSessionIndex get(fn current_era_start_session_index): SessionIndex; /// Rewards for the current era. Using indices of current elected set. - CurrentEraPointsEarned get(current_era_reward): EraPoints; + CurrentEraPointsEarned get(fn current_era_reward): EraPoints; /// The amount of balance actively at stake for each validator slot, currently. /// /// This is used to derive rewards and punishments. - pub SlotStake get(slot_stake) build(|config: &GenesisConfig| { + pub SlotStake get(fn slot_stake) build(|config: &GenesisConfig| { config.stakers.iter().map(|&(_, _, value, _)| value).min().unwrap_or_default() }): BalanceOf; /// True if the next session change will be a new era regardless of index. - pub ForceEra get(force_era) config(): Forcing; + pub ForceEra get(fn force_era) config(): Forcing; /// The percentage of the slash that is distributed to reporters. /// /// The rest of the slashed value is handled by the `Slash`. - pub SlashRewardFraction get(slash_reward_fraction) config(): Perbill; + pub SlashRewardFraction get(fn slash_reward_fraction) config(): Perbill; /// A mapping from still-bonded eras to the first session index of that era. BondedEras: Vec<(EraIndex, SessionIndex)>; /// All slashes that have occurred in a given era. - EraSlashJournal get(era_slash_journal): + EraSlashJournal get(fn era_slash_journal): map EraIndex => Vec>>; } add_extra_genesis { @@ -961,7 +958,7 @@ decl_module! { } /// The ideal number of validators. - #[weight = SimpleDispatchInfo::FixedOperational(150_000)] + #[weight = SimpleDispatchInfo::FreeOperational] fn set_validator_count(origin, #[compact] new: u32) { ensure_root(origin)?; ValidatorCount::put(new); @@ -974,7 +971,7 @@ decl_module! { /// # /// - No arguments. /// # - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + #[weight = SimpleDispatchInfo::FreeOperational] fn force_no_eras(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNone); @@ -986,18 +983,40 @@ decl_module! { /// # /// - No arguments. /// # - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + #[weight = SimpleDispatchInfo::FreeOperational] fn force_new_era(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNew); } /// Set the validators who cannot be slashed (if any). - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + #[weight = SimpleDispatchInfo::FreeOperational] fn set_invulnerables(origin, validators: Vec) { ensure_root(origin)?; >::put(validators); } + + /// Force a current staker to become completely unstaked, immediately. + #[weight = SimpleDispatchInfo::FreeOperational] + fn force_unstake(origin, stash: T::AccountId) { + ensure_root(origin)?; + + // remove the lock. + T::Currency::remove_lock(STAKING_ID, &stash); + // remove all staking-related information. + Self::kill_stash(&stash); + } + + /// Force there to be a new era at the end of sessions indefinitely. + /// + /// # + /// - One storage write + /// # + #[weight = SimpleDispatchInfo::FreeOperational] + fn force_new_era_always(origin) { + ensure_root(origin)?; + ForceEra::put(Forcing::ForceAlways); + } } } @@ -1149,6 +1168,7 @@ impl Module { let era_length = session_index.checked_sub(Self::current_era_start_session_index()).unwrap_or(0); match ForceEra::get() { Forcing::ForceNew => ForceEra::kill(), + Forcing::ForceAlways => (), Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (), _ => return None, } @@ -1262,31 +1282,12 @@ impl Module { let to_balance = |e: ExtendedBalance| >>::convert(e); - // Initialize the support of each candidate. - let mut supports = >::new(); - elected_stashes - .iter() - .map(|e| (e, to_votes(Self::slashable_balance_of(e)))) - .for_each(|(e, s)| { - let item = Support { own: s, total: s, ..Default::default() }; - supports.insert(e.clone(), item); - }); - - // build support struct. - for (n, assignment) in assignments.iter() { - for (c, per_thing) in assignment.iter() { - let nominator_stake = to_votes(Self::slashable_balance_of(n)); - // AUDIT: it is crucially important for the `Mul` implementation of all - // per-things to be sound. - let other_stake = *per_thing * nominator_stake; - if let Some(support) = supports.get_mut(c) { - // For an astronomically rich validator with more astronomically rich - // set of nominators, this might saturate. - support.total = support.total.saturating_add(other_stake); - support.others.push((n.clone(), other_stake)); - } - } - } + let mut supports = build_support_map::<_, _, _, T::CurrencyToVote>( + &elected_stashes, + &assignments, + Self::slashable_balance_of, + true, + ); if cfg!(feature = "equalize") { let mut staked_assignments @@ -1419,6 +1420,14 @@ impl Module { } }); } + + /// Ensures that at the end of the current session there will be a new era. + fn ensure_new_era() { + match ForceEra::get() { + Forcing::ForceAlways | Forcing::ForceNew => (), + _ => ForceEra::put(Forcing::ForceNew), + } + } } impl session::OnSessionEnding for Module { @@ -1515,6 +1524,13 @@ impl OnOffenceHandler>::exists(stash) { + >::remove(stash); + Self::ensure_new_era(); + } + // calculate the amount to slash let slash_exposure = exposure.total; let amount = *slash_fraction * slash_exposure; @@ -1527,7 +1543,7 @@ impl OnOffenceHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [KeyTypeId] = &[key_types::DUMMY]; + fn on_genesis_session(_validators: &[(AccountId, Ks)]) {} fn on_new_session( @@ -118,7 +120,6 @@ impl system::Trait for Test { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -129,23 +130,17 @@ impl system::Trait for Test { parameter_types! { pub const TransferFee: Balance = 0; pub const CreationFee: Balance = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = Balance; type OnFreeBalanceZero = Staking; type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const Period: BlockNumber = 1; @@ -274,7 +269,7 @@ impl ExtBuilder { pub fn set_associated_consts(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); } - pub fn build(self) -> runtime_io::TestExternalities { + pub fn build(self) -> runtime_io::TestExternalities { self.set_associated_consts(); let mut storage = system::GenesisConfig::default().build_storage::().unwrap(); let balance_factor = if self.existential_deposit > 0 { @@ -340,8 +335,8 @@ impl ExtBuilder { keys: validators.iter().map(|x| (*x, UintAuthorityId(*x))).collect(), }.assimilate_storage(&mut storage); - let mut ext = storage.into(); - runtime_io::with_externalities(&mut ext, || { + let mut ext = runtime_io::TestExternalities::from(storage); + ext.execute_with(|| { let validators = Session::validators(); SESSION.with(|x| *x.borrow_mut() = (validators.clone(), HashSet::new()) diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 6b51fb9b3f0d8abddb75d214f48886c6c9e13388..aef5a26a04537828b4a0ed62fa855e107507d7de 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -18,18 +18,36 @@ use super::*; use mock::*; -use runtime_io::with_externalities; use sr_primitives::{assert_eq_error_rate, traits::OnInitialize}; use sr_staking_primitives::offence::{OffenceDetails, OnOffenceHandler}; -use support::{assert_ok, assert_noop, assert_eq_uvec}; -use support::traits::{Currency, ReservableCurrency}; +use support::{assert_ok, assert_noop, assert_eq_uvec, traits::{Currency, ReservableCurrency}}; + +#[test] +fn force_unstake_works() { + // Verifies initial conditions of mock + ExtBuilder::default().build().execute_with(|| { + // Account 11 is stashed and locked, and account 10 is the controller + assert_eq!(Staking::bonded(&11), Some(10)); + // Cant transfer + assert_noop!( + Balances::transfer(Origin::signed(11), 1, 10), + "account liquidity restrictions prevent withdrawal" + ); + // Force unstake requires root. + assert_noop!(Staking::force_unstake(Origin::signed(11), 11), "RequireRootOrigin"); + // We now force them to unstake + assert_ok!(Staking::force_unstake(Origin::ROOT, 11)); + // No longer bonded. + assert_eq!(Staking::bonded(&11), None); + // Transfer works. + assert_ok!(Balances::transfer(Origin::signed(11), 1, 10)); + }); +} #[test] fn basic_setup_works() { // Verifies initial conditions of mock - with_externalities(&mut ExtBuilder::default() - .build(), - || { + ExtBuilder::default().build().execute_with(|| { // Account 11 is stashed and locked, and account 10 is the controller assert_eq!(Staking::bonded(&11), Some(10)); // Account 21 is stashed and locked, and account 20 is the controller @@ -109,8 +127,7 @@ fn basic_setup_works() { #[test] fn change_controller_works() { - with_externalities(&mut ExtBuilder::default().build(), - || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Staking::bonded(&11), Some(10)); assert!(>::enumerate().map(|(c, _)| c).collect::>().contains(&11)); @@ -136,10 +153,7 @@ fn rewards_should_work() { // * rewards get recorded per session // * rewards get paid per Era // * Check that nominators are also rewarded - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { // Init some balances let _ = Balances::make_free_balance_be(&2, 500); @@ -217,10 +231,7 @@ fn multi_era_reward_should_work() { // Should check that: // The value of current_session_reward is set at the end of each era, based on // slot_stake and session_reward. - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { let init_balance_10 = Balances::total_balance(&10); // Set payee to controller @@ -229,7 +240,6 @@ 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 - dbg!(>::slot_stake()); >::reward_by_ids(vec![(11, 1)]); start_session(0); @@ -260,122 +270,122 @@ fn staking_should_work() { // * new validators can be added to the default set // * new ones will be chosen per era // * either one can unlock the stash and back-down from being a validator via `chill`ing. - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .nominate(false) .fair(false) // to give 20 more staked value - .build(), - || { - Timestamp::set_timestamp(1); // Initialize time. + .build() + .execute_with(|| { + Timestamp::set_timestamp(1); // Initialize time. - // remember + compare this along with the test. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + // remember + compare this along with the test. + assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // put some money in account that we'll use. - for i in 1..5 { let _ = Balances::make_free_balance_be(&i, 2000); } + // put some money in account that we'll use. + for i in 1..5 { let _ = Balances::make_free_balance_be(&i, 2000); } - // --- Block 1: - start_session(1); - // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller)); - assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); + // --- Block 1: + start_session(1); + // add a new candidate for being a validator. account 3 controlled by 4. + assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller)); + assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); - // No effects will be seen so far. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + // No effects will be seen so far. + assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // --- Block 2: - start_session(2); + // --- Block 2: + start_session(2); - // No effects will be seen so far. Era has not been yet triggered. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + // No effects will be seen so far. Era has not been yet triggered. + assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // --- Block 3: the validators will now be queued. - start_session(3); - assert_eq!(Staking::current_era(), 1); + // --- Block 3: the validators will now be queued. + start_session(3); + assert_eq!(Staking::current_era(), 1); - // --- Block 4: the validators will now be changed. - start_session(4); + // --- Block 4: the validators will now be changed. + start_session(4); - assert_eq_uvec!(validator_controllers(), vec![20, 4]); - // --- Block 4: Unstake 4 as a validator, freeing up the balance stashed in 3 - // 4 will chill - Staking::chill(Origin::signed(4)).unwrap(); + assert_eq_uvec!(validator_controllers(), vec![20, 4]); + // --- Block 4: Unstake 4 as a validator, freeing up the balance stashed in 3 + // 4 will chill + Staking::chill(Origin::signed(4)).unwrap(); - // --- Block 5: nothing. 4 is still there. - start_session(5); - assert_eq_uvec!(validator_controllers(), vec![20, 4]); + // --- Block 5: nothing. 4 is still there. + start_session(5); + assert_eq_uvec!(validator_controllers(), vec![20, 4]); - // --- Block 6: 4 will not be a validator. - start_session(7); - assert_eq_uvec!(validator_controllers(), vec![20, 10]); + // --- Block 6: 4 will not be a validator. + start_session(7); + assert_eq_uvec!(validator_controllers(), vec![20, 10]); - // Note: the stashed value of 4 is still lock - assert_eq!( - Staking::ledger(&4), - Some(StakingLedger { stash: 3, total: 1500, active: 1500, unlocking: vec![] }) - ); - // e.g. it cannot spend more than 500 that it has free from the total 2000 - assert_noop!(Balances::reserve(&3, 501), "account liquidity restrictions prevent withdrawal"); - assert_ok!(Balances::reserve(&3, 409)); - }); + // Note: the stashed value of 4 is still lock + assert_eq!( + Staking::ledger(&4), + Some(StakingLedger { stash: 3, total: 1500, active: 1500, unlocking: vec![] }) + ); + // e.g. it cannot spend more than 500 that it has free from the total 2000 + assert_noop!(Balances::reserve(&3, 501), "account liquidity restrictions prevent withdrawal"); + assert_ok!(Balances::reserve(&3, 409)); + }); } #[test] fn less_than_needed_candidates_works() { - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .minimum_validator_count(1) .validator_count(4) .nominate(false) .num_validators(3) - .build(), - || { - assert_eq!(Staking::validator_count(), 4); - assert_eq!(Staking::minimum_validator_count(), 1); - assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); - - start_era(1); - - // Previous set is selected. NO election algorithm is even executed. - assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); - - // But the exposure is updated in a simple way. No external votes exists. This is purely self-vote. - assert_eq!(Staking::stakers(10).others.len(), 0); - assert_eq!(Staking::stakers(20).others.len(), 0); - assert_eq!(Staking::stakers(30).others.len(), 0); - check_exposure_all(); - check_nominator_all(); - }); + .build() + .execute_with(|| { + assert_eq!(Staking::validator_count(), 4); + assert_eq!(Staking::minimum_validator_count(), 1); + assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); + + start_era(1); + + // Previous set is selected. NO election algorithm is even executed. + assert_eq_uvec!(validator_controllers(), vec![30, 20, 10]); + + // But the exposure is updated in a simple way. No external votes exists. + // This is purely self-vote. + assert_eq!(Staking::stakers(10).others.len(), 0); + assert_eq!(Staking::stakers(20).others.len(), 0); + assert_eq!(Staking::stakers(30).others.len(), 0); + check_exposure_all(); + check_nominator_all(); + }); } #[test] fn no_candidate_emergency_condition() { - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .minimum_validator_count(10) .validator_count(15) .num_validators(4) .validator_pool(true) .nominate(false) - .build(), - || { + .build() + .execute_with(|| { + // initial validators + assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); - // initial validators - assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); + // set the minimum validator count. + ::MinimumValidatorCount::put(10); + ::ValidatorCount::put(15); + assert_eq!(Staking::validator_count(), 15); - // set the minimum validator count. - ::MinimumValidatorCount::put(10); - ::ValidatorCount::put(15); - assert_eq!(Staking::validator_count(), 15); + let _ = Staking::chill(Origin::signed(10)); - let _ = Staking::chill(Origin::signed(10)); - - // trigger era - System::set_block_number(1); - Session::on_initialize(System::block_number()); + // trigger era + System::set_block_number(1); + Session::on_initialize(System::block_number()); - // Previous ones are elected. chill is invalidates. TODO: #2494 - assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); - assert_eq!(Staking::current_elected().len(), 0); - }); + // Previous ones are elected. chill is invalidates. TODO: #2494 + assert_eq_uvec!(validator_controllers(), vec![10, 20, 30, 40]); + assert_eq!(Staking::current_elected().len(), 0); + }); } #[test] @@ -415,181 +425,181 @@ fn nominating_and_rewards_should_work() { // 10 with stake 400.0 20 with stake 600.0 30 with stake 0 // 4 has load 0.0005555555555555556 and supported // 10 with stake 600.0 20 with stake 400.0 40 with stake 0.0 - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .nominate(false) .validator_pool(true) - .build(), - || { - // initial validators -- everyone is actually even. - assert_eq_uvec!(validator_controllers(), vec![40, 30]); - - // Set payee to controller - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller)); - - // give the man some money - let initial_balance = 1000; - for i in [1, 2, 3, 4, 5, 10, 11, 20, 21].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); - } - - // bond two account pairs and state interest in nomination. - // 2 will nominate for 10, 20, 30 - assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller)); - assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21, 31])); - // 4 will nominate for 10, 20, 40 - assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller)); - assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41])); - - // 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 - >::reward_by_ids(vec![(41, 1)]); - >::reward_by_ids(vec![(31, 1)]); - >::reward_by_ids(vec![(21, 10)]); // must be no-op - >::reward_by_ids(vec![(11, 10)]); // must be no-op - - start_era(1); - - // 10 and 20 have more votes, they will be chosen by phragmen. - assert_eq_uvec!(validator_controllers(), vec![20, 10]); - - // OLD validators must have already received some rewards. - assert_eq!(Balances::total_balance(&40), 1 + total_payout_0 / 2); - assert_eq!(Balances::total_balance(&30), 1 + total_payout_0 / 2); - - // ------ check the staked value of all parties. - - if cfg!(feature = "equalize") { - // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. - assert_eq!(Staking::stakers(11).own, 1000); - assert_eq_error_rate!(Staking::stakers(11).total, 1000 + 1000, 2); - // 2 and 4 supported 10, each with stake 600, according to phragmen. - assert_eq!( - Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), - vec![600, 400] - ); - assert_eq!( - Staking::stakers(11).others.iter().map(|e| e.who).collect::>(), - vec![3, 1] - ); - // total expo of 20, with 500 coming from nominators (externals), according to phragmen. - assert_eq!(Staking::stakers(21).own, 1000); - assert_eq_error_rate!(Staking::stakers(21).total, 1000 + 1000, 2); - // 2 and 4 supported 20, each with stake 250, according to phragmen. - assert_eq!( - Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), - vec![400, 600] - ); - assert_eq!( - Staking::stakers(21).others.iter().map(|e| e.who).collect::>(), - vec![3, 1] - ); - } else { - // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. - assert_eq!(Staking::stakers(11).own, 1000); - assert_eq!(Staking::stakers(11).total, 1000 + 800); - // 2 and 4 supported 10, each with stake 600, according to phragmen. - assert_eq!( - Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), - vec![400, 400] - ); - assert_eq!( - Staking::stakers(11).others.iter().map(|e| e.who).collect::>(), - vec![3, 1] - ); - // total expo of 20, with 500 coming from nominators (externals), according to phragmen. - assert_eq!(Staking::stakers(21).own, 1000); - assert_eq_error_rate!(Staking::stakers(21).total, 1000 + 1200, 2); - // 2 and 4 supported 20, each with stake 250, according to phragmen. - assert_eq!( - Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), - vec![600, 600] - ); - assert_eq!( - Staking::stakers(21).others.iter().map(|e| e.who).collect::>(), - vec![3, 1] - ); - } - - // They are not chosen anymore - assert_eq!(Staking::stakers(31).total, 0); - assert_eq!(Staking::stakers(41).total, 0); - - // 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 - >::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)]); - >::reward_by_ids(vec![(11, 1)]); - - start_era(2); - - // nothing else will happen, era ends and rewards are paid again, - // it is expected that nominators will also be paid. See below - - let payout_for_10 = total_payout_1 / 3; - let payout_for_20 = 2 * total_payout_1 / 3; - if cfg!(feature = "equalize") { - // Nominator 2: has [400 / 2000 ~ 1 / 5 from 10] + [600 / 2000 ~ 3 / 10 from 20]'s reward. - assert_eq_error_rate!( - Balances::total_balance(&2), - initial_balance + payout_for_10 / 5 + payout_for_20 * 3 / 10, - 2, - ); - // Nominator 4: has [400 / 2000 ~ 1 / 5 from 20] + [600 / 2000 ~ 3 / 10 from 10]'s reward. - assert_eq_error_rate!( - Balances::total_balance(&4), - initial_balance + payout_for_20 / 5 + payout_for_10 * 3 / 10, - 2, - ); - - // Validator 10: got 1000 / 2000 external stake. - assert_eq_error_rate!( - Balances::total_balance(&10), - initial_balance + payout_for_10 / 2, - 1, - ); - // Validator 20: got 1000 / 2000 external stake. - assert_eq_error_rate!( - Balances::total_balance(&20), - initial_balance + payout_for_20 / 2, - 1, - ); - } else { - // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 - assert_eq_error_rate!( - Balances::total_balance(&2), - initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), - 1, - ); - // Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 - assert_eq_error_rate!( - Balances::total_balance(&4), - initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), - 1, - ); - - // Validator 10: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 - assert_eq_error_rate!( - Balances::total_balance(&10), - initial_balance + 5 * payout_for_10 / 9, - 1, - ); - // Validator 20: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11 - assert_eq_error_rate!( - Balances::total_balance(&20), - initial_balance + 5 * payout_for_20 / 11, - 1, - ); - } - - check_exposure_all(); - check_nominator_all(); - }); + .build() + .execute_with(|| { + // initial validators -- everyone is actually even. + assert_eq_uvec!(validator_controllers(), vec![40, 30]); + + // Set payee to controller + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller)); + + // give the man some money + let initial_balance = 1000; + for i in [1, 2, 3, 4, 5, 10, 11, 20, 21].iter() { + let _ = Balances::make_free_balance_be(i, initial_balance); + } + + // bond two account pairs and state interest in nomination. + // 2 will nominate for 10, 20, 30 + assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller)); + assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21, 31])); + // 4 will nominate for 10, 20, 40 + assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller)); + assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41])); + + // 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 + >::reward_by_ids(vec![(41, 1)]); + >::reward_by_ids(vec![(31, 1)]); + >::reward_by_ids(vec![(21, 10)]); // must be no-op + >::reward_by_ids(vec![(11, 10)]); // must be no-op + + start_era(1); + + // 10 and 20 have more votes, they will be chosen by phragmen. + assert_eq_uvec!(validator_controllers(), vec![20, 10]); + + // OLD validators must have already received some rewards. + assert_eq!(Balances::total_balance(&40), 1 + total_payout_0 / 2); + assert_eq!(Balances::total_balance(&30), 1 + total_payout_0 / 2); + + // ------ check the staked value of all parties. + + if cfg!(feature = "equalize") { + // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. + assert_eq!(Staking::stakers(11).own, 1000); + assert_eq_error_rate!(Staking::stakers(11).total, 1000 + 1000, 2); + // 2 and 4 supported 10, each with stake 600, according to phragmen. + assert_eq!( + Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), + vec![600, 400] + ); + assert_eq!( + Staking::stakers(11).others.iter().map(|e| e.who).collect::>(), + vec![3, 1] + ); + // total expo of 20, with 500 coming from nominators (externals), according to phragmen. + assert_eq!(Staking::stakers(21).own, 1000); + assert_eq_error_rate!(Staking::stakers(21).total, 1000 + 1000, 2); + // 2 and 4 supported 20, each with stake 250, according to phragmen. + assert_eq!( + Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), + vec![400, 600] + ); + assert_eq!( + Staking::stakers(21).others.iter().map(|e| e.who).collect::>(), + vec![3, 1] + ); + } else { + // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. + assert_eq!(Staking::stakers(11).own, 1000); + assert_eq!(Staking::stakers(11).total, 1000 + 800); + // 2 and 4 supported 10, each with stake 600, according to phragmen. + assert_eq!( + Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), + vec![400, 400] + ); + assert_eq!( + Staking::stakers(11).others.iter().map(|e| e.who).collect::>(), + vec![3, 1] + ); + // total expo of 20, with 500 coming from nominators (externals), according to phragmen. + assert_eq!(Staking::stakers(21).own, 1000); + assert_eq_error_rate!(Staking::stakers(21).total, 1000 + 1200, 2); + // 2 and 4 supported 20, each with stake 250, according to phragmen. + assert_eq!( + Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), + vec![600, 600] + ); + assert_eq!( + Staking::stakers(21).others.iter().map(|e| e.who).collect::>(), + vec![3, 1] + ); + } + + // They are not chosen anymore + assert_eq!(Staking::stakers(31).total, 0); + assert_eq!(Staking::stakers(41).total, 0); + + // 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 + >::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)]); + >::reward_by_ids(vec![(11, 1)]); + + start_era(2); + + // nothing else will happen, era ends and rewards are paid again, + // it is expected that nominators will also be paid. See below + + let payout_for_10 = total_payout_1 / 3; + let payout_for_20 = 2 * total_payout_1 / 3; + if cfg!(feature = "equalize") { + // Nominator 2: has [400 / 2000 ~ 1 / 5 from 10] + [600 / 2000 ~ 3 / 10 from 20]'s reward. + assert_eq_error_rate!( + Balances::total_balance(&2), + initial_balance + payout_for_10 / 5 + payout_for_20 * 3 / 10, + 2, + ); + // Nominator 4: has [400 / 2000 ~ 1 / 5 from 20] + [600 / 2000 ~ 3 / 10 from 10]'s reward. + assert_eq_error_rate!( + Balances::total_balance(&4), + initial_balance + payout_for_20 / 5 + payout_for_10 * 3 / 10, + 2, + ); + + // Validator 10: got 1000 / 2000 external stake. + assert_eq_error_rate!( + Balances::total_balance(&10), + initial_balance + payout_for_10 / 2, + 1, + ); + // Validator 20: got 1000 / 2000 external stake. + assert_eq_error_rate!( + Balances::total_balance(&20), + initial_balance + payout_for_20 / 2, + 1, + ); + } else { + // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 + assert_eq_error_rate!( + Balances::total_balance(&2), + initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), + 1, + ); + // Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 + assert_eq_error_rate!( + Balances::total_balance(&4), + initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), + 1, + ); + + // Validator 10: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 + assert_eq_error_rate!( + Balances::total_balance(&10), + initial_balance + 5 * payout_for_10 / 9, + 1, + ); + // Validator 20: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11 + assert_eq_error_rate!( + Balances::total_balance(&20), + initial_balance + 5 * payout_for_20 / 11, + 1, + ); + } + + check_exposure_all(); + check_nominator_all(); + }); } #[test] @@ -599,7 +609,7 @@ fn nominators_also_get_slashed() { // 10 - is the controller of 11 // 11 - is the stash. // 2 - is the nominator of 20, 10 - with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { + ExtBuilder::default().nominate(false).build().execute_with(|| { assert_eq!(Staking::validator_count(), 2); // Set payee to controller @@ -659,53 +669,49 @@ fn double_staking_should_fail() { // * an account already bonded as stash cannot be be stashed again. // * an account already bonded as stash cannot nominate. // * an account already bonded as controller can nominate. - with_externalities(&mut ExtBuilder::default() - .build(), - || { - let arbitrary_value = 5; - // 2 = controller, 1 stashed => ok - assert_ok!( - Staking::bond(Origin::signed(1), 2, arbitrary_value, - RewardDestination::default()) - ); - // 4 = not used so far, 1 stashed => not allowed. - assert_noop!( - Staking::bond(Origin::signed(1), 4, arbitrary_value, - RewardDestination::default()), "stash already bonded" - ); - // 1 = stashed => attempting to nominate should fail. - assert_noop!(Staking::nominate(Origin::signed(1), vec![1]), "not a controller"); - // 2 = controller => nominating should work. - assert_ok!(Staking::nominate(Origin::signed(2), vec![1])); - }); + ExtBuilder::default().build().execute_with(|| { + let arbitrary_value = 5; + // 2 = controller, 1 stashed => ok + assert_ok!( + Staking::bond(Origin::signed(1), 2, arbitrary_value, + RewardDestination::default()) + ); + // 4 = not used so far, 1 stashed => not allowed. + assert_noop!( + Staking::bond(Origin::signed(1), 4, arbitrary_value, + RewardDestination::default()), "stash already bonded" + ); + // 1 = stashed => attempting to nominate should fail. + assert_noop!(Staking::nominate(Origin::signed(1), vec![1]), "not a controller"); + // 2 = controller => nominating should work. + assert_ok!(Staking::nominate(Origin::signed(2), vec![1])); + }); } #[test] fn double_controlling_should_fail() { // should test (in the same order): // * an account already bonded as controller CANNOT be reused as the controller of another account. - with_externalities(&mut ExtBuilder::default() - .build(), - || { - let arbitrary_value = 5; - // 2 = controller, 1 stashed => ok - assert_ok!( - Staking::bond(Origin::signed(1), 2, arbitrary_value, - RewardDestination::default()) - ); - // 2 = controller, 3 stashed (Note that 2 is reused.) => no-op - assert_noop!( - Staking::bond(Origin::signed(3), 2, arbitrary_value, RewardDestination::default()), - "controller already paired" - ); - }); + ExtBuilder::default().build().execute_with(|| { + let arbitrary_value = 5; + // 2 = controller, 1 stashed => ok + assert_ok!(Staking::bond( + Origin::signed(1), + 2, + arbitrary_value, + RewardDestination::default(), + )); + // 2 = controller, 3 stashed (Note that 2 is reused.) => no-op + assert_noop!( + Staking::bond(Origin::signed(3), 2, arbitrary_value, RewardDestination::default()), + "controller already paired", + ); + }); } #[test] fn session_and_eras_work() { - with_externalities(&mut ExtBuilder::default() - .build(), - || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Staking::current_era(), 0); // Block 1: No change. @@ -747,7 +753,7 @@ fn session_and_eras_work() { #[test] fn forcing_new_era_works() { - with_externalities(&mut ExtBuilder::default().build(),|| { + ExtBuilder::default().build().execute_with(|| { // normal flow of session. assert_eq!(Staking::current_era(), 0); start_session(0); @@ -769,7 +775,7 @@ fn forcing_new_era_works() { assert_eq!(Staking::current_era(), 1); // back to normal. - // this immediatelly starts a new session. + // this immediately starts a new session. ForceEra::put(Forcing::NotForcing); start_session(7); assert_eq!(Staking::current_era(), 2); @@ -777,16 +783,30 @@ fn forcing_new_era_works() { assert_eq!(Staking::current_era(), 2); // forceful change - ForceEra::put(Forcing::ForceNew); + ForceEra::put(Forcing::ForceAlways); start_session(9); assert_eq!(Staking::current_era(), 3); + start_session(10); + assert_eq!(Staking::current_era(), 4); + start_session(11); + assert_eq!(Staking::current_era(), 5); + + // just one forceful change + ForceEra::put(Forcing::ForceNew); + start_session(12); + assert_eq!(Staking::current_era(), 6); + + assert_eq!(ForceEra::get(), Forcing::NotForcing); + start_session(13); + assert_eq!(Staking::current_era(), 6); + }); } #[test] fn cannot_transfer_staked_balance() { // Tests that a stash account cannot transfer funds - with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { + ExtBuilder::default().nominate(false).build().execute_with(|| { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(10)); // Confirm account 11 has some free balance @@ -811,11 +831,7 @@ fn cannot_transfer_staked_balance_2() { // Tests that a stash account cannot transfer funds // Same test as above but with 20, and more accurate. // 21 has 2000 free balance but 1000 at stake - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .fair(true) - .build(), - || { + ExtBuilder::default().nominate(false).fair(true).build().execute_with(|| { // Confirm account 21 is stashed assert_eq!(Staking::bonded(&21), Some(20)); // Confirm account 21 has some free balance @@ -834,7 +850,7 @@ fn cannot_transfer_staked_balance_2() { #[test] fn cannot_reserve_staked_balance() { // Checks that a bonded account cannot reserve balance from free balance - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(10)); // Confirm account 11 has some free balance @@ -854,7 +870,7 @@ fn cannot_reserve_staked_balance() { #[test] fn reward_destination_works() { // Rewards go to the correct destination as determined in Payee - with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { + ExtBuilder::default().nominate(false).build().execute_with(|| { // Check that account 11 is a validator assert!(Staking::current_elected().contains(&11)); // Check the balance of the validator account @@ -946,9 +962,7 @@ fn validator_payment_prefs_work() { // Test that validator preferences are correctly honored // Note: unstake threshold is being directly tested in slashing tests. // This test will focus on validator payment. - with_externalities(&mut ExtBuilder::default() - .build(), - || { + ExtBuilder::default().build().execute_with(|| { // Initial config let validator_cut = 5; let stash_initial_balance = Balances::total_balance(&11); @@ -998,8 +1012,7 @@ fn bond_extra_works() { // Tests that extra `free_balance` in the stash can be added to stake // NOTE: this tests only verifies `StakingLedger` for correct updates // See `bond_extra_and_withdraw_unbonded_works` for more details and updates on `Exposure`. - with_externalities(&mut ExtBuilder::default().build(), - || { + ExtBuilder::default().build().execute_with(|| { // Check that account 10 is a validator assert!(>::exists(11)); // Check that account 10 is bonded to account 11 @@ -1044,10 +1057,7 @@ fn bond_extra_and_withdraw_unbonded_works() { // * It can add extra funds to the bonded account. // * it can unbond a portion of its funds from the stash account. // * Once the unbonding period is done, it can actually take the funds out of the stash. - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { // Set payee to controller. avoids confusion assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); @@ -1131,7 +1141,7 @@ fn bond_extra_and_withdraw_unbonded_works() { #[test] fn too_many_unbond_calls_should_not_work() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { // locked at era 0 until 3 for _ in 0..MAX_UNLOCKING_CHUNKS-1 { assert_ok!(Staking::unbond(Origin::signed(10), 1)); @@ -1160,11 +1170,7 @@ fn too_many_unbond_calls_should_not_work() { fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment() { // Test that slot_stake is determined by the least staked validator // Test that slot_stake is the maximum punishment that can happen to a validator - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .fair(false) - .build(), - || { + ExtBuilder::default().nominate(false).fair(false).build().execute_with(|| { // Confirm validator count is 2 assert_eq!(Staking::validator_count(), 2); // Confirm account 10 and 20 are validators @@ -1213,10 +1219,7 @@ fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment( fn on_free_balance_zero_stash_removes_validator() { // Tests that validator storage items are cleaned up when stash is empty // Tests that storage items are untouched when controller is empty - with_externalities(&mut ExtBuilder::default() - .existential_deposit(10) - .build(), - || { + ExtBuilder::default().existential_deposit(10).build().execute_with(|| { // Check the balance of the validator account assert_eq!(Balances::free_balance(&10), 256); // Check the balance of the stash account @@ -1266,10 +1269,7 @@ fn on_free_balance_zero_stash_removes_validator() { fn on_free_balance_zero_stash_removes_nominator() { // Tests that nominator storage items are cleaned up when stash is empty // Tests that storage items are untouched when controller is empty - with_externalities(&mut ExtBuilder::default() - .existential_deposit(10) - .build(), - || { + ExtBuilder::default().existential_deposit(10).build().execute_with(|| { // Make 10 a nominator assert_ok!(Staking::nominate(Origin::signed(10), vec![20])); // Check that account 10 is a nominator @@ -1322,10 +1322,7 @@ fn on_free_balance_zero_stash_removes_nominator() { #[test] fn switching_roles() { // Test that it should be possible to switch between roles (nominator, validator, idle) with minimal overhead. - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { Timestamp::set_timestamp(1); // Initialize time. // Reset reward destination @@ -1391,11 +1388,7 @@ fn switching_roles() { #[test] fn wrong_vote_is_null() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .validator_pool(true) - .build(), - || { + ExtBuilder::default().nominate(false).validator_pool(true).build().execute_with(|| { assert_eq_uvec!(validator_controllers(), vec![40, 30]); // put some money in account that we'll use. @@ -1419,174 +1412,173 @@ fn wrong_vote_is_null() { fn bond_with_no_staked_value() { // Behavior when someone bonds with no staked value. // Particularly when she votes and the candidate is elected. - with_externalities(&mut ExtBuilder::default() - .validator_count(3) - .existential_deposit(5) - .nominate(false) - .minimum_validator_count(1) - .build(), || { - // Can't bond with 1 - assert_noop!( - Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller), - "can not bond with value less than minimum balance" - ); - // bonded with absolute minimum value possible. - assert_ok!(Staking::bond(Origin::signed(1), 2, 5, RewardDestination::Controller)); - assert_eq!(Balances::locks(&1)[0].amount, 5); + ExtBuilder::default() + .validator_count(3) + .existential_deposit(5) + .nominate(false) + .minimum_validator_count(1) + .build() + .execute_with(|| { + // Can't bond with 1 + assert_noop!( + Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller), + "can not bond with value less than minimum balance", + ); + // bonded with absolute minimum value possible. + assert_ok!(Staking::bond(Origin::signed(1), 2, 5, RewardDestination::Controller)); + assert_eq!(Balances::locks(&1)[0].amount, 5); - // unbonding even 1 will cause all to be unbonded. - assert_ok!(Staking::unbond(Origin::signed(2), 1)); - assert_eq!( - Staking::ledger(2), - Some(StakingLedger { - stash: 1, - active: 0, - total: 5, - unlocking: vec![UnlockChunk {value: 5, era: 3}] - }) - ); + // unbonding even 1 will cause all to be unbonded. + assert_ok!(Staking::unbond(Origin::signed(2), 1)); + assert_eq!( + Staking::ledger(2), + Some(StakingLedger { + stash: 1, + active: 0, + total: 5, + unlocking: vec![UnlockChunk {value: 5, era: 3}] + }) + ); - start_era(1); - start_era(2); + start_era(1); + start_era(2); - // not yet removed. - assert_ok!(Staking::withdraw_unbonded(Origin::signed(2))); - assert!(Staking::ledger(2).is_some()); - assert_eq!(Balances::locks(&1)[0].amount, 5); + // not yet removed. + assert_ok!(Staking::withdraw_unbonded(Origin::signed(2))); + assert!(Staking::ledger(2).is_some()); + assert_eq!(Balances::locks(&1)[0].amount, 5); - start_era(3); + start_era(3); - // poof. Account 1 is removed from the staking system. - assert_ok!(Staking::withdraw_unbonded(Origin::signed(2))); - assert!(Staking::ledger(2).is_none()); - assert_eq!(Balances::locks(&1).len(), 0); - }); + // poof. Account 1 is removed from the staking system. + assert_ok!(Staking::withdraw_unbonded(Origin::signed(2))); + assert!(Staking::ledger(2).is_none()); + assert_eq!(Balances::locks(&1).len(), 0); + }); } #[test] fn bond_with_little_staked_value_bounded_by_slot_stake() { // Behavior when someone bonds with little staked value. // Particularly when she votes and the candidate is elected. - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .validator_count(3) .nominate(false) .minimum_validator_count(1) - .build(), - || { - - // setup - assert_ok!(Staking::chill(Origin::signed(30))); - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); - let init_balance_2 = Balances::free_balance(&2); - let init_balance_10 = Balances::free_balance(&10); - - // Stingy validator. - assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller)); - 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 - reward_all_elected(); - start_era(1); - - // 2 is elected. - // and fucks up the slot stake. - assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); - assert_eq!(Staking::slot_stake(), 1); - - // Old ones are rewarded. - 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); - - let total_payout_1 = current_total_payout_for_duration(3000); - assert!(total_payout_1 > 100); // Test is meaningfull 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(&10), init_balance_10 + total_payout_0 / 3 + total_payout_1 / 3); - check_exposure_all(); - check_nominator_all(); - }); + .build() + .execute_with(|| { + // setup + assert_ok!(Staking::chill(Origin::signed(30))); + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + let init_balance_2 = Balances::free_balance(&2); + let init_balance_10 = Balances::free_balance(&10); + + // Stingy validator. + assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller)); + 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 + reward_all_elected(); + start_era(1); + + // 2 is elected. + // and fucks up the slot stake. + assert_eq_uvec!(validator_controllers(), vec![20, 10, 2]); + assert_eq!(Staking::slot_stake(), 1); + + // Old ones are rewarded. + 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); + + let total_payout_1 = current_total_payout_for_duration(3000); + assert!(total_payout_1 > 100); // Test is meaningfull 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(&10), + init_balance_10 + total_payout_0 / 3 + total_payout_1 / 3, + ); + check_exposure_all(); + check_nominator_all(); + }); } #[cfg(feature = "equalize")] #[test] fn phragmen_linear_worse_case_equalize() { - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .nominate(false) .validator_pool(true) .fair(true) - .build(), - || { - - bond_validator(50, 1000); - bond_validator(60, 1000); - bond_validator(70, 1000); - - bond_nominator(2, 2000, vec![11]); - bond_nominator(4, 1000, vec![11, 21]); - bond_nominator(6, 1000, vec![21, 31]); - bond_nominator(8, 1000, vec![31, 41]); - bond_nominator(110, 1000, vec![41, 51]); - bond_nominator(120, 1000, vec![51, 61]); - bond_nominator(130, 1000, vec![61, 71]); - - for i in &[10, 20, 30, 40, 50, 60, 70] { - assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); - } - - assert_eq_uvec!(validator_controllers(), vec![40, 30]); - assert_ok!(Staking::set_validator_count(Origin::ROOT, 7)); - - start_era(1); - - assert_eq_uvec!(validator_controllers(), vec![10, 60, 40, 20, 50, 30, 70]); - - assert_eq_error_rate!(Staking::stakers(11).total, 3000, 2); - assert_eq_error_rate!(Staking::stakers(21).total, 2255, 2); - assert_eq_error_rate!(Staking::stakers(31).total, 2255, 2); - assert_eq_error_rate!(Staking::stakers(41).total, 1925, 2); - assert_eq_error_rate!(Staking::stakers(51).total, 1870, 2); - assert_eq_error_rate!(Staking::stakers(61).total, 1890, 2); - assert_eq_error_rate!(Staking::stakers(71).total, 1800, 2); - - check_exposure_all(); - check_nominator_all(); - }) + .build() + .execute_with(|| { + bond_validator(50, 1000); + bond_validator(60, 1000); + bond_validator(70, 1000); + + bond_nominator(2, 2000, vec![11]); + bond_nominator(4, 1000, vec![11, 21]); + bond_nominator(6, 1000, vec![21, 31]); + bond_nominator(8, 1000, vec![31, 41]); + bond_nominator(110, 1000, vec![41, 51]); + bond_nominator(120, 1000, vec![51, 61]); + bond_nominator(130, 1000, vec![61, 71]); + + for i in &[10, 20, 30, 40, 50, 60, 70] { + assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); + } + + assert_eq_uvec!(validator_controllers(), vec![40, 30]); + assert_ok!(Staking::set_validator_count(Origin::ROOT, 7)); + + start_era(1); + + assert_eq_uvec!(validator_controllers(), vec![10, 60, 40, 20, 50, 30, 70]); + + assert_eq_error_rate!(Staking::stakers(11).total, 3000, 2); + assert_eq_error_rate!(Staking::stakers(21).total, 2255, 2); + assert_eq_error_rate!(Staking::stakers(31).total, 2255, 2); + assert_eq_error_rate!(Staking::stakers(41).total, 1925, 2); + assert_eq_error_rate!(Staking::stakers(51).total, 1870, 2); + assert_eq_error_rate!(Staking::stakers(61).total, 1890, 2); + assert_eq_error_rate!(Staking::stakers(71).total, 1800, 2); + + check_exposure_all(); + check_nominator_all(); + }) } #[test] fn new_era_elects_correct_number_of_validators() { - with_externalities(&mut ExtBuilder::default() + ExtBuilder::default() .nominate(true) .validator_pool(true) .fair(true) .validator_count(1) - .build(), - || { - assert_eq!(Staking::validator_count(), 1); - assert_eq!(validator_controllers().len(), 1); - - System::set_block_number(1); - Session::on_initialize(System::block_number()); - - assert_eq!(validator_controllers().len(), 1); - check_exposure_all(); - check_nominator_all(); - }) + .build() + .execute_with(|| { + assert_eq!(Staking::validator_count(), 1); + assert_eq!(validator_controllers().len(), 1); + + System::set_block_number(1); + Session::on_initialize(System::block_number()); + + assert_eq!(validator_controllers().len(), 1); + check_exposure_all(); + check_nominator_all(); + }) } #[test] fn phragmen_should_not_overflow_validators() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { let _ = Staking::chill(Origin::signed(10)); let _ = Staking::chill(Origin::signed(20)); @@ -1609,10 +1601,7 @@ fn phragmen_should_not_overflow_validators() { #[test] fn phragmen_should_not_overflow_nominators() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { let _ = Staking::chill(Origin::signed(10)); let _ = Staking::chill(Origin::signed(20)); @@ -1634,10 +1623,7 @@ fn phragmen_should_not_overflow_nominators() { #[test] fn phragmen_should_not_overflow_ultimate() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { + ExtBuilder::default().nominate(false).build().execute_with(|| { bond_validator(2, u64::max_value()); bond_validator(4, u64::max_value()); @@ -1656,9 +1642,7 @@ fn phragmen_should_not_overflow_ultimate() { #[test] fn reward_validator_slashing_validator_doesnt_overflow() { - with_externalities(&mut ExtBuilder::default() - .build(), - || { + ExtBuilder::default().build().execute_with(|| { let stake = u32::max_value() as u64 * 2; let reward_slash = u32::max_value() as u64 * 2; @@ -1689,9 +1673,7 @@ fn reward_validator_slashing_validator_doesnt_overflow() { #[test] fn reward_from_authorship_event_handler_works() { - with_externalities(&mut ExtBuilder::default() - .build(), - || { + ExtBuilder::default().build().execute_with(|| { use authorship::EventHandler; assert_eq!(>::author(), 11); @@ -1716,9 +1698,7 @@ fn reward_from_authorship_event_handler_works() { #[test] fn add_reward_points_fns_works() { - with_externalities(&mut ExtBuilder::default() - .build(), - || { + ExtBuilder::default().build().execute_with(|| { let validators = >::current_elected(); // Not mandatory but must be coherent with rewards assert_eq!(validators, vec![21, 11]); @@ -1744,7 +1724,7 @@ fn add_reward_points_fns_works() { #[test] fn unbonded_balance_is_not_slashable() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { // total amount staked is slashable. assert_eq!(Staking::slashable_balance_of(&11), 1000); @@ -1759,7 +1739,7 @@ fn unbonded_balance_is_not_slashable() { fn era_is_always_same_length() { // This ensures that the sessions is always of the same length if there is no forcing no // session changes. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { start_era(1); assert_eq!(Staking::current_era_start_session_index(), SessionsPerEra::get()); @@ -1779,7 +1759,27 @@ fn era_is_always_same_length() { #[test] fn offence_forces_new_era() { - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { + Staking::on_offence( + &[OffenceDetails { + offender: ( + 11, + Staking::stakers(&11), + ), + reporters: vec![], + }], + &[Perbill::from_percent(5)], + ); + + assert_eq!(Staking::force_era(), Forcing::ForceNew); + }); +} + +#[test] +fn offence_ensures_new_era_without_clobbering() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Staking::force_new_era_always(Origin::ROOT)); + Staking::on_offence( &[OffenceDetails { offender: ( @@ -1791,7 +1791,26 @@ fn offence_forces_new_era() { &[Perbill::from_percent(5)], ); + assert_eq!(Staking::force_era(), Forcing::ForceAlways); + }); +} + +#[test] +fn offence_deselects_validator_when_slash_is_zero() { + ExtBuilder::default().build().execute_with(|| { + assert!(>::exists(11)); + Staking::on_offence( + &[OffenceDetails { + offender: ( + 11, + Staking::stakers(&11), + ), + reporters: vec![], + }], + &[Perbill::from_percent(0)], + ); assert_eq!(Staking::force_era(), Forcing::ForceNew); + assert!(!>::exists(11)); }); } @@ -1799,7 +1818,7 @@ fn offence_forces_new_era() { fn slashing_performed_according_exposure() { // This test checks that slashing is performed according the exposure (or more precisely, // historical exposure), not the current balance. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Staking::stakers(&11).own, 1000); // Handle an offence with a historical exposure. @@ -1827,7 +1846,7 @@ fn slashing_performed_according_exposure() { fn reporters_receive_their_slice() { // This test verifies that the reporters of the offence receive their slice from the slashed // amount. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { // The reporters' reward is calculated from the total exposure. #[cfg(feature = "equalize")] let initial_balance = 1250; @@ -1857,44 +1876,41 @@ fn reporters_receive_their_slice() { #[test] fn invulnerables_are_not_slashed() { // For invulnerable validators no slashing is performed. - with_externalities( - &mut ExtBuilder::default().invulnerables(vec![11]).build(), - || { - #[cfg(feature = "equalize")] - let initial_balance = 1250; - #[cfg(not(feature = "equalize"))] - let initial_balance = 1375; - - assert_eq!(Balances::free_balance(&11), 1000); - assert_eq!(Balances::free_balance(&21), 2000); - assert_eq!(Staking::stakers(&21).total, initial_balance); - - Staking::on_offence( - &[ - OffenceDetails { - offender: (11, Staking::stakers(&11)), - reporters: vec![], - }, - OffenceDetails { - offender: (21, Staking::stakers(&21)), - reporters: vec![], - }, - ], - &[Perbill::from_percent(50), Perbill::from_percent(20)], - ); + ExtBuilder::default().invulnerables(vec![11]).build().execute_with(|| { + #[cfg(feature = "equalize")] + let initial_balance = 1250; + #[cfg(not(feature = "equalize"))] + let initial_balance = 1375; - // The validator 11 hasn't been slashed, but 21 has been. - 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(&11), 1000); + assert_eq!(Balances::free_balance(&21), 2000); + assert_eq!(Staking::stakers(&21).total, initial_balance); + + Staking::on_offence( + &[ + OffenceDetails { + offender: (11, Staking::stakers(&11)), + reporters: vec![], + }, + OffenceDetails { + offender: (21, Staking::stakers(&21)), + reporters: vec![], + }, + ], + &[Perbill::from_percent(50), Perbill::from_percent(20)], + ); + + // The validator 11 hasn't been slashed, but 21 has been. + assert_eq!(Balances::free_balance(&11), 1000); + // 2000 - (0.2 * initial_balance) + assert_eq!(Balances::free_balance(&21), 2000 - (2 * initial_balance / 10)); + }); } #[test] fn dont_slash_if_fraction_is_zero() { // Don't slash if the fraction is zero. - with_externalities(&mut ExtBuilder::default().build(), || { + ExtBuilder::default().build().execute_with(|| { assert_eq!(Balances::free_balance(&11), 1000); Staking::on_offence( @@ -1910,6 +1926,5 @@ 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!(Staking::force_era(), Forcing::NotForcing); }); } diff --git a/srml/sudo/src/lib.rs b/srml/sudo/src/lib.rs index c4e13eefe24ef1ce124c5d5bbbedf85e607cf383..7801384abcc2fe7b3f3e7e4fb2102e3d6a41221e 100644 --- a/srml/sudo/src/lib.rs +++ b/srml/sudo/src/lib.rs @@ -200,6 +200,6 @@ decl_event!( decl_storage! { trait Store for Module as Sudo { /// The `AccountId` of the sudo key. - Key get(key) config(): T::AccountId; + Key get(fn key) config(): T::AccountId; } } diff --git a/srml/support/Cargo.toml b/srml/support/Cargo.toml index 79a3beb6dc6af08262acb63aee651ae8a43a109e..46ebebc7d3bf4190f7fed44959d471eda745fe26 100644 --- a/srml/support/Cargo.toml +++ b/srml/support/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [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"] } srml-metadata = { path = "../metadata", default-features = false } @@ -17,7 +18,7 @@ srml-support-procedural = { package = "srml-support-procedural", path = "./proce paste = "0.1.6" once_cell = { version = "0.2.4", default-features = false, optional = true } bitmask = { version = "0.5.0", default-features = false } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/srml/support/procedural/src/lib.rs b/srml/support/procedural/src/lib.rs index a9cfa8d30d1b180cd5b55c19bccdc0b4842dc5c9..792f739f1c35761c6bc370fd0cfb112f5e403a3a 100644 --- a/srml/support/procedural/src/lib.rs +++ b/srml/support/procedural/src/lib.rs @@ -33,7 +33,7 @@ use proc_macro::TokenStream; /// ```nocompile /// decl_storage! { /// trait Store for Module as Example { -/// Foo get(foo) config(): u32=12; +/// Foo get(fn foo) config(): u32=12; /// Bar: map u32 => u32; /// pub Zed build(|config| vec![(0, 0)]): linked_map u32 => u32; /// } @@ -120,11 +120,11 @@ use proc_macro::TokenStream; /// /// Basic storage can be extended as such: /// -/// `#vis #name get(#getter) config(#field_name) build(#closure): #type = #default;` +/// `#vis #name get(fn #getter) config(#field_name) build(#closure): #type = #default;` /// /// * `#vis`: Set the visibility of the structure. `pub` or nothing. /// * `#name`: Name of the storage item, used as a prefix in storage. -/// * [optional] `get(#getter)`: Implements the function #getter to `Module`. +/// * [optional] `get(fn #getter)`: Implements the function #getter to `Module`. /// * [optional] `config(#field_name)`: `field_name` is optional if get is set. /// Will include the item in `GenesisConfig`. /// * [optional] `build(#closure)`: Closure called with storage overlays. @@ -213,5 +213,5 @@ use proc_macro::TokenStream; /// #[proc_macro] pub fn decl_storage(input: TokenStream) -> TokenStream { - storage::transformation::decl_storage_impl(input) + storage::decl_storage_impl(input) } diff --git a/srml/support/procedural/src/storage/genesis_config/builder_def.rs b/srml/support/procedural/src/storage/genesis_config/builder_def.rs new file mode 100644 index 0000000000000000000000000000000000000000..fc425e4a6d53261fb5f9868127be0ce90ddff5e1 --- /dev/null +++ b/srml/support/procedural/src/storage/genesis_config/builder_def.rs @@ -0,0 +1,111 @@ +// Copyright 2017-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 . + +//! Builder logic definition used to build genesis storage. + +use srml_support_procedural_tools::syn_ext as ext; +use proc_macro2::TokenStream; +use syn::spanned::Spanned; +use quote::{quote, quote_spanned}; +use super::super::{DeclStorageDefExt, StorageLineTypeDef}; + +/// Definition of builder blocks, each block insert some value in the storage. +/// They must be called inside externalities, and with `self` being the genesis config. +pub struct BuilderDef { + /// Contains: + /// * build block for storage with build attribute. + /// * build block for storage with config attribute and no build attribute. + /// * build block for extra genesis build expression. + pub blocks: Vec, + /// The build blocks requires generic traits. + pub is_generic: bool, +} + +impl BuilderDef { + pub fn from_def(scrate: &TokenStream, def: &DeclStorageDefExt) -> Self { + let mut blocks = Vec::new(); + let mut is_generic = false; + + for line in def.storage_lines.iter() { + let storage_struct = &line.storage_struct; + let storage_trait = &line.storage_trait; + let value_type = &line.value_type; + + // Contains the data to inset at genesis either from build or config. + let mut data = None; + + if let Some(builder) = &line.build { + is_generic |= ext::expr_contains_ident(&builder, &def.module_runtime_generic); + is_generic |= line.is_generic; + + data = Some(quote_spanned!(builder.span() => &(#builder)(self))); + } else if let Some(config) = &line.config { + is_generic |= line.is_generic; + + data = Some(quote!(&self.#config;)); + }; + + if let Some(data) = data { + blocks.push(match &line.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!{{ + let v: &#value_type = #data; + <#storage_struct as #scrate::#storage_trait>::put::<&#value_type>(v); + }} + }, + StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + quote!{{ + let data: &#scrate::rstd::vec::Vec<(#key, #value_type)> = #data; + data.iter().for_each(|(k, v)| { + <#storage_struct as #scrate::#storage_trait>::insert::< + &#key, &#value_type + >(k, v); + }); + }} + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + quote!{{ + let data: &#scrate::rstd::vec::Vec<(#key1, #key2, #value_type)> = #data; + data.iter().for_each(|(k1, k2, v)| { + <#storage_struct as #scrate::#storage_trait>::insert::< + &#key1, &#key2, &#value_type + >(k1, k2, v); + }); + }} + }, + }); + } + } + + if let Some(builder) = def.extra_genesis_build.as_ref() { + is_generic |= ext::expr_contains_ident(&builder, &def.module_runtime_generic); + + blocks.push(quote_spanned! { builder.span() => + let extra_genesis_builder: fn(&Self) = #builder; + extra_genesis_builder(self); + }); + } + + + Self { + blocks, + is_generic, + } + } +} diff --git a/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs b/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs new file mode 100644 index 0000000000000000000000000000000000000000..f9d2f8abe8055a8128ba1ef222f5e5342ba1b138 --- /dev/null +++ b/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs @@ -0,0 +1,144 @@ +// Copyright 2017-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 . + +//! Genesis config defintion. + +use srml_support_procedural_tools::syn_ext as ext; +use proc_macro2::TokenStream; +use syn::parse_quote; +use quote::quote; +use super::super::{DeclStorageDefExt, StorageLineTypeDef}; + +pub struct GenesisConfigFieldDef { + pub doc: Vec, + pub name: syn::Ident, + pub typ: syn::Type, + pub default: TokenStream, +} + +pub struct GenesisConfigDef { + pub is_generic: bool, + pub fields: Vec, + /// For example: `, I: Instance=DefaultInstance>`. + pub genesis_struct_decl: TokenStream, + /// For example: ``. + pub genesis_struct: TokenStream, + /// For example: `, I: Instance>`. + pub genesis_impl: TokenStream, + /// The where clause to use to constrain generics if genesis config is generic. + pub genesis_where_clause: Option, +} + +impl GenesisConfigDef { + pub fn from_def(def: &DeclStorageDefExt) -> Self { + let fields = Self::get_genesis_config_field_defs(def); + + let is_generic = fields.iter() + .any(|field| ext::type_contains_ident(&field.typ, &def.module_runtime_generic)); + + let ( + genesis_struct_decl, + genesis_impl, + genesis_struct, + genesis_where_clause + ) = if is_generic { + let runtime_generic = &def.module_runtime_generic; + let runtime_trait = &def.module_runtime_trait; + let optional_instance = &def.optional_instance; + let optional_instance_bound = &def.optional_instance_bound; + let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default; + let where_clause = &def.where_clause; + ( + quote!(<#runtime_generic: #runtime_trait, #optional_instance_bound_optional_default>), + quote!(<#runtime_generic: #runtime_trait, #optional_instance_bound>), + quote!(<#runtime_generic, #optional_instance>), + where_clause.clone(), + ) + } else { + (quote!(), quote!(), quote!(), None) + }; + + Self { + is_generic, + fields, + genesis_struct_decl, + genesis_struct, + genesis_impl, + genesis_where_clause, + } + } + + fn get_genesis_config_field_defs(def: &DeclStorageDefExt) -> Vec { + let mut config_field_defs = Vec::new(); + + for (config_field, line) in def.storage_lines.iter() + .filter_map(|line| line.config.as_ref().map(|config_field| (config_field.clone(), line))) + { + let value_type = &line.value_type; + + let typ = match &line.storage_type { + StorageLineTypeDef::Simple(_) => (*value_type).clone(), + StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + parse_quote!( Vec<(#key, #value_type)> ) + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + + parse_quote!( Vec<(#key1, #key2, #value_type)> ) + }, + }; + + let default = line.default_value.as_ref() + .map(|d| { + if line.is_option { + quote!( #d.unwrap_or_default() ) + } else { + quote!( #d ) + } + }) + .unwrap_or_else(|| quote!( Default::default() )); + + config_field_defs.push(GenesisConfigFieldDef { + doc: line.doc_attrs.clone(), + name: config_field, + typ, + default, + }); + } + + for line in &def.extra_genesis_config_lines { + let doc = line.attrs.iter() + .filter_map(|a| a.parse_meta().ok()) + .filter(|m| m.name() == "doc") + .collect(); + + let default = line.default.as_ref().map(|e| quote!( #e )) + .unwrap_or_else(|| quote!( Default::default() )); + + + config_field_defs.push(GenesisConfigFieldDef { + doc, + name: line.name.clone(), + typ: line.typ.clone(), + default, + }); + } + + config_field_defs + } +} diff --git a/srml/support/procedural/src/storage/genesis_config/mod.rs b/srml/support/procedural/src/storage/genesis_config/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..2d4d4af3861ed56c186f15de4352b0aa0a9ca372 --- /dev/null +++ b/srml/support/procedural/src/storage/genesis_config/mod.rs @@ -0,0 +1,203 @@ +// Copyright 2017-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 . + +//! Declaration of genesis config structure and implementation of build storage trait and +//! functions. + +use proc_macro2::{TokenStream, Span}; +use quote::quote; +use super::{DeclStorageDefExt, instance_trait::DEFAULT_INSTANTIABLE_TRAIT_NAME}; +use genesis_config_def::GenesisConfigDef; +use builder_def::BuilderDef; + +mod genesis_config_def; +mod builder_def; + +const DEFAULT_INSTANCE_NAME: &str = "__GeneratedInstance"; + +fn decl_genesis_config_and_impl_default( + scrate: &TokenStream, + genesis_config: &GenesisConfigDef, +) -> TokenStream { + let config_fields = genesis_config.fields.iter().map(|field| { + let (name, typ, doc) = (&field.name, &field.typ, &field.doc); + quote!( #( #[ #doc] )* pub #name: #typ, ) + }); + + let config_field_defaults = genesis_config.fields.iter().map(|field| { + let (name, default, doc) = (&field.name, &field.default, &field.doc); + quote!( #( #[ #doc] )* #name: #default, ) + }); + + let serde_bug_bound = if !genesis_config.fields.is_empty() { + let mut b_ser = String::new(); + let mut b_dser = String::new(); + + for typ in genesis_config.fields.iter().map(|c| &c.typ) { + let typ = quote!( #typ ); + b_ser.push_str(&format!("{} : {}::serde::Serialize, ", typ, scrate)); + b_dser.push_str(&format!("{} : {}::serde::de::DeserializeOwned, ", typ, scrate)); + } + + quote! { + #[serde(bound(serialize = #b_ser))] + #[serde(bound(deserialize = #b_dser))] + } + } else { + quote!() + }; + + let genesis_struct_decl = &genesis_config.genesis_struct_decl; + let genesis_struct = &genesis_config.genesis_struct; + let genesis_impl = &genesis_config.genesis_impl; + let genesis_where_clause = &genesis_config.genesis_where_clause; + + quote!( + #[derive(#scrate::Serialize, #scrate::Deserialize)] + #[cfg(feature = "std")] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #serde_bug_bound + pub struct GenesisConfig#genesis_struct_decl #genesis_where_clause { + #( #config_fields )* + } + + #[cfg(feature = "std")] + impl#genesis_impl Default for GenesisConfig#genesis_struct #genesis_where_clause { + fn default() -> Self { + GenesisConfig { + #( #config_field_defaults )* + } + } + } + ) +} + +fn impl_build_storage( + scrate: &TokenStream, + def: &DeclStorageDefExt, + genesis_config: &GenesisConfigDef, + builders: &BuilderDef, +) -> TokenStream { + let runtime_generic = &def.module_runtime_generic; + let runtime_trait = &def.module_runtime_trait; + let optional_instance = &def.optional_instance; + let optional_instance_bound = &def.optional_instance_bound; + let where_clause = &def.where_clause; + + let inherent_instance = def.optional_instance.clone().unwrap_or_else(|| { + let name = syn::Ident::new(DEFAULT_INSTANCE_NAME, Span::call_site()); + quote!( #name ) + }); + let inherent_instance_bound = def.optional_instance_bound.clone().unwrap_or_else(|| { + let bound = syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site()); + quote!( #inherent_instance: #bound ) + }); + + let build_storage_impl = quote!( + <#runtime_generic: #runtime_trait, #inherent_instance_bound> + ); + + let genesis_struct = &genesis_config.genesis_struct; + let genesis_impl = &genesis_config.genesis_impl; + let genesis_where_clause = &genesis_config.genesis_where_clause; + + let ( + fn_generic, + fn_traitinstance, + fn_where_clause + ) = if !genesis_config.is_generic && builders.is_generic { + ( + quote!( <#runtime_generic: #runtime_trait, #optional_instance_bound> ), + quote!( #runtime_generic, #optional_instance ), + Some(&def.where_clause), + ) + } else { + (quote!(), quote!(), None) + }; + + let builder_blocks = &builders.blocks; + + let build_storage_impl_trait = quote!( + #scrate::sr_primitives::BuildModuleGenesisStorage<#runtime_generic, #inherent_instance> + ); + + quote!{ + #[cfg(feature = "std")] + impl#genesis_impl GenesisConfig#genesis_struct #genesis_where_clause { + pub fn build_storage #fn_generic (&self) -> std::result::Result< + ( + #scrate::sr_primitives::StorageOverlay, + #scrate::sr_primitives::ChildrenStorageOverlay, + ), + String + > #fn_where_clause { + let mut storage = (Default::default(), Default::default()); + self.assimilate_storage::<#fn_traitinstance>(&mut storage)?; + Ok(storage) + } + + /// Assimilate the storage for this module into pre-existing overlays. + pub fn assimilate_storage #fn_generic ( + &self, + tuple_storage: &mut ( + #scrate::sr_primitives::StorageOverlay, + #scrate::sr_primitives::ChildrenStorageOverlay, + ), + ) -> std::result::Result<(), String> #fn_where_clause { + #scrate::with_storage(tuple_storage, || { + #( #builder_blocks )* + Ok(()) + }) + } + } + + #[cfg(feature = "std")] + impl#build_storage_impl #build_storage_impl_trait for GenesisConfig#genesis_struct + #where_clause + { + fn build_module_genesis_storage( + &self, + storage: &mut ( + #scrate::sr_primitives::StorageOverlay, + #scrate::sr_primitives::ChildrenStorageOverlay, + ), + ) -> std::result::Result<(), String> { + self.assimilate_storage::<#fn_traitinstance> (storage) + } + } + } +} + +pub fn genesis_config_and_build_storage( + scrate: &TokenStream, + def: &DeclStorageDefExt, +) -> TokenStream { + let builders = BuilderDef::from_def(scrate, def); + if !builders.blocks.is_empty() { + let genesis_config = &GenesisConfigDef::from_def(def); + let decl_genesis_config_and_impl_default = + decl_genesis_config_and_impl_default(scrate, genesis_config); + let impl_build_storage = impl_build_storage(scrate, def, genesis_config, &builders); + + quote!{ + #decl_genesis_config_and_impl_default + #impl_build_storage + } + } else { + quote!() + } +} diff --git a/srml/support/procedural/src/storage/getters.rs b/srml/support/procedural/src/storage/getters.rs new file mode 100644 index 0000000000000000000000000000000000000000..f30e489eb58bc7907bcce3fed2bae031ae12e893 --- /dev/null +++ b/srml/support/procedural/src/storage/getters.rs @@ -0,0 +1,80 @@ +// Copyright 2017-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 . + +//! Implementation of getters on module structure. + +use proc_macro2::TokenStream; +use quote::quote; +use super::{DeclStorageDefExt, StorageLineTypeDef}; + +pub fn impl_getters(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut getters = TokenStream::new(); + + for (get_fn, line) in def.storage_lines.iter() + .filter_map(|line| line.getter.as_ref().map(|get_fn| (get_fn, line))) + { + let attrs = &line.doc_attrs; + + let storage_struct = &line.storage_struct; + let storage_trait = &line.storage_trait; + + let getter = match &line.storage_type { + StorageLineTypeDef::Simple(value) => { + quote!{ + #( #[ #attrs ] )* + pub fn #get_fn() -> #value { + <#storage_struct as #scrate::#storage_trait>::get() + } + } + }, + StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + let value = &map.value; + quote!{ + #( #[ #attrs ] )* + pub fn #get_fn>(key: K) -> #value { + <#storage_struct as #scrate::#storage_trait>::get(key) + } + } + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + let value = &map.value; + quote!{ + pub fn #get_fn(k1: KArg1, k2: KArg2) -> #value + where + KArg1: #scrate::codec::EncodeLike<#key1>, + KArg2: #scrate::codec::EncodeLike<#key2>, + { + <#storage_struct as #scrate::#storage_trait>::get(k1, k2) + } + } + }, + }; + getters.extend(getter); + } + + let module_struct = &def.module_struct; + let module_impl = &def.module_impl; + let where_clause = &def.where_clause; + + quote!( + impl#module_impl #module_struct #where_clause { + #getters + } + ) +} diff --git a/srml/support/procedural/src/storage/impls.rs b/srml/support/procedural/src/storage/impls.rs deleted file mode 100644 index ee6fa9a1642ffbedfa8cc59449ece55d92488972..0000000000000000000000000000000000000000 --- a/srml/support/procedural/src/storage/impls.rs +++ /dev/null @@ -1,418 +0,0 @@ -// 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 . - -use crate::storage::transformation::{DeclStorageTypeInfos, InstanceOpts}; - -use srml_support_procedural_tools::syn_ext as ext; -use proc_macro2::TokenStream as TokenStream2; -use syn::Ident; -use quote::quote; - -fn from_optional_value_to_query(is_option: bool, fielddefault: TokenStream2) -> TokenStream2 { - if !is_option { - // raw type case - quote!( v.unwrap_or_else(|| #fielddefault ) ) - } else { - // Option<> type case - quote!( v.or_else(|| #fielddefault ) ) - } -} - -fn from_query_to_optional_value(is_option: bool) -> TokenStream2 { - if !is_option { - // raw type case - quote!( Some(v) ) - } else { - // Option<> type case - quote!( v ) - } -} - -// prefix for consts in trait Instance -pub(crate) const PREFIX_FOR: &str = "PREFIX_FOR_"; -pub(crate) const HEAD_KEY_FOR: &str = "HEAD_KEY_FOR_"; - -pub(crate) struct Impls<'a, I: Iterator> { - pub scrate: &'a TokenStream2, - pub visibility: &'a syn::Visibility, - pub traitinstance: &'a syn::Ident, - pub traittype: &'a syn::TypeParamBound, - pub instance_opts: &'a InstanceOpts, - pub type_infos: DeclStorageTypeInfos<'a>, - pub fielddefault: TokenStream2, - pub prefix: String, - pub cratename: &'a syn::Ident, - pub name: &'a syn::Ident, - pub attrs: I, - pub where_clause: &'a Option, -} - -impl<'a, I: Iterator> Impls<'a, I> { - pub fn simple_value(self) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix, - name, - attrs, - where_clause, - .. - } = self; - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = Ident::new(&format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site()); - quote!{ #instance::#const_name.as_bytes() } - } else { - quote!{ #prefix.as_bytes() } - }; - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if ext::type_contains_ident( - value_type, traitinstance - ) { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for value - quote! { - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait>( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageValue<#typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - - fn unhashed_key() -> &'static [u8] { - #final_prefix - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } - - pub fn map(self, hasher: TokenStream2, kty: &syn::Type) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix, - name, - attrs, - where_clause, - .. - } = self; - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = syn::Ident::new( - &format!("{}{}", PREFIX_FOR, name.to_string()), - proc_macro2::Span::call_site(), - ); - quote! { #instance::#const_name.as_bytes() } - } else { - quote! { #prefix.as_bytes() } - }; - - let trait_required = ext::type_contains_ident(value_type, traitinstance) - || ext::type_contains_ident(kty, traitinstance); - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if trait_required { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for map - quote!{ - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait>( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageMap<#kty, #typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - type Hasher = #scrate::#hasher; - - fn prefix() -> &'static [u8] { - #final_prefix - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } - - pub fn linked_map(self, hasher: TokenStream2, kty: &syn::Type) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix, - name, - attrs, - where_clause, - .. - } = self; - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = Ident::new( - &format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - quote!{ #instance::#const_name.as_bytes() } - } else { - quote!{ #prefix.as_bytes() } - }; - - // make sure to use different prefix for head and elements. - let head_key = if let Some(instance) = instance { - let const_name = Ident::new( - &format!("{}{}", HEAD_KEY_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - quote!{ #instance::#const_name.as_bytes() } - } else { - let head_key = format!("head of {}", prefix); - quote!{ #head_key.as_bytes() } - }; - - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let trait_required = ext::type_contains_ident(value_type, traitinstance) - || ext::type_contains_ident(kty, traitinstance); - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if trait_required { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for linked map - quote! { - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait>( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageLinkedMap<#kty, #typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - type Hasher = #scrate::#hasher; - - fn prefix() -> &'static [u8] { - #final_prefix - } - - fn head_key() -> &'static [u8] { - #head_key - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } - - pub fn double_map( - self, - hasher: TokenStream2, - k1ty: &syn::Type, - k2ty: &syn::Type, - k2_hasher: TokenStream2, - ) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - type_infos, - fielddefault, - prefix, - name, - attrs, - instance_opts, - where_clause, - .. - } = self; - - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = Ident::new( - &format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - quote!{ #instance::#const_name.as_bytes() } - } else { - quote!{ #prefix.as_bytes() } - }; - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if ext::type_contains_ident( - value_type, traitinstance - ) || ext::type_contains_ident(k1ty, traitinstance) || ext::type_contains_ident(k2ty, traitinstance) - { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for double map - quote!{ - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait> ( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageDoubleMap<#k1ty, #k2ty, #typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - - type Hasher1 = #scrate::#hasher; - - type Hasher2 = #scrate::#k2_hasher; - - fn key1_prefix() -> &'static [u8] { - #final_prefix - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } -} diff --git a/srml/support/procedural/src/storage/instance_trait.rs b/srml/support/procedural/src/storage/instance_trait.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a7add89a4b80024f35f512176db7eba61655fd2 --- /dev/null +++ b/srml/support/procedural/src/storage/instance_trait.rs @@ -0,0 +1,200 @@ +// Copyright 2017-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 . + +//! Implementation of the trait instance and the instance structures implementing it. +//! (For not instantiable traits there is still the inherent instance implemented). + +use proc_macro2::{TokenStream, Span}; +use quote::quote; +use super::{DeclStorageDefExt, StorageLineTypeDef}; + +const NUMBER_OF_INSTANCE: usize = 16; +const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; +pub(crate) const DEFAULT_INSTANTIABLE_TRAIT_NAME: &str = "__GeneratedInstantiable"; + +// prefix for consts in trait Instance +pub(crate) const PREFIX_FOR: &str = "PREFIX_FOR_"; +pub(crate) const HEAD_KEY_FOR: &str = "HEAD_KEY_FOR_"; + +// Used to generate the const: +// `const $name: &'static str = $value_prefix ++ instance_prefix ++ $value_suffix` +struct InstanceConstDef { + name: syn::Ident, + value_prefix: String, + value_suffix: String, +} + +// Used to generate an instance implementation. +struct InstanceDef { + prefix: String, + instance_struct: syn::Ident, + doc: TokenStream, +} + +pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut impls = TokenStream::new(); + + let mut const_defs = vec![]; + + for line in def.storage_lines.iter() { + let storage_prefix = format!("{} {}", def.crate_name, line.name); + + let const_name = syn::Ident::new( + &format!("{}{}", PREFIX_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + const_defs.push(InstanceConstDef { + name: const_name, + value_prefix: String::new(), + value_suffix: storage_prefix.clone(), + }); + + if let StorageLineTypeDef::LinkedMap(_) = line.storage_type { + let const_name = syn::Ident::new( + &format!("{}{}", HEAD_KEY_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + const_defs.push(InstanceConstDef { + name: const_name, + value_prefix: "head of ".into(), + value_suffix: storage_prefix, + }); + } + } + + impls.extend(create_instance_trait(&const_defs, def)); + + // Implementation of instances. + if let Some(module_instance) = &def.module_instance { + let instance_defs = (0..NUMBER_OF_INSTANCE) + .map(|i| { + let name = format!("Instance{}", i); + InstanceDef { + instance_struct: syn::Ident::new(&name, proc_macro2::Span::call_site()), + prefix: name, + doc: quote!(#[doc=r"Module instance"]), + } + }) + .chain( + module_instance.instance_default.as_ref().map(|ident| InstanceDef { + prefix: String::new(), + instance_struct: ident.clone(), + doc: quote!(#[doc=r"Default module instance"]), + }) + ); + + for instance_def in instance_defs { + impls.extend(create_and_impl_instance_struct(scrate, &instance_def, &const_defs, def)); + } + } + + // The name of the inherently available instance. + let inherent_instance = syn::Ident::new(INHERENT_INSTANCE_NAME, Span::call_site()); + + // Implementation of inherent instance. + if let Some(default_instance) = def.module_instance.as_ref() + .and_then(|i| i.instance_default.as_ref()) + { + impls.extend(quote! { + #[doc(hidden)] + pub type #inherent_instance = #default_instance; + }); + } else { + let instance_def = InstanceDef { + prefix: String::new(), + instance_struct: inherent_instance, + doc: quote!(#[doc(hidden)]), + }; + impls.extend(create_and_impl_instance_struct(scrate, &instance_def, &const_defs, def)); + } + + impls +} + +fn create_instance_trait( + const_defs: &[InstanceConstDef], + def: &DeclStorageDefExt, +) -> TokenStream { + let instance_trait = def.module_instance.as_ref().map(|i| i.instance_trait.clone()) + .unwrap_or_else(|| syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); + + let mut const_impls = TokenStream::new(); + for const_def in const_defs { + let const_name = &const_def.name; + const_impls.extend(quote! { + const #const_name: &'static str; + }); + } + + let optional_hide = if def.module_instance.is_some() { + quote!() + } else { + quote!(#[doc(hidden)]) + }; + + quote! { + /// Tag a type as an instance of a module. + /// + /// Defines storage prefixes, they must be unique. + #optional_hide + pub trait #instance_trait: 'static { + /// The prefix used by any storage entry of an instance. + const PREFIX: &'static str; + #const_impls + } + } +} + +fn create_and_impl_instance_struct( + scrate: &TokenStream, + instance_def: &InstanceDef, + const_defs: &[InstanceConstDef], + def: &DeclStorageDefExt, +) -> TokenStream { + let mut const_impls = TokenStream::new(); + + for const_def in const_defs { + let const_value = format!( + "{}{}{}", const_def.value_prefix, instance_def.prefix, const_def.value_suffix + ); + let const_name = &const_def.name; + + const_impls.extend(quote! { + const #const_name: &'static str = #const_value; + }); + } + + let instance_trait = def.module_instance.as_ref().map(|i| i.instance_trait.clone()) + .unwrap_or_else(|| syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); + + let instance_struct = &instance_def.instance_struct; + let prefix = format!("{}{}", instance_def.prefix, def.crate_name.to_string()); + let doc = &instance_def.doc; + + quote! { + // Those trait are derived because of wrong bounds for generics + #[derive( + Clone, Eq, PartialEq, + #scrate::codec::Encode, + #scrate::codec::Decode, + #scrate::RuntimeDebug, + )] + #doc + pub struct #instance_struct; + impl #instance_trait for #instance_struct { + const PREFIX: &'static str = #prefix; + #const_impls + } + } +} diff --git a/srml/support/procedural/src/storage/metadata.rs b/srml/support/procedural/src/storage/metadata.rs new file mode 100644 index 0000000000000000000000000000000000000000..e280c7d8a20c6c45fc88eb25d7cd1af4905094cc --- /dev/null +++ b/srml/support/procedural/src/storage/metadata.rs @@ -0,0 +1,230 @@ +// Copyright 2017-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 . + +//! Implementation of `storage_metadata` on module structure, used by construct_runtime. + +use srml_support_procedural_tools::clean_type_string; +use proc_macro2::TokenStream; +use quote::quote; +use super::{DeclStorageDefExt, StorageLineDefExt, StorageLineTypeDef}; + +fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> TokenStream { + let value_type = &line.value_type; + let value_type = clean_type_string("e!( #value_type ).to_string()); + match &line.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!{ + #scrate::metadata::StorageEntryType::Plain( + #scrate::metadata::DecodeDifferent::Encode(#value_type), + ) + } + }, + StorageLineTypeDef::Map(map) => { + let hasher = map.hasher.into_metadata(); + let key = &map.key; + let key = clean_type_string("e!(#key).to_string()); + quote!{ + #scrate::metadata::StorageEntryType::Map { + hasher: #scrate::metadata::#hasher, + key: #scrate::metadata::DecodeDifferent::Encode(#key), + value: #scrate::metadata::DecodeDifferent::Encode(#value_type), + is_linked: false, + } + } + }, + StorageLineTypeDef::LinkedMap(map) => { + let hasher = map.hasher.into_metadata(); + let key = &map.key; + let key = clean_type_string("e!(#key).to_string()); + quote!{ + #scrate::metadata::StorageEntryType::Map { + hasher: #scrate::metadata::#hasher, + key: #scrate::metadata::DecodeDifferent::Encode(#key), + value: #scrate::metadata::DecodeDifferent::Encode(#value_type), + is_linked: true, + } + } + }, + StorageLineTypeDef::DoubleMap(map) => { + let hasher1 = map.hasher1.into_metadata(); + let hasher2 = map.hasher2.into_metadata(); + let key1 = &map.key1; + let key1 = clean_type_string("e!(#key1).to_string()); + let key2 = &map.key2; + let key2 = clean_type_string("e!(#key2).to_string()); + quote!{ + #scrate::metadata::StorageEntryType::DoubleMap { + hasher: #scrate::metadata::#hasher1, + key1: #scrate::metadata::DecodeDifferent::Encode(#key1), + key2: #scrate::metadata::DecodeDifferent::Encode(#key2), + value: #scrate::metadata::DecodeDifferent::Encode(#value_type), + key2_hasher: #scrate::metadata::#hasher2, + } + } + }, + } +} + +fn default_byte_getter( + scrate: &TokenStream, + line: &StorageLineDefExt, + def: &DeclStorageDefExt, +) -> (TokenStream, TokenStream) { + let default = line.default_value.as_ref().map(|d| quote!( #d )) + .unwrap_or_else(|| quote!( Default::default() )); + + let str_name = line.name.to_string(); + let struct_name = syn::Ident::new(&("__GetByteStruct".to_string() + &str_name), line.name.span()); + let cache_name = syn::Ident::new(&("__CACHE_GET_BYTE_STRUCT_".to_string() + &str_name), line.name.span()); + + let runtime_generic = &def.module_runtime_generic; + let runtime_trait = &def.module_runtime_trait; + let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default; + let optional_instance_bound = &def.optional_instance_bound; + let optional_instance = &def.optional_instance; + let optional_comma_instance = optional_instance.as_ref().map(|i| quote!(, #i)); + let where_clause = &def.where_clause; + + let query_type = &line.query_type; + + let struct_def = quote! { + #[doc(hidden)] + pub struct #struct_name< + #runtime_generic, #optional_instance_bound_optional_default + >(pub #scrate::rstd::marker::PhantomData<(#runtime_generic #optional_comma_instance)>); + + #[cfg(feature = "std")] + #[allow(non_upper_case_globals)] + static #cache_name: #scrate::once_cell::sync::OnceCell<#scrate::rstd::vec::Vec> = + #scrate::once_cell::sync::OnceCell::new(); + + #[cfg(feature = "std")] + impl<#runtime_generic: #runtime_trait, #optional_instance_bound> + #scrate::metadata::DefaultByte + for #struct_name<#runtime_generic, #optional_instance> + #where_clause + { + fn default_byte(&self) -> #scrate::rstd::vec::Vec { + use #scrate::codec::Encode; + #cache_name.get_or_init(|| { + let def_val: #query_type = #default; + <#query_type as Encode>::encode(&def_val) + }).clone() + } + } + + unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Send + for #struct_name<#runtime_generic, #optional_instance> #where_clause {} + + unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Sync + for #struct_name<#runtime_generic, #optional_instance> #where_clause {} + + #[cfg(not(feature = "std"))] + impl<#runtime_generic: #runtime_trait, #optional_instance_bound> + #scrate::metadata::DefaultByte + for #struct_name<#runtime_generic, #optional_instance> + #where_clause + { + fn default_byte(&self) -> #scrate::rstd::vec::Vec { + use #scrate::codec::Encode; + let def_val: #query_type = #default; + <#query_type as Encode>::encode(&def_val) + } + } + }; + let struct_instance = quote!( + #struct_name::<#runtime_generic, #optional_instance>(#scrate::rstd::marker::PhantomData) + ); + + (struct_def, struct_instance) +} + +pub fn impl_metadata(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut entries = TokenStream::new(); + let mut default_byte_getter_struct_defs = TokenStream::new(); + + for line in def.storage_lines.iter() { + let str_name = line.name.to_string(); + + let modifier = if line.is_option { + quote!(#scrate::metadata::StorageEntryModifier::Optional) + } else { + quote!(#scrate::metadata::StorageEntryModifier::Default) + }; + + let ty = storage_line_metadata_type(scrate, line); + + let ( + default_byte_getter_struct_def, + default_byte_getter_struct_instance, + ) = default_byte_getter(scrate, line, def); + + let mut docs = TokenStream::new(); + for attr in line.attrs.iter().filter_map(|v| v.parse_meta().ok()) { + if let syn::Meta::NameValue(meta) = attr { + if meta.ident == "doc" { + let lit = meta.lit; + docs.extend(quote!(#lit,)); + } + } + } + + let entry = quote! { + #scrate::metadata::StorageEntryMetadata { + name: #scrate::metadata::DecodeDifferent::Encode(#str_name), + modifier: #modifier, + ty: #ty, + default: #scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::DefaultByteGetter(&#default_byte_getter_struct_instance) + ), + documentation: #scrate::metadata::DecodeDifferent::Encode(&[ #docs ]), + }, + }; + + default_byte_getter_struct_defs.extend(default_byte_getter_struct_def); + entries.extend(entry); + } + + let prefix = if let Some(instance) = &def.module_instance { + let instance_generic = &instance.instance_generic; + quote!(#instance_generic::PREFIX) + } else { + let prefix = def.crate_name.to_string(); + quote!(#prefix) + }; + + let store_metadata = quote!( + #scrate::metadata::StorageMetadata { + prefix: #scrate::metadata::DecodeDifferent::Encode(#prefix), + entries: #scrate::metadata::DecodeDifferent::Encode(&[ #entries ][..]), + } + ); + + let module_struct = &def.module_struct; + let module_impl = &def.module_impl; + let where_clause = &def.where_clause; + + quote!( + #default_byte_getter_struct_defs + + impl#module_impl #module_struct #where_clause { + #[doc(hidden)] + pub fn storage_metadata() -> #scrate::metadata::StorageMetadata { + #store_metadata + } + } + ) +} diff --git a/srml/support/procedural/src/storage/mod.rs b/srml/support/procedural/src/storage/mod.rs index 4253206f44da064d5903362c3ed72aedbb51a0c7..9a6931d87e9c8eef5a2a46b506d12a989877a144 100644 --- a/srml/support/procedural/src/storage/mod.rs +++ b/srml/support/procedural/src/storage/mod.rs @@ -14,191 +14,358 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -// tag::description[] -//! `decl_storage` macro -// end::description[] +//! `decl_storage` input definition and expansion. + +mod storage_struct; +mod parse; +mod store_trait; +mod getters; +mod metadata; +mod instance_trait; +mod genesis_config; -use srml_support_procedural_tools::{ToTokens, Parse, syn_ext as ext}; -use syn::{Ident, Token}; -use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use srml_support_procedural_tools::{ + generate_crate_access, generate_hidden_includes, syn_ext as ext +}; -mod impls; - -pub mod transformation; - -mod keyword { - syn::custom_keyword!(hiddencrate); - syn::custom_keyword!(add_extra_genesis); - syn::custom_keyword!(extra_genesis_skip_phantom_data_field); - syn::custom_keyword!(config); - syn::custom_keyword!(build); - syn::custom_keyword!(get); - syn::custom_keyword!(map); - syn::custom_keyword!(linked_map); - syn::custom_keyword!(double_map); - syn::custom_keyword!(blake2_256); - syn::custom_keyword!(blake2_128); - syn::custom_keyword!(twox_256); - syn::custom_keyword!(twox_128); - syn::custom_keyword!(twox_64_concat); - syn::custom_keyword!(hasher); +/// All informations contained in input of decl_storage +pub struct DeclStorageDef { + /// Name of the module used to import hidden imports. + hidden_crate: Option, + /// Visibility of store trait. + visibility: syn::Visibility, + /// Name of store trait: usually `Store`. + store_trait: syn::Ident, + /// Module name used by construct_runtime: usually `Module`. + module_name: syn::Ident, + /// Usually `T`. + module_runtime_generic: syn::Ident, + /// Usually `Trait` + module_runtime_trait: syn::Path, + /// For instantiable module: usually `I: Instance=DefaultInstance`. + module_instance: Option, + /// Where claused used to constrain T and I even more. + where_clause: Option, + /// The extra build function used to build storage at genesis. + extra_genesis_build: Option, + /// The extra genesis config fields. + extra_genesis_config_lines: Vec, + /// Definition of storages. + storage_lines: Vec, + /// Name of the crate, used for storage prefixes. + crate_name: syn::Ident, } -/// Parsing usage only -#[derive(Parse, ToTokens, Debug)] -struct StorageDefinition { - pub hidden_crate: ext::Opt, - pub visibility: syn::Visibility, - pub trait_token: Token![trait], - pub ident: Ident, - pub for_token: Token![for], - pub module_ident: Ident, - pub mod_lt_token: Token![<], - pub mod_param: syn::GenericParam, - pub mod_instance_param_token: Option, - pub mod_instance: Option, - pub mod_instantiable_token: Option, - pub mod_instantiable: Option, - pub mod_default_instance_token: Option, - pub mod_default_instance: Option, - pub mod_gt_token: Token![>], - pub as_token: Token![as], - pub crate_ident: Ident, - pub where_clause: Option, - pub content: ext::Braces>, - pub extra_genesis: ext::Opt, +impl syn::parse::Parse for DeclStorageDef { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + parse::parse(input) + } } -#[derive(Parse, ToTokens, Debug)] -struct SpecificHiddenCrate { - pub keyword: keyword::hiddencrate, - pub ident: ext::Parens, +/// Extended version of `DeclStorageDef` with useful precomputed value. +pub struct DeclStorageDefExt { + /// Name of the module used to import hidden imports. + hidden_crate: Option, + /// Visibility of store trait. + visibility: syn::Visibility, + /// Name of store trait: usually `Store`. + store_trait: syn::Ident, + /// Module name used by construct_runtime: usually `Module`. + #[allow(unused)] + module_name: syn::Ident, + /// Usually `T`. + module_runtime_generic: syn::Ident, + /// Usually `Trait`. + module_runtime_trait: syn::Path, + /// For instantiable module: usually `I: Instance=DefaultInstance`. + module_instance: Option, + /// Where claused used to constrain T and I even more. + where_clause: Option, + /// The extra build function used to build storage at genesis. + extra_genesis_build: Option, + /// The extra genesis config fields. + extra_genesis_config_lines: Vec, + /// Definition of storages. + storage_lines: Vec, + /// Name of the crate, used for storage prefixes. + crate_name: syn::Ident, + /// Full struct expansion: `Module`. + module_struct: proc_macro2::TokenStream, + /// Impl block for module: ``. + module_impl: proc_macro2::TokenStream, + /// For instantiable: `I`. + optional_instance: Option, + /// For instantiable: `I: Instance`. + optional_instance_bound: Option, + /// For instantiable: `I: Instance = DefaultInstance`. + optional_instance_bound_optional_default: Option, } -#[derive(Parse, ToTokens, Debug)] -struct AddExtraGenesis { - pub extragenesis_keyword: keyword::add_extra_genesis, - pub content: ext::Braces, -} +impl From for DeclStorageDefExt { + fn from(mut def: DeclStorageDef) -> Self { + let storage_lines = def.storage_lines.drain(..).collect::>(); + let storage_lines = storage_lines.into_iter() + .map(|line| StorageLineDefExt::from_def(line, &def)) + .collect(); + + let ( + optional_instance, + optional_instance_bound, + optional_instance_bound_optional_default, + ) = if let Some(instance) = def.module_instance.as_ref() { + let instance_generic = &instance.instance_generic; + let instance_trait= &instance.instance_trait; + let optional_equal_instance_default = instance.instance_default.as_ref() + .map(|d| quote!( = #d )); + ( + Some(quote!(#instance_generic)), + Some(quote!(#instance_generic: #instance_trait)), + Some(quote!(#instance_generic: #instance_trait #optional_equal_instance_default)), + ) + } else { + (None, None, None) + }; + + let module_runtime_generic = &def.module_runtime_generic; + let module_runtime_trait = &def.module_runtime_trait; + let module_name = &def.module_name; -#[derive(Parse, ToTokens, Debug)] -struct AddExtraGenesisContent { - pub lines: ext::Punctuated, + let module_struct = quote!( + #module_name<#module_runtime_generic, #optional_instance> + ); + + let module_impl = quote!( + <#module_runtime_generic: #module_runtime_trait + 'static, #optional_instance_bound> + ); + + Self { + hidden_crate: def.hidden_crate, + visibility: def.visibility, + store_trait: def.store_trait, + module_name: def.module_name, + module_runtime_generic: def.module_runtime_generic, + module_runtime_trait: def.module_runtime_trait, + module_instance: def.module_instance, + where_clause: def.where_clause, + extra_genesis_build: def.extra_genesis_build, + extra_genesis_config_lines: def.extra_genesis_config_lines, + crate_name: def.crate_name, + storage_lines, + module_struct, + module_impl, + optional_instance, + optional_instance_bound, + optional_instance_bound_optional_default, + } + } } -#[derive(Parse, ToTokens, Debug)] -enum AddExtraGenesisLineEnum { - AddExtraGenesisLine(AddExtraGenesisLine), - AddExtraGenesisBuild(DeclStorageBuild), +/// Usually `I: Instance=DefaultInstance`. +pub struct ModuleInstanceDef { + /// Usually: `I`. + instance_generic: syn::Ident, + /// Usually: `Instance`. + instance_trait: syn::Ident, + /// Usually: `DefaultInstance`. + instance_default: Option, } -#[derive(Parse, ToTokens, Debug)] -struct AddExtraGenesisLine { - pub attrs: ext::OuterAttributes, - pub config_keyword: keyword::config, - pub extra_field: ext::Parens, - pub coldot_token: Token![:], - pub extra_type: syn::Type, - pub default_value: ext::Opt, +pub struct StorageLineDef { + attrs: Vec, + /// Visibility of the storage struct. + visibility: syn::Visibility, + name: syn::Ident, + /// The name of getter function to be implemented on Module struct. + getter: Option, + /// The name of the field to be used in genesis config if any. + config: Option, + /// The build function of the storage if any. + build: Option, + /// Default value of genesis config field and also for storage when no value available. + default_value: Option, + storage_type: StorageLineTypeDef, } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageLine { - // attrs (main use case is doc) - pub attrs: ext::OuterAttributes, - // visibility (no need to make optional - pub visibility: syn::Visibility, - // name - pub name: Ident, - pub getter: ext::Opt, - pub config: ext::Opt, - pub build: ext::Opt, - pub coldot_token: Token![:], - pub storage_type: DeclStorageType, - pub default_value: ext::Opt, +pub struct StorageLineDefExt { + #[allow(unused)] + attrs: Vec, + /// Visibility of the storage struct. + visibility: syn::Visibility, + name: syn::Ident, + /// The name of getter function to be implemented on Module struct. + getter: Option, + /// The name of the field to be used in genesis config if any. + config: Option, + /// The build function of the storage if any. + build: Option, + /// Default value of genesis config field and also for storage when no value available. + default_value: Option, + storage_type: StorageLineTypeDef, + doc_attrs: Vec, + /// Either the type stored in storage or wrapped in an Option. + query_type: syn::Type, + /// The type stored in storage. + value_type: syn::Type, + /// Full struct, for example: `StorageName`. + storage_struct: proc_macro2::TokenStream, + /// If storage is generic over runtime then `T`. + optional_storage_runtime_comma: Option, + /// If storage is generic over runtime then `T: Trait`. + optional_storage_runtime_bound_comma: Option, + /// The where clause to use to constrain generics if storage is generic over runtime. + optional_storage_where_clause: Option, + /// Full trait, for example: `storage::StorageMap`. + storage_trait: proc_macro2::TokenStream, + /// Full trait, for example: `storage::generator::StorageMap`. + storage_generator_trait: proc_macro2::TokenStream, + /// Weither the storage is generic. + is_generic: bool, + /// Weither the storage value is an option. + is_option: bool, } +impl StorageLineDefExt { + fn from_def(storage_def: StorageLineDef, def: &DeclStorageDef) -> Self { + let is_generic = match &storage_def.storage_type { + StorageLineTypeDef::Simple(value) => { + ext::type_contains_ident(&value, &def.module_runtime_generic) + }, + StorageLineTypeDef::Map(map) => { + ext::type_contains_ident(&map.key, &def.module_runtime_generic) + || ext::type_contains_ident(&map.value, &def.module_runtime_generic) + } + StorageLineTypeDef::LinkedMap(map) => { + ext::type_contains_ident(&map.key, &def.module_runtime_generic) + || ext::type_contains_ident(&map.value, &def.module_runtime_generic) + } + StorageLineTypeDef::DoubleMap(map) => { + ext::type_contains_ident(&map.key1, &def.module_runtime_generic) + || ext::type_contains_ident(&map.key2, &def.module_runtime_generic) + || ext::type_contains_ident(&map.value, &def.module_runtime_generic) + } + }; -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageGetter { - pub getter_keyword: keyword::get, - pub getfn: ext::Parens, -} + let query_type = match &storage_def.storage_type { + StorageLineTypeDef::Simple(value) => value.clone(), + StorageLineTypeDef::Map(map) => map.value.clone(), + StorageLineTypeDef::LinkedMap(map) => map.value.clone(), + StorageLineTypeDef::DoubleMap(map) => map.value.clone(), + }; + let is_option = ext::extract_type_option(&query_type).is_some(); + let value_type = ext::extract_type_option(&query_type).unwrap_or(query_type.clone()); -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageConfig { - pub config_keyword: keyword::config, - pub expr: ext::Parens>, -} + let module_runtime_generic = &def.module_runtime_generic; + let module_runtime_trait = &def.module_runtime_trait; + let optional_storage_runtime_comma = if is_generic { + Some(quote!( #module_runtime_generic, )) + } else { + None + }; + let optional_storage_runtime_bound_comma = if is_generic { + Some(quote!( #module_runtime_generic: #module_runtime_trait, )) + } else { + None + }; -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageBuild { - pub build_keyword: keyword::build, - pub expr: ext::Parens, -} + let storage_name = &storage_def.name; + let optional_instance_generic = def.module_instance.as_ref().map(|i| { + let instance_generic = &i.instance_generic; + quote!( #instance_generic ) + }); + let storage_struct = quote!( + #storage_name<#optional_storage_runtime_comma #optional_instance_generic> + ); -#[derive(Parse, ToTokens, Debug)] -enum DeclStorageType { - Map(DeclStorageMap), - LinkedMap(DeclStorageLinkedMap), - DoubleMap(DeclStorageDoubleMap), - Simple(syn::Type), + let optional_storage_where_clause = if is_generic { + def.where_clause.as_ref().map(|w| quote!( #w )) + } else { + None + }; + + let storage_trait_trunkated = match &storage_def.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!( StorageValue<#value_type> ) + }, + StorageLineTypeDef::Map(map) => { + let key = &map.key; + quote!( StorageMap<#key, #value_type> ) + }, + StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + quote!( StorageLinkedMap<#key, #value_type> ) + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + quote!( StorageDoubleMap<#key1, #key2, #value_type> ) + }, + }; + + let storage_trait = quote!( storage::#storage_trait_trunkated ); + let storage_generator_trait = quote!( storage::generator::#storage_trait_trunkated ); + + let doc_attrs = storage_def.attrs.iter() + .filter_map(|a| a.parse_meta().ok()) + .filter(|m| m.name() == "doc") + .collect(); + + Self { + attrs: storage_def.attrs, + visibility: storage_def.visibility, + name: storage_def.name, + getter: storage_def.getter, + config: storage_def.config, + build: storage_def.build, + default_value: storage_def.default_value, + storage_type: storage_def.storage_type, + doc_attrs, + query_type, + value_type, + storage_struct, + optional_storage_runtime_comma, + optional_storage_runtime_bound_comma, + optional_storage_where_clause, + storage_trait, + storage_generator_trait, + is_generic, + is_option, + } + } } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageMap { - pub map_keyword: keyword::map, - pub hasher: ext::Opt, - pub key: syn::Type, - pub ass_keyword: Token![=>], - pub value: syn::Type, +pub enum StorageLineTypeDef { + Map(MapDef), + LinkedMap(MapDef), + DoubleMap(DoubleMapDef), + Simple(syn::Type), } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageLinkedMap { - pub map_keyword: keyword::linked_map, - pub hasher: ext::Opt, +pub struct MapDef { + pub hasher: HasherKind, pub key: syn::Type, - pub ass_keyword: Token![=>], + /// This is the query value not the inner value used in storage trait implementation. pub value: syn::Type, } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageDoubleMap { - pub map_keyword: keyword::double_map, - pub hasher: ext::Opt, +pub struct DoubleMapDef { + pub hasher1: HasherKind, + pub hasher2: HasherKind, pub key1: syn::Type, - pub comma_keyword: Token![,], - pub key2_hasher: Hasher, - pub key2: ext::Parens, - pub ass_keyword: Token![=>], + pub key2: syn::Type, + /// This is the query value not the inner value used in storage trait implementation. pub value: syn::Type, } -#[derive(Parse, ToTokens, Debug)] -enum Hasher { - Blake2_256(keyword::blake2_256), - Blake2_128(keyword::blake2_128), - Twox256(keyword::twox_256), - Twox128(keyword::twox_128), - Twox64Concat(keyword::twox_64_concat), -} - -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageDefault { - pub equal_token: Token![=], - pub expr: syn::Expr, -} - -#[derive(Parse, ToTokens, Debug)] -struct SetHasher { - pub hasher_keyword: keyword::hasher, - pub inner: ext::Parens, +pub struct ExtraGenesisLineDef { + attrs: Vec, + name: syn::Ident, + typ: syn::Type, + default: Option, } #[derive(Debug, Clone)] -enum HasherKind { +pub enum HasherKind { Blake2_256, Blake2_128, Twox256, @@ -206,26 +373,8 @@ enum HasherKind { Twox64Concat, } -impl From<&SetHasher> for HasherKind { - fn from(set_hasher: &SetHasher) -> Self { - (&set_hasher.inner.content).into() - } -} - -impl From<&Hasher> for HasherKind { - fn from(hasher: &Hasher) -> Self { - match hasher { - Hasher::Blake2_256(_) => HasherKind::Blake2_256, - Hasher::Blake2_128(_) => HasherKind::Blake2_128, - Hasher::Twox256(_) => HasherKind::Twox256, - Hasher::Twox128(_) => HasherKind::Twox128, - Hasher::Twox64Concat(_) => HasherKind::Twox64Concat, - } - } -} - impl HasherKind { - fn into_storage_hasher_struct(&self) -> TokenStream2 { + fn to_storage_hasher_struct(&self) -> proc_macro2::TokenStream { match self { HasherKind::Blake2_256 => quote!( Blake2_256 ), HasherKind::Blake2_128 => quote!( Blake2_128 ), @@ -235,7 +384,7 @@ impl HasherKind { } } - fn into_metadata(&self) -> TokenStream2 { + fn into_metadata(&self) -> proc_macro2::TokenStream { match self { HasherKind::Blake2_256 => quote!( StorageHasher::Blake2_256 ), HasherKind::Blake2_128 => quote!( StorageHasher::Blake2_128 ), @@ -245,3 +394,39 @@ impl HasherKind { } } } + +/// Full implementation of decl_storage. +pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let def = syn::parse_macro_input!(input as DeclStorageDef); + let def_ext = DeclStorageDefExt::from(def); + + let hidden_crate_name = def_ext.hidden_crate.as_ref().map(|i| i.to_string()) + .unwrap_or_else(|| "decl_storage".to_string()); + + let scrate = generate_crate_access(&hidden_crate_name, "srml-support"); + let scrate_decl = generate_hidden_includes(&hidden_crate_name, "srml-support"); + + let store_trait = store_trait::decl_and_impl(&def_ext); + let getters = getters::impl_getters(&scrate, &def_ext); + let metadata = metadata::impl_metadata(&scrate, &def_ext); + let instance_trait = instance_trait::decl_and_impl(&scrate, &def_ext); + let genesis_config = genesis_config::genesis_config_and_build_storage(&scrate, &def_ext); + let storage_struct = storage_struct::decl_and_impl(&scrate, &def_ext); + + quote!( + use #scrate::{ + StorageValue as _, + StorageMap as _, + StorageLinkedMap as _, + StorageDoubleMap as _ + }; + + #scrate_decl + #store_trait + #getters + #metadata + #instance_trait + #genesis_config + #storage_struct + ).into() +} diff --git a/srml/support/procedural/src/storage/parse.rs b/srml/support/procedural/src/storage/parse.rs new file mode 100644 index 0000000000000000000000000000000000000000..e428fbe24f2951483636f213fc95188e7031bd4e --- /dev/null +++ b/srml/support/procedural/src/storage/parse.rs @@ -0,0 +1,404 @@ +// Copyright 2017-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 . + +//! Parsing of decl_storage input. + +use srml_support_procedural_tools::{ToTokens, Parse, syn_ext as ext}; +use syn::{Ident, Token, spanned::Spanned}; + +mod keyword { + syn::custom_keyword!(hiddencrate); + syn::custom_keyword!(add_extra_genesis); + syn::custom_keyword!(extra_genesis_skip_phantom_data_field); + syn::custom_keyword!(config); + syn::custom_keyword!(build); + syn::custom_keyword!(get); + syn::custom_keyword!(map); + syn::custom_keyword!(linked_map); + syn::custom_keyword!(double_map); + syn::custom_keyword!(blake2_256); + syn::custom_keyword!(blake2_128); + syn::custom_keyword!(twox_256); + syn::custom_keyword!(twox_128); + syn::custom_keyword!(twox_64_concat); + syn::custom_keyword!(hasher); +} + +/// Parsing usage only +#[derive(Parse, ToTokens, Debug)] +struct StorageDefinition { + pub hidden_crate: ext::Opt, + pub visibility: syn::Visibility, + pub trait_token: Token![trait], + pub ident: Ident, + pub for_token: Token![for], + pub module_ident: Ident, + pub mod_lt_token: Token![<], + pub mod_param_generic: syn::Ident, + pub mod_param_bound_token: Option, + pub mod_param_bound: syn::Path, + pub mod_instance_param_token: Option, + pub mod_instance: Option, + pub mod_instantiable_token: Option, + pub mod_instantiable: Option, + pub mod_default_instance_token: Option, + pub mod_default_instance: Option, + pub mod_gt_token: Token![>], + pub as_token: Token![as], + pub crate_ident: Ident, + pub where_clause: Option, + pub content: ext::Braces>, + pub extra_genesis: ext::Opt, +} + +#[derive(Parse, ToTokens, Debug)] +struct SpecificHiddenCrate { + pub keyword: keyword::hiddencrate, + pub ident: ext::Parens, +} + +#[derive(Parse, ToTokens, Debug)] +struct AddExtraGenesis { + pub extragenesis_keyword: keyword::add_extra_genesis, + pub content: ext::Braces, +} + +#[derive(Parse, ToTokens, Debug)] +struct AddExtraGenesisContent { + pub lines: ext::Punctuated, +} + +#[derive(Parse, ToTokens, Debug)] +enum AddExtraGenesisLineEnum { + AddExtraGenesisLine(AddExtraGenesisLine), + AddExtraGenesisBuild(DeclStorageBuild), +} + +#[derive(Parse, ToTokens, Debug)] +struct AddExtraGenesisLine { + pub attrs: ext::OuterAttributes, + pub config_keyword: keyword::config, + pub extra_field: ext::Parens, + pub coldot_token: Token![:], + pub extra_type: syn::Type, + pub default_value: ext::Opt, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageLine { + // attrs (main use case is doc) + pub attrs: ext::OuterAttributes, + // visibility (no need to make optional + pub visibility: syn::Visibility, + // name + pub name: Ident, + pub getter: ext::Opt, + pub config: ext::Opt, + pub build: ext::Opt, + pub coldot_token: Token![:], + pub storage_type: DeclStorageType, + pub default_value: ext::Opt, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageGetterBody { + fn_keyword: Option, + ident: Ident, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageGetter { + pub getter_keyword: keyword::get, + pub getfn: ext::Parens, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageConfig { + pub config_keyword: keyword::config, + pub expr: ext::Parens>, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageBuild { + pub build_keyword: keyword::build, + pub expr: ext::Parens, +} + +#[derive(Parse, ToTokens, Debug)] +enum DeclStorageType { + Map(DeclStorageMap), + LinkedMap(DeclStorageLinkedMap), + DoubleMap(DeclStorageDoubleMap), + Simple(syn::Type), +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageMap { + pub map_keyword: keyword::map, + pub hasher: ext::Opt, + pub key: syn::Type, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageLinkedMap { + pub map_keyword: keyword::linked_map, + pub hasher: ext::Opt, + pub key: syn::Type, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageDoubleMap { + pub map_keyword: keyword::double_map, + pub hasher: ext::Opt, + pub key1: syn::Type, + pub comma_keyword: Token![,], + pub key2_hasher: Hasher, + pub key2: ext::Parens, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +enum Hasher { + Blake2_256(keyword::blake2_256), + Blake2_128(keyword::blake2_128), + Twox256(keyword::twox_256), + Twox128(keyword::twox_128), + Twox64Concat(keyword::twox_64_concat), +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageDefault { + pub equal_token: Token![=], + pub expr: syn::Expr, +} + +#[derive(Parse, ToTokens, Debug)] +struct SetHasher { + pub hasher_keyword: keyword::hasher, + pub inner: ext::Parens, +} + +impl From for super::HasherKind { + fn from(set_hasher: SetHasher) -> Self { + set_hasher.inner.content.into() + } +} + +impl From for super::HasherKind { + fn from(hasher: Hasher) -> Self { + match hasher { + Hasher::Blake2_256(_) => super::HasherKind::Blake2_256, + Hasher::Blake2_128(_) => super::HasherKind::Blake2_128, + Hasher::Twox256(_) => super::HasherKind::Twox256, + Hasher::Twox128(_) => super::HasherKind::Twox128, + Hasher::Twox64Concat(_) => super::HasherKind::Twox64Concat, + } + } +} + +fn get_module_instance( + instance: Option, + instantiable: Option, + default_instance: Option, +) -> syn::Result> { + let right_syntax = "Should be $Instance: $Instantiable = $DefaultInstance"; + + match (instance, instantiable, default_instance) { + (Some(instance), Some(instantiable), default_instance) => { + Ok(Some(super::ModuleInstanceDef { + instance_generic: instance, + instance_trait: instantiable, + instance_default: default_instance, + })) + }, + (None, None, None) => Ok(None), + (Some(instance), None, _) => Err( + syn::Error::new( + instance.span(), + format!( + "Expect instantiable trait bound for instance: {}. {}", + instance, + right_syntax, + ) + ) + ), + (None, Some(instantiable), _) => Err( + syn::Error::new( + instantiable.span(), + format!( + "Expect instance generic for bound instantiable: {}. {}", + instantiable, + right_syntax, + ) + ) + ), + (None, _, Some(default_instance)) => Err( + syn::Error::new( + default_instance.span(), + format!( + "Expect instance generic for default instance: {}. {}", + default_instance, + right_syntax, + ) + ) + ), + } +} + +pub fn parse(input: syn::parse::ParseStream) -> syn::Result { + use syn::parse::Parse; + + let def = StorageDefinition::parse(input)?; + + let module_instance = get_module_instance( + def.mod_instance, + def.mod_instantiable, + def.mod_default_instance, + )?; + + let mut extra_genesis_config_lines = vec![]; + let mut extra_genesis_build = None; + + for line in def.extra_genesis.inner.into_iter() + .flat_map(|o| o.content.content.lines.inner.into_iter()) + { + match line { + AddExtraGenesisLineEnum::AddExtraGenesisLine(def) => { + extra_genesis_config_lines.push(super::ExtraGenesisLineDef{ + attrs: def.attrs.inner, + name: def.extra_field.content, + typ: def.extra_type, + default: def.default_value.inner.map(|o| o.expr), + }); + } + AddExtraGenesisLineEnum::AddExtraGenesisBuild(def) => { + if extra_genesis_build.is_some() { + return Err(syn::Error::new( + def.span(), + "Only one build expression allowed for extra genesis" + )) + } + + extra_genesis_build = Some(def.expr.content); + } + } + } + + let storage_lines = parse_storage_line_defs(def.content.content.inner.into_iter())?; + + Ok(super::DeclStorageDef { + hidden_crate: def.hidden_crate.inner.map(|i| i.ident.content), + visibility: def.visibility, + module_name: def.module_ident, + store_trait: def.ident, + module_runtime_generic: def.mod_param_generic, + module_runtime_trait: def.mod_param_bound, + where_clause: def.where_clause, + crate_name: def.crate_ident, + module_instance, + extra_genesis_build, + extra_genesis_config_lines, + storage_lines, + }) +} + +/// Parse the `DeclStorageLine` into `StorageLineDef`. +fn parse_storage_line_defs( + defs: impl Iterator, +) -> syn::Result> { + let mut storage_lines = Vec::::new(); + + for line in defs { + let getter = line.getter.inner.map(|o| o.getfn.content.ident); + let config = if let Some(config) = line.config.inner { + if let Some(ident) = config.expr.content { + Some(ident) + } else if let Some(ref ident) = getter { + Some(ident.clone()) + } else { + return Err(syn::Error::new( + config.span(), + "Invalid storage definition, couldn't find config identifier: storage must \ + either have a get identifier `get(fn ident)` or a defined config identifier \ + `config(ident)`", + )) + } + } else { + None + }; + + if let Some(ref config) = config { + storage_lines.iter().filter_map(|sl| sl.config.as_ref()).try_for_each(|other_config| { + if other_config == config { + Err(syn::Error::new( + config.span(), + "`config()`/`get()` with the same name already defined.", + )) + } else { + Ok(()) + } + })?; + } + + 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), + 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), + key: map.key, + value: map.value, + } + ), + DeclStorageType::DoubleMap(map) => super::StorageLineTypeDef::DoubleMap( + super::DoubleMapDef { + hasher1: map.hasher.inner.map(Into::into) + .unwrap_or(super::HasherKind::Blake2_256), + hasher2: map.key2_hasher.into(), + key1: map.key1, + key2: map.key2.content, + value: map.value, + } + ), + DeclStorageType::Simple(expr) => super::StorageLineTypeDef::Simple(expr), + }; + + storage_lines.push(super::StorageLineDef { + attrs: line.attrs.inner, + visibility: line.visibility, + name: line.name, + getter, + config, + build: line.build.inner.map(|o| o.expr.content), + default_value: line.default_value.inner.map(|o| o.expr), + storage_type, + }) + } + + Ok(storage_lines) +} diff --git a/srml/support/procedural/src/storage/storage_struct.rs b/srml/support/procedural/src/storage/storage_struct.rs new file mode 100644 index 0000000000000000000000000000000000000000..e195fb53e8a2c3a28e773f6fcb845f0bdc2dffc8 --- /dev/null +++ b/srml/support/procedural/src/storage/storage_struct.rs @@ -0,0 +1,220 @@ +// 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 . + +//! Implementation of storage structures and implementation of storage traits on them. + +use proc_macro2::TokenStream; +use quote::quote; +use super::{ + DeclStorageDefExt, StorageLineTypeDef, + instance_trait::{PREFIX_FOR, HEAD_KEY_FOR}, +}; + +fn from_optional_value_to_query(is_option: bool, default: &Option) -> TokenStream { + let default = default.as_ref().map(|d| quote!( #d )) + .unwrap_or_else(|| quote!( Default::default() )); + + if !is_option { + // raw type case + quote!( v.unwrap_or_else(|| #default ) ) + } else { + // Option<> type case + quote!( v.or_else(|| #default ) ) + } +} + +fn from_query_to_optional_value(is_option: bool) -> TokenStream { + if !is_option { + // raw type case + quote!( Some(v) ) + } else { + // Option<> type case + quote!( v ) + } +} + +pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut impls = TokenStream::new(); + + for line in &def.storage_lines { + + // Propagate doc attributes. + let attrs = &line.doc_attrs; + + let visibility = &line.visibility; + let optional_storage_runtime_comma = &line.optional_storage_runtime_comma; + let optional_storage_runtime_bound_comma = &line.optional_storage_runtime_bound_comma; + let optional_storage_where_clause = &line.optional_storage_where_clause; + let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default; + let optional_instance_bound = &def.optional_instance_bound; + let optional_instance = &def.optional_instance; + let name = &line.name; + + let struct_decl = quote!( + #( #[ #attrs ] )* + #visibility struct #name< + #optional_storage_runtime_bound_comma #optional_instance_bound_optional_default + >( + #scrate::rstd::marker::PhantomData< + (#optional_storage_runtime_comma #optional_instance) + > + ) #optional_storage_where_clause; + ); + + let from_query_to_optional_value = from_query_to_optional_value(line.is_option); + let from_optional_value_to_query = + from_optional_value_to_query(line.is_option, &line.default_value); + + let final_prefix = if let Some(instance) = def.module_instance.as_ref() { + let instance = &instance.instance_generic; + let const_name = syn::Ident::new( + &format!("{}{}", PREFIX_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + quote!( #instance::#const_name.as_bytes() ) + } else { + let prefix = format!("{} {}", def.crate_name, line.name); + quote!( #prefix.as_bytes() ) + }; + + + let storage_generator_trait = &line.storage_generator_trait; + let storage_struct = &line.storage_struct; + let impl_trait = quote!( #optional_storage_runtime_bound_comma #optional_instance_bound ); + let value_type = &line.value_type; + let query_type = &line.query_type; + + let struct_impl = match &line.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + + fn unhashed_key() -> &'static [u8] { + #final_prefix + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + }, + StorageLineTypeDef::Map(map) => { + let hasher = map.hasher.to_storage_hasher_struct(); + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + type Hasher = #scrate::#hasher; + + fn prefix() -> &'static [u8] { + #final_prefix + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + }, + StorageLineTypeDef::LinkedMap(map) => { + let hasher = map.hasher.to_storage_hasher_struct(); + + // make sure to use different prefix for head and elements. + let head_key = if let Some(instance) = def.module_instance.as_ref() { + let instance = &instance.instance_generic; + let const_name = syn::Ident::new( + &format!("{}{}", HEAD_KEY_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + quote!( #instance::#const_name.as_bytes() ) + } else { + let prefix = format!("head of {} {}", def.crate_name, line.name); + quote!( #prefix.as_bytes() ) + }; + + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + type Hasher = #scrate::#hasher; + + fn prefix() -> &'static [u8] { + #final_prefix + } + + fn head_key() -> &'static [u8] { + #head_key + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + }, + StorageLineTypeDef::DoubleMap(map) => { + let hasher1 = map.hasher1.to_storage_hasher_struct(); + let hasher2 = map.hasher2.to_storage_hasher_struct(); + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + + type Hasher1 = #scrate::#hasher1; + + type Hasher2 = #scrate::#hasher2; + + fn key1_prefix() -> &'static [u8] { + #final_prefix + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + } + }; + + impls.extend(quote!( + #struct_decl + #struct_impl + )) + } + + impls +} diff --git a/srml/support/procedural/src/storage/store_trait.rs b/srml/support/procedural/src/storage/store_trait.rs new file mode 100644 index 0000000000000000000000000000000000000000..4c9d96b6bb27a9ba644f5f559df9408a46139eb2 --- /dev/null +++ b/srml/support/procedural/src/storage/store_trait.rs @@ -0,0 +1,54 @@ +// Copyright 2017-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 . + +//! Declaration of store trait and implementation on module structure. + +use proc_macro2::TokenStream; +use quote::quote; +use super::DeclStorageDefExt; + +pub fn decl_and_impl(def: &DeclStorageDefExt) -> TokenStream { + let decl_store_items = def.storage_lines.iter() + .map(|sline| &sline.name) + .fold(TokenStream::new(), |mut items, name| { + items.extend(quote!(type #name;)); + items + }); + + let impl_store_items = def.storage_lines.iter() + .fold(TokenStream::new(), |mut items, line| { + let name = &line.name; + let storage_struct = &line.storage_struct; + + items.extend(quote!(type #name = #storage_struct;)); + items + }); + + let visibility = &def.visibility; + let store_trait = &def.store_trait; + let module_struct = &def.module_struct; + let module_impl = &def.module_impl; + let where_clause = &def.where_clause; + + quote!( + #visibility trait #store_trait { + #decl_store_items + } + impl#module_impl #store_trait for #module_struct #where_clause { + #impl_store_items + } + ) +} diff --git a/srml/support/procedural/src/storage/transformation.rs b/srml/support/procedural/src/storage/transformation.rs deleted file mode 100644 index c6a3a1f668cdcbabb8e5d5066598a297923a60c8..0000000000000000000000000000000000000000 --- a/srml/support/procedural/src/storage/transformation.rs +++ /dev/null @@ -1,1264 +0,0 @@ -// Copyright 2017-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 . - -//! `decl_storage` macro transformation - -use srml_support_procedural_tools::syn_ext as ext; -use srml_support_procedural_tools::{ - generate_crate_access, generate_hidden_includes, clean_type_string -}; - -use proc_macro::TokenStream; -use proc_macro2::{TokenStream as TokenStream2, Span}; - -use syn::{ - Ident, - GenericParam, - WhereClause, - spanned::Spanned, - parse::{ - Error, - Result, - }, - parse_macro_input, -}; -use quote::{quote, quote_spanned}; - -use super::*; - -const NUMBER_OF_INSTANCE: usize = 16; -const DEFAULT_INSTANTIABLE_TRAIT_NAME: &str = "__GeneratedInstantiable"; -const DEFAULT_INSTANCE_NAME: &str = "__GeneratedInstance"; -const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; - -// try macro but returning tokenized error -macro_rules! try_tok(( $expre : expr ) => { - match $expre { - Ok(r) => r, - Err (err) => { - return err.to_compile_error().into() - } - } -}); - -pub fn decl_storage_impl(input: TokenStream) -> TokenStream { - let def = parse_macro_input!(input as StorageDefinition); - - let StorageDefinition { - hidden_crate, - visibility, - ident: storetype, - module_ident, - mod_param: strait, - mod_instance, - mod_instantiable, - mod_default_instance, - crate_ident: cratename, - content: ext::Braces { content: storage_lines, ..}, - extra_genesis, - where_clause, - .. - } = def; - - let instance_opts = match get_instance_opts( - mod_instance, - mod_instantiable, - mod_default_instance - ) { - Ok(opts) => opts, - Err(err) => return err.to_compile_error().into(), - }; - - let hidden_crate_name = hidden_crate.inner.map(|rc| rc.ident.content).map(|i| i.to_string()) - .unwrap_or_else(|| "decl_storage".to_string()); - let scrate = generate_crate_access(&hidden_crate_name, "srml-support"); - let scrate_decl = generate_hidden_includes( - &hidden_crate_name, - "srml-support", - ); - - let ( - traitinstance, - traittypes, - ) = if let GenericParam::Type(syn::TypeParam {ident, bounds, ..}) = strait { - (ident, bounds) - } else { - return try_tok!(Err(Error::new(strait.span(), "Missing declare store generic params"))); - }; - - let traittype = if let Some(traittype) = traittypes.first() { - traittype.into_value() - } else { - return try_tok!(Err(Error::new(traittypes.span(), "Trait bound expected"))); - }; - - let extra_genesis = try_tok!(decl_store_extra_genesis( - &scrate, - &traitinstance, - &traittype, - &instance_opts, - &storage_lines, - &extra_genesis.inner, - &where_clause, - )); - let decl_storage_items = decl_storage_items( - &scrate, - &traitinstance, - &traittype, - &instance_opts, - &cratename, - &storage_lines, - &where_clause, - ).unwrap_or_else(|err| err.to_compile_error()); - - let decl_store_items = decl_store_items( - &storage_lines, - ); - let impl_store_items = impl_store_items( - &traitinstance, - &instance_opts.instance, - &storage_lines, - ); - let impl_store_fns = impl_store_fns( - &scrate, - &traitinstance, - &instance_opts.instance, - &storage_lines, - ); - let (store_default_struct, store_metadata) = store_functions_to_metadata( - &scrate, - &traitinstance, - &traittype, - &instance_opts, - &storage_lines, - &where_clause, - &cratename, - ); - - let InstanceOpts { - instance, - bound_instantiable, - .. - } = instance_opts; - - let expanded = quote! { - use #scrate::{ - StorageValue as _, - StorageMap as _, - StorageLinkedMap as _, - StorageDoubleMap as _ - }; - - #scrate_decl - #decl_storage_items - #visibility trait #storetype { - #decl_store_items - } - #store_default_struct - impl<#traitinstance: #traittype, #instance #bound_instantiable> #storetype - for #module_ident<#traitinstance, #instance> #where_clause - { - #impl_store_items - } - impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> - #module_ident<#traitinstance, #instance> #where_clause - { - #impl_store_fns - #[doc(hidden)] - pub fn storage_metadata() -> #scrate::metadata::StorageMetadata { - #store_metadata - } - } - - #extra_genesis - }; - - expanded.into() -} - -fn decl_store_extra_genesis( - scrate: &TokenStream2, - traitinstance: &Ident, - traittype: &syn::TypeParamBound, - instance_opts: &InstanceOpts, - storage_lines: &ext::Punctuated, - extra_genesis: &Option, - where_clause: &Option, -) -> Result { - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let mut is_trait_needed = false; - let mut serde_complete_bound = Vec::new(); - let mut config_field = TokenStream2::new(); - let mut config_field_default = TokenStream2::new(); - let mut builders = TokenStream2::new(); - let mut assimilate_require_generic = instance.is_some(); - let mut builders_clone_bound = Vec::new(); - - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - getter, - config, - build, - storage_type, - default_value, - .. - } = sline; - - let type_infos = get_type_infos(storage_type); - - let opt_build = build - .inner - .as_ref() - .map(|b| { - assimilate_require_generic |= ext::expr_contains_ident(&b.expr.content, traitinstance); - &b.expr.content - }) - .map(|b| quote!( #b )); - - // need build line - let builder = if let Some(ref config) = config.inner { - let ident = if let Some(ident) = config.expr.content.as_ref() { - quote!( #ident ) - } else if let Some(ref getter) = getter.inner { - let ident = &getter.getfn.content; - quote!( #ident ) - } else { - return Err( - Error::new_spanned( - name, - "Invalid storage definiton, couldn't find config identifier: storage must either have a get identifier \ - `get(ident)` or a defined config identifier `config(ident)`" - ) - ); - }; - - if ext::type_contains_ident(type_infos.value_type, traitinstance) { - is_trait_needed = true; - } - - if opt_build.is_none() { - builders_clone_bound.push(type_infos.value_type.clone()); - } - - let value_type = &type_infos.value_type; - serde_complete_bound.push(quote!( #value_type )); - match type_infos.kind { - DeclStorageTypeInfosKind::Map { key_type, .. } => { - serde_complete_bound.push(quote!( #key_type )); - is_trait_needed = is_trait_needed - || ext::type_contains_ident(key_type, traitinstance); - - if opt_build.is_none() { - builders_clone_bound.push(key_type.clone()); - } - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - serde_complete_bound.push(quote!( #key1_type )); - serde_complete_bound.push(quote!( #key2_type )); - is_trait_needed = is_trait_needed - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance); - if opt_build.is_none() { - builders_clone_bound.push(key1_type.clone()); - builders_clone_bound.push(key2_type.clone()); - } - }, - _ => {}, - } - - if type_infos.is_option { - serde_complete_bound.push(type_infos.typ.clone()); - } - - // Propagate doc attributes. - let attrs = attrs.inner.iter().filter_map(|a| a.parse_meta().ok()).filter(|m| m.name() == "doc"); - - let storage_type = type_infos.typ.clone(); - config_field.extend(match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - quote!( #( #[ #attrs ] )* pub #ident: #storage_type, ) - }, - DeclStorageTypeInfosKind::Map {key_type, .. } => { - quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key_type, #storage_type)>, ) - }, - DeclStorageTypeInfosKind::DoubleMap {key1_type, key2_type, .. } => { - quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key1_type, #key2_type, #storage_type)>, ) - }, - }); - - let fielddefault = default_value.inner.as_ref().map(|d| &d.expr).map(|d| - if type_infos.is_option { - quote!( #d.unwrap_or_default() ) - } else { - quote!( #d ) - }).unwrap_or_else(|| quote!( Default::default() )); - - config_field_default.extend(quote!( #ident: #fielddefault, )); - - opt_build.or_else(|| Some(quote!( (|config: &Self| config.#ident.clone()) ))) - } else { - opt_build - }; - - let typ = type_infos.typ; - if let Some(builder) = builder { - builders.extend(match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) { - assimilate_require_generic = true; - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{{ - let v = (#builder)(&self); - < - #name<#struct_trait #instance> as - #scrate::storage::StorageValue<#typ> - >::put::<#typ>(v); - }} - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key_type, traitinstance) - { - assimilate_require_generic = true; - quote!(#traitinstance,) - } else { - quote!() - }; - - let map = if is_linked { - quote! { StorageLinkedMap } - } else { - quote! { StorageMap } - }; - - quote!{{ - let data = (#builder)(&self); - data.into_iter().for_each(|(k, v)| { - < - #name<#struct_trait #instance> as - #scrate::storage::#map<#key_type, #typ> - >::insert::<#key_type, #typ>(k, v); - }); - }} - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance) - { - assimilate_require_generic = true; - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{{ - let data = (#builder)(&self); - data.into_iter().for_each(|(k1, k2, v)| { - < - #name<#struct_trait #instance> as - #scrate::storage::StorageDoubleMap<#key1_type, #key2_type, #typ> - >::insert::<#key1_type, #key2_type, #typ>(k1, k2, v); - }); - }} - }, - }); - } - } - - let mut has_scall = false; - let mut scall = quote!{ let scall: fn(&Self) = |_| {}; scall }; - let mut genesis_extrafields = TokenStream2::new(); - let mut genesis_extrafields_default = TokenStream2::new(); - - // extra genesis - if let Some(eg) = extra_genesis { - for ex_content in eg.content.content.lines.inner.iter() { - match ex_content { - AddExtraGenesisLineEnum::AddExtraGenesisLine(AddExtraGenesisLine { - attrs, - extra_field, - extra_type, - default_value, - .. - }) => { - if ext::type_contains_ident(&extra_type, traitinstance) { - is_trait_needed = true; - } - - serde_complete_bound.push(quote!( #extra_type )); - - let extrafield = &extra_field.content; - genesis_extrafields.extend(quote!{ - #attrs pub #extrafield: #extra_type, - }); - let extra_default = default_value.inner.as_ref().map(|d| &d.expr).map(|e| quote!{ #e }) - .unwrap_or_else(|| quote!( Default::default() )); - genesis_extrafields_default.extend(quote!{ - #extrafield: #extra_default, - }); - }, - AddExtraGenesisLineEnum::AddExtraGenesisBuild(DeclStorageBuild{ expr, .. }) => { - if has_scall { - return Err(Error::new(expr.span(), "Only one build expression allowed for extra genesis")); - } - assimilate_require_generic |= ext::expr_contains_ident(&expr.content, traitinstance); - let content = &expr.content; - scall = quote_spanned! { expr.span() => - let scall: fn(&Self) = #content; scall - }; - has_scall = true; - }, - } - } - } - - let serde_bug_bound = if !serde_complete_bound.is_empty() { - let mut b_ser = String::new(); - let mut b_dser = String::new(); - - serde_complete_bound.into_iter().for_each(|bound| { - let stype = quote!(#bound); - b_ser.push_str(&format!("{} : {}::serde::Serialize, ", stype, scrate)); - b_dser.push_str(&format!("{} : {}::serde::de::DeserializeOwned, ", stype, scrate)); - }); - - quote! { - #[serde(bound(serialize = #b_ser))] - #[serde(bound(deserialize = #b_dser))] - } - } else { - quote!() - }; - - let is_extra_genesis_needed = has_scall - || !config_field.is_empty() - || !genesis_extrafields.is_empty() - || !builders.is_empty(); - if is_extra_genesis_needed { - let (inherent_instance, inherent_bound_instantiable) = if instance.is_some() { - (instance.clone(), bound_instantiable.clone()) - } else { - let instantiable = Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site()); - ( - Some(Ident::new(DEFAULT_INSTANCE_NAME, Span::call_site())), - quote!(: #instantiable), - ) - }; - - let (fparam_struct, fparam_impl, sparam, build_storage_impl) = if is_trait_needed { - ( - quote!(<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>), - quote!(<#traitinstance: #traittype, #instance #bound_instantiable>), - quote!(<#traitinstance, #instance>), - quote!(<#traitinstance: #traittype, #inherent_instance #inherent_bound_instantiable>), - ) - } else { - // do not even need type parameter - ( - quote!(), - quote!(), - quote!(), - quote!(<#traitinstance: #traittype, #inherent_instance #inherent_bound_instantiable>), - ) - }; - - let (fn_generic, fn_traitinstance) = if !is_trait_needed && assimilate_require_generic { - ( - quote!( <#traitinstance: #traittype, #instance #bound_instantiable> ), - quote!( #traitinstance, #instance ) - ) - } else { - (quote!(), quote!()) - }; - - let impl_trait = quote!(BuildModuleGenesisStorage<#traitinstance, #inherent_instance>); - - let extend_where_clause = |to_extend: &mut WhereClause| { - if let Some(where_clause) = where_clause { - to_extend.predicates.extend(where_clause.predicates.iter().cloned()); - } - }; - - let mut genesis_where_clause: WhereClause = syn::parse_quote!( - where #( #builders_clone_bound: Clone ),* - ); - let mut fn_where_clause = genesis_where_clause.clone(); - - - let mut build_storage_where_clause = genesis_where_clause.clone(); - extend_where_clause(&mut build_storage_where_clause); - - if is_trait_needed { - extend_where_clause(&mut genesis_where_clause); - } else if assimilate_require_generic { - extend_where_clause(&mut fn_where_clause); - } - - let res = quote!{ - #[derive(#scrate::Serialize, #scrate::Deserialize)] - #[cfg(feature = "std")] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - #serde_bug_bound - pub struct GenesisConfig#fparam_struct #genesis_where_clause { - #config_field - #genesis_extrafields - } - - #[cfg(feature = "std")] - impl#fparam_impl Default for GenesisConfig#sparam #genesis_where_clause { - fn default() -> Self { - GenesisConfig { - #config_field_default - #genesis_extrafields_default - } - } - } - - #[cfg(feature = "std")] - impl#fparam_impl GenesisConfig#sparam #genesis_where_clause { - pub fn build_storage #fn_generic (self) -> std::result::Result< - ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), - String - > #fn_where_clause { - let mut storage = (Default::default(), Default::default()); - self.assimilate_storage::<#fn_traitinstance>(&mut storage)?; - Ok(storage) - } - - /// Assimilate the storage for this module into pre-existing overlays. - pub fn assimilate_storage #fn_generic ( - self, - tuple_storage: &mut ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), - ) -> std::result::Result<(), String> #fn_where_clause { - #scrate::with_storage(tuple_storage, || { - #builders - - #scall(&self); - - Ok(()) - }) - } - } - - #[cfg(feature = "std")] - impl#build_storage_impl #scrate::sr_primitives::#impl_trait - for GenesisConfig#sparam #build_storage_where_clause - { - fn build_module_genesis_storage( - self, - storage: &mut ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), - ) -> std::result::Result<(), String> { - self.assimilate_storage::<#fn_traitinstance> (storage) - } - } - }; - - Ok(res) - } else { - Ok(quote!()) - } -} - -fn create_and_impl_instance( - instance_prefix: &str, - ident: &Ident, - doc: &TokenStream2, - const_names: &[(Ident, String, String)], - scrate: &TokenStream2, - instantiable: &Ident, - cratename: &Ident, -) -> TokenStream2 { - let mut const_impls = TokenStream2::new(); - - for (const_name, const_value_prefix, const_value_suffix) in const_names { - let const_value = format!("{}{}{}", const_value_prefix, instance_prefix, const_value_suffix); - const_impls.extend(quote! { - const #const_name: &'static str = #const_value; - }); - } - - let prefix = format!("{}{}", instance_prefix, cratename.to_string()); - - quote! { - // Those trait are derived because of wrong bounds for generics - #[cfg_attr(feature = "std", derive(Debug))] - #[derive(Clone, Eq, PartialEq, #scrate::codec::Encode, #scrate::codec::Decode)] - #doc - pub struct #ident; - impl #instantiable for #ident { - const PREFIX: &'static str = #prefix; - #const_impls - } - } -} - -fn decl_storage_items( - scrate: &TokenStream2, - traitinstance: &Ident, - traittype: &syn::TypeParamBound, - instance_opts: &InstanceOpts, - cratename: &Ident, - storage_lines: &ext::Punctuated, - where_clause: &Option, -) -> syn::Result { - let mut impls = TokenStream2::new(); - - let InstanceOpts { - instance, - default_instance, - instantiable, - .. - } = instance_opts; - - let build_prefix = |cratename, name| format!("{} {}", cratename, name); - - // Build Instantiable trait - let mut const_names = vec![]; - - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - storage_type, - name, - .. - } = sline; - - let prefix = build_prefix(cratename, name); - - let type_infos = get_type_infos(storage_type); - - let const_name = syn::Ident::new( - &format!("{}{}", impls::PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - const_names.push((const_name, String::new(), prefix.clone())); - - if let DeclStorageTypeInfosKind::Map { is_linked: true, .. } = type_infos.kind { - let const_name = syn::Ident::new( - &format!("{}{}", impls::HEAD_KEY_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - const_names.push((const_name, "head of ".into(), prefix)); - } - } - - let instantiable = instantiable - .clone() - .unwrap_or_else(|| Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); - - // Declare Instance trait - { - let mut const_impls = TokenStream2::new(); - for (const_name, ..) in &const_names { - const_impls.extend(quote! { - const #const_name: &'static str; - }); - } - - let hide = if instance.is_some() { - quote!() - } else { - quote!(#[doc(hidden)]) - }; - - impls.extend(quote! { - /// Tag a type as an instance of a module. - /// - /// Defines storage prefixes, they must be unique. - #hide - pub trait #instantiable: 'static { - /// The prefix used by any storage entry of an instance. - const PREFIX: &'static str; - #const_impls - } - }); - } - - if instance.is_some() { - let instances = (0..NUMBER_OF_INSTANCE) - .map(|i| { - let name = format!("Instance{}", i); - let ident = Ident::new(&name, proc_macro2::Span::call_site()); - (name, ident, quote! {#[doc=r"Module instance"]}) - }) - .chain( - default_instance - .clone() - .map(|ident| - (String::new(), ident, quote! {#[doc=r"Default module instance"]}) - ) - ); - - // Impl Instance trait for instances - for (instance_prefix, ident, doc) in instances { - impls.extend( - create_and_impl_instance( - &instance_prefix, &ident, &doc, &const_names, scrate, &instantiable, cratename - ) - ); - } - } - - // The name of the inherently available instance. - let inherent_instance = Ident::new(INHERENT_INSTANCE_NAME, Span::call_site()); - - if default_instance.is_some() { - impls.extend(quote! { - #[doc(hidden)] - pub type #inherent_instance = #default_instance; - }); - } else { - impls.extend( - create_and_impl_instance( - "", - &inherent_instance, - "e!(#[doc(hidden)]), - &const_names, - scrate, - &instantiable, - cratename, - ) - ); - } - - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - storage_type, - default_value, - visibility, - .. - } = sline; - - let type_infos = get_type_infos(storage_type); - - if type_infos.is_option && default_value.inner.is_some() { - return Err(syn::Error::new_spanned( - default_value, - "Default values for Option types are not supported" - )); - } - - let fielddefault = default_value.inner - .as_ref() - .map(|d| &d.expr) - .map(|d| quote!( #d )) - .unwrap_or_else(|| quote!{ Default::default() }); - let kind = type_infos.kind.clone(); - // Propagate doc attributes. - let attrs = attrs.inner.iter().filter_map(|a| a.parse_meta().ok()).filter(|m| m.name() == "doc"); - - let i = impls::Impls { - scrate, - visibility, - cratename, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix: build_prefix(cratename, name), - name, - attrs, - where_clause, - }; - - let implementation = match kind { - DeclStorageTypeInfosKind::Simple => { - i.simple_value() - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked: false, hasher } => { - i.map(hasher.into_storage_hasher_struct(), key_type) - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked: true, hasher } => { - i.linked_map(hasher.into_storage_hasher_struct(), key_type) - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, key2_hasher, hasher } => { - i.double_map(hasher.into_storage_hasher_struct(), key1_type, key2_type, key2_hasher.into_storage_hasher_struct()) - }, - }; - impls.extend(implementation) - } - - Ok(impls) -} - -fn decl_store_items(storage_lines: &ext::Punctuated) -> TokenStream2 { - storage_lines.inner.iter().map(|sline| &sline.name) - .fold(TokenStream2::new(), |mut items, name| { - items.extend(quote!(type #name;)); - items - }) -} - -fn impl_store_items( - traitinstance: &Ident, - instance: &Option, - storage_lines: &ext::Punctuated, -) -> TokenStream2 { - storage_lines.inner - .iter() - .fold(TokenStream2::new(), |mut items, line| { - let name = &line.name; - let type_infos = get_type_infos(&line.storage_type); - let requires_trait = match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - ext::type_contains_ident(&type_infos.value_type, traitinstance) - }, - DeclStorageTypeInfosKind::Map { key_type, .. } => { - ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key_type, traitinstance) - } - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance) - } - }; - - let struct_trait = if requires_trait { - quote!(#traitinstance,) - } else { - quote!() - }; - - items.extend( - quote!( - type #name = #name<#struct_trait #instance>; - ) - ); - items - }) -} - -fn impl_store_fns( - scrate: &TokenStream2, - traitinstance: &Ident, - instance: &Option, - storage_lines: &ext::Punctuated, -) -> TokenStream2 { - let mut items = TokenStream2::new(); - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - getter, - storage_type, - .. - } = sline; - - if let Some(getter) = getter.inner.as_ref() { - let get_fn = &getter.getfn.content; - - let type_infos = get_type_infos(storage_type); - let value_type = type_infos.value_type; - - // Propagate doc attributes. - let attrs = attrs.inner.iter().filter_map(|a| a.parse_meta().ok()).filter(|m| m.name() == "doc"); - - let typ = type_infos.typ; - let item = match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) { - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{ - #( #[ #attrs ] )* - pub fn #get_fn() -> #value_type { - < - #name<#struct_trait #instance> as - #scrate::storage::StorageValue<#typ> - >::get() - } - } - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key_type, traitinstance) - { - quote!(#traitinstance,) - } else { - quote!() - }; - - let map = if is_linked { - quote! { StorageLinkedMap } - } else { - quote! { StorageMap } - }; - - quote!{ - #( #[ #attrs ] )* - pub fn #get_fn>(key: K) -> #value_type { - < - #name<#struct_trait #instance> as - #scrate::storage::#map<#key_type, #typ> - >::get(key) - } - } - } - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance) - { - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{ - pub fn #get_fn(k1: KArg1, k2: KArg2) -> #value_type - where - KArg1: #scrate::codec::EncodeLike<#key1_type>, - KArg2: #scrate::codec::EncodeLike<#key2_type>, - { - < - #name<#struct_trait #instance> as - #scrate::storage::StorageDoubleMap<#key1_type, #key2_type, #typ> - >::get(k1, k2) - } - } - } - }; - items.extend(item); - } - } - items -} - -fn store_functions_to_metadata ( - scrate: &TokenStream2, - traitinstance: &Ident, - traittype: &syn::TypeParamBound, - instance_opts: &InstanceOpts, - storage_lines: &ext::Punctuated, - where_clause: &Option, - cratename: &Ident, -) -> (TokenStream2, TokenStream2) { - let InstanceOpts { - comma_instance, - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let mut items = TokenStream2::new(); - let mut default_getter_struct_def = TokenStream2::new(); - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - storage_type, - default_value, - .. - } = sline; - - let type_infos = get_type_infos(storage_type); - let value_type = type_infos.value_type; - - let typ = type_infos.typ; - let styp = clean_type_string(&typ.to_string()); - let stype = match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - quote!{ - #scrate::metadata::StorageEntryType::Plain( - #scrate::metadata::DecodeDifferent::Encode(#styp), - ) - } - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked, hasher } => { - let hasher = hasher.into_metadata(); - let kty = clean_type_string("e!(#key_type).to_string()); - quote!{ - #scrate::metadata::StorageEntryType::Map { - hasher: #scrate::metadata::#hasher, - key: #scrate::metadata::DecodeDifferent::Encode(#kty), - value: #scrate::metadata::DecodeDifferent::Encode(#styp), - is_linked: #is_linked, - } - } - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, key2_hasher, hasher } => { - let hasher = hasher.into_metadata(); - let k1ty = clean_type_string("e!(#key1_type).to_string()); - let k2ty = clean_type_string("e!(#key2_type).to_string()); - let k2_hasher = key2_hasher.into_metadata(); - quote!{ - #scrate::metadata::StorageEntryType::DoubleMap { - hasher: #scrate::metadata::#hasher, - key1: #scrate::metadata::DecodeDifferent::Encode(#k1ty), - key2: #scrate::metadata::DecodeDifferent::Encode(#k2ty), - value: #scrate::metadata::DecodeDifferent::Encode(#styp), - key2_hasher: #scrate::metadata::#k2_hasher, - } - } - }, - }; - let modifier = if type_infos.is_option { - quote!{ - #scrate::metadata::StorageEntryModifier::Optional - } - } else { - quote!{ - #scrate::metadata::StorageEntryModifier::Default - } - }; - let default = default_value.inner.as_ref().map(|d| &d.expr) - .map(|d| { - quote!( #d ) - }) - .unwrap_or_else(|| quote!( Default::default() )); - let mut docs = TokenStream2::new(); - for attr in attrs.inner.iter().filter_map(|v| v.parse_meta().ok()) { - if let syn::Meta::NameValue(syn::MetaNameValue{ - ref ident, - ref lit, - .. - }) = attr { - if ident == "doc" { - docs.extend(quote!(#lit,)); - } - } - } - let str_name = name.to_string(); - let struct_name = proc_macro2::Ident::new(&("__GetByteStruct".to_string() + &str_name), name.span()); - let cache_name = proc_macro2::Ident::new(&("__CACHE_GET_BYTE_STRUCT_".to_string() + &str_name), name.span()); - - let item = quote! { - #scrate::metadata::StorageEntryMetadata { - name: #scrate::metadata::DecodeDifferent::Encode(#str_name), - modifier: #modifier, - ty: #stype, - default: #scrate::metadata::DecodeDifferent::Encode( - #scrate::metadata::DefaultByteGetter( - &#struct_name::<#traitinstance, #instance>(#scrate::rstd::marker::PhantomData) - ) - ), - documentation: #scrate::metadata::DecodeDifferent::Encode(&[ #docs ]), - }, - }; - items.extend(item); - - let def_get = quote! { - #[doc(hidden)] - pub struct #struct_name< - #traitinstance, #instance #bound_instantiable #equal_default_instance - >(pub #scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>); - - #[cfg(feature = "std")] - #[allow(non_upper_case_globals)] - static #cache_name: #scrate::once_cell::sync::OnceCell< - #scrate::rstd::vec::Vec - > = #scrate::once_cell::sync::OnceCell::new(); - - #[cfg(feature = "std")] - impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::metadata::DefaultByte - for #struct_name<#traitinstance, #instance> #where_clause - { - fn default_byte(&self) -> #scrate::rstd::vec::Vec { - use #scrate::codec::Encode; - #cache_name.get_or_init(|| { - let def_val: #value_type = #default; - <#value_type as Encode>::encode(&def_val) - }).clone() - } - } - - unsafe impl<#traitinstance: #traittype, #instance #bound_instantiable> Send - for #struct_name<#traitinstance, #instance> #where_clause {} - - unsafe impl<#traitinstance: #traittype, #instance #bound_instantiable> Sync - for #struct_name<#traitinstance, #instance> #where_clause {} - - #[cfg(not(feature = "std"))] - impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::metadata::DefaultByte - for #struct_name<#traitinstance, #instance> #where_clause - { - fn default_byte(&self) -> #scrate::rstd::vec::Vec { - use #scrate::codec::Encode; - let def_val: #value_type = #default; - <#value_type as Encode>::encode(&def_val) - } - } - }; - - default_getter_struct_def.extend(def_get); - } - - let prefix = cratename.to_string(); - let prefix = instance.as_ref().map_or_else(|| quote!(#prefix), |i| quote!(#i::PREFIX)); - - (default_getter_struct_def, quote!{ - #scrate::metadata::StorageMetadata { - prefix: #scrate::metadata::DecodeDifferent::Encode(#prefix), - entries: #scrate::metadata::DecodeDifferent::Encode(&[ #items ][..]), - } - }) -} - - -#[derive(Debug, Clone)] -pub(crate) struct DeclStorageTypeInfos<'a> { - pub is_option: bool, - pub typ: TokenStream2, - pub value_type: &'a syn::Type, - kind: DeclStorageTypeInfosKind<'a>, -} - -#[derive(Debug, Clone)] -enum DeclStorageTypeInfosKind<'a> { - Simple, - Map { - hasher: HasherKind, - key_type: &'a syn::Type, - is_linked: bool, - }, - DoubleMap { - hasher: HasherKind, - key1_type: &'a syn::Type, - key2_type: &'a syn::Type, - key2_hasher: HasherKind, - } -} - -fn get_type_infos(storage_type: &DeclStorageType) -> DeclStorageTypeInfos { - let (value_type, kind) = match storage_type { - DeclStorageType::Simple(ref st) => (st, DeclStorageTypeInfosKind::Simple), - DeclStorageType::Map(ref map) => (&map.value, DeclStorageTypeInfosKind::Map { - hasher: map.hasher.inner.as_ref().map(|h| h.into()).unwrap_or(HasherKind::Blake2_256), - key_type: &map.key, - is_linked: false, - }), - DeclStorageType::LinkedMap(ref map) => (&map.value, DeclStorageTypeInfosKind::Map { - hasher: map.hasher.inner.as_ref().map(|h| h.into()).unwrap_or(HasherKind::Blake2_256), - key_type: &map.key, - is_linked: true, - }), - DeclStorageType::DoubleMap(ref map) => (&map.value, DeclStorageTypeInfosKind::DoubleMap { - hasher: map.hasher.inner.as_ref().map(|h| h.into()).unwrap_or(HasherKind::Blake2_256), - key1_type: &map.key1, - key2_type: &map.key2.content, - key2_hasher: (&map.key2_hasher).into(), - }), - }; - - let extracted_type = ext::extract_type_option(value_type); - let is_option = extracted_type.is_some(); - let typ = extracted_type.unwrap_or(quote!( #value_type )); - - DeclStorageTypeInfos { - is_option, - typ, - value_type, - kind, - } - -} - -#[derive(Default)] -pub(crate) struct InstanceOpts { - pub instance: Option, - pub default_instance: Option, - pub instantiable: Option, - pub comma_instance: TokenStream2, - pub equal_default_instance: TokenStream2, - pub bound_instantiable: TokenStream2, -} - -fn get_instance_opts( - instance: Option, - instantiable: Option, - default_instance: Option, -) -> Result { - let right_syntax = "Should be $Instance: $Instantiable = $DefaultInstance"; - - match (instance, instantiable, default_instance) { - (Some(instance), Some(instantiable), default_instance) => { - let (equal_default_instance, default_instance) = if let Some(def) = default_instance { - (quote!{= #def}, Some(def)) - } else { - (quote!(), None) - }; - - Ok(InstanceOpts { - comma_instance: quote!{, #instance}, - equal_default_instance, - bound_instantiable: quote!{: #instantiable}, - instance: Some(instance), - default_instance, - instantiable: Some(instantiable), - }) - }, - (None, None, None) => Ok(Default::default()), - (Some(instance), None, _) => Err( - Error::new( - instance.span(), - format!( - "Expect instantiable trait bound for instance: {}. {}", - instance, - right_syntax, - ) - ) - ), - (None, Some(instantiable), _) => Err( - Error::new( - instantiable.span(), - format!( - "Expect instance generic for bound instantiable: {}. {}", - instantiable, - right_syntax, - ) - ) - ), - (None, _, Some(default_instance)) => Err( - Error::new( - default_instance.span(), - format!( - "Expect instance generic for default instance: {}. {}", - default_instance, - right_syntax, - ) - ) - ), - } -} diff --git a/srml/support/procedural/tools/src/syn_ext.rs b/srml/support/procedural/tools/src/syn_ext.rs index 1033ebcce2de5b7da84e3d7ebc21bc0220ae4bfe..1658a6b4aea25caabe196066885b6b5c82202af4 100644 --- a/srml/support/procedural/tools/src/syn_ext.rs +++ b/srml/support/procedural/tools/src/syn_ext.rs @@ -184,13 +184,15 @@ impl ToTokens for Opt

{ } } -pub fn extract_type_option(typ: &syn::Type) -> Option { +pub fn extract_type_option(typ: &syn::Type) -> Option { if let syn::Type::Path(ref path) = typ { let v = path.path.segments.last()?; if v.value().ident == "Option" { - if let syn::PathArguments::AngleBracketed(ref a) = v.value().arguments { - let args = &a.args; - return Some(quote!{ #args }) + // Option has only one type argument in angle bracket. + if let syn::PathArguments::AngleBracketed(a) = &v.value().arguments { + if let syn::GenericArgument::Type(typ) = a.args.last()?.value() { + return Some(typ.clone()) + } } } } @@ -253,4 +255,4 @@ pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool { visit::visit_expr(&mut visit, expr); visit.result -} \ No newline at end of file +} diff --git a/srml/support/src/debug.rs b/srml/support/src/debug.rs new file mode 100644 index 0000000000000000000000000000000000000000..1c4e463bf1234bbd98aba63415d85add5db97dff --- /dev/null +++ b/srml/support/src/debug.rs @@ -0,0 +1,209 @@ +// 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 . + +//! Runtime debugging and logging utilities. +//! +//! This module contains macros and functions that will allow +//! you to print logs out of the runtime code. +//! +//! First and foremost be aware that adding regular logging code to +//! your runtime will have a negative effect on the performance +//! 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 +//! located in `primitives` crate: `primitives::RuntimeDebug`. +//! This custom-derive generates `core::fmt::Debug` implementation, +//! just like regular `derive(Debug)`, however it does not generate +//! any code when the code is compiled to WASM. This means that +//! you can safely sprinkle `RuntimeDebug` in your runtime codebase, +//! without affecting the size. This also allows you to print/log +//! both when the code is running natively or in WASM, but note +//! that WASM debug formatting of structs will be empty. +//! +//! ```rust,no_run +//! use srml_support::debug; +//! +//! #[derive(primitives::RuntimeDebug)] +//! struct MyStruct { +//! a: u64, +//! } +//! +//! // First initialize the logger. +//! // +//! // This is only required when you want the logs to be printed +//! // also during non-native run. +//! // Note that enabling the logger has performance impact on +//! // WASM runtime execution and should be used sparingly. +//! debug::RuntimeLogger::init(); +//! +//! let x = MyStruct { a: 5 }; +//! // will log an info line `"My struct: MyStruct{a:5}"` when running +//! // natively, but will only print `"My struct: "` when running WASM. +//! debug::info!("My struct: {:?}", x); +//! +//! // same output here, although this will print to stdout +//! // (and without log format) +//! debug::print!("My struct: {:?}", x); +//! ``` +//! +//! If you want to avoid extra overhead in WASM, but still be able +//! to print / log when the code is executed natively you can use +//! macros coming from `native` sub-module. This module enables +//! logs conditionally and strips out logs in WASM. +//! +//! ```rust,no_run +//! use srml_support::debug::native; +//! +//! #[derive(primitives::RuntimeDebug)] +//! struct MyStruct { +//! a: u64, +//! } +//! +//! // We don't initialize the logger, since +//! // we are not printing anything out in WASM. +//! // debug::RuntimeLogger::init(); +//! +//! let x = MyStruct { a: 5 }; +//! +//! // Displays an info log when running natively, nothing when WASM. +//! native::info!("My struct: {:?}", x); +//! +//! // same output to stdout, no overhead on WASM. +//! native::print!("My struct: {:?}", x); +//! ``` + +use rstd::vec::Vec; +use rstd::fmt::{self, Debug}; + +pub use log::{info, debug, error, trace, warn}; +pub use crate::runtime_print as print; + +/// Native-only logging. +/// +/// Using any functions from this module will have any effect +/// only if the runtime is running natively (i.e. not via WASM) +#[cfg(feature = "std")] +pub mod native { + pub use super::{info, debug, error, trace, warn, print}; +} + +/// Native-only logging. +/// +/// Using any functions from this module will have any effect +/// only if the runtime is running natively (i.e. not via WASM) +#[cfg(not(feature = "std"))] +pub mod native { + #[macro_export] + macro_rules! noop { + ($($arg:tt)+) => {} + } + pub use noop as info; + pub use noop as debug; + pub use noop as error; + pub use noop as trace; + pub use noop as warn; + pub use noop as print; +} + +/// Print out a formatted message. +#[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(); + } +} + +/// Print out the debuggable type. +pub fn debug(data: &impl Debug) { + runtime_print!("{:?}", data); +} + +/// A target for `core::write!` macro - constructs a string in memory. +#[derive(Default)] +pub struct Writer(Vec); + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.extend(s.as_bytes()); + Ok(()) + } +} + +impl Writer { + /// Print the content of this `Writer` out. + pub fn print(&self) { + runtime_io::print_utf8(&self.0) + } +} + +/// Runtime logger implementation - `log` crate backend. +/// +/// The logger should be initialized if you want to display +/// logs inside the runtime that is not necessarily running natively. +/// +/// When runtime is executed natively any log statements are displayed +/// even if this logger is NOT initialized. +/// +/// Note that even though the logs are not displayed in WASM, they +/// may still affect the size and performance of the generated runtime. +/// To lower the footprint make sure to only use macros from `native` +/// sub-module. +pub struct RuntimeLogger; + +impl RuntimeLogger { + /// Initialize the logger. + /// + /// This is a no-op when running natively (`std`). + #[cfg(feature = "std")] + pub fn init() {} + + /// Initialize the logger. + /// + /// This is a no-op when running natively (`std`). + #[cfg(not(feature = "std"))] + pub fn init() { + static LOGGER: RuntimeLogger = RuntimeLogger;; + let _ = log::set_logger(&LOGGER); + } +} + +impl log::Log for RuntimeLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + // to avoid calling to host twice, we pass everything + // and let the host decide what to print. + // If someone is initializing the logger they should + // know what they are doing. + true + } + + fn log(&self, record: &log::Record) { + use fmt::Write; + let mut w = Writer::default(); + let _ = core::write!(&mut w, "{}", record.args()); + + runtime_io::log( + record.level().into(), + record.target().as_bytes(), + &w.0, + ); + } + + fn flush(&self) {} +} diff --git a/srml/support/src/dispatch.rs b/srml/support/src/dispatch.rs index 67cd3e44bf92f16812598610319bce71e28b2396..df86f436117a1bf7c66508bb26a56251473bacbb 100644 --- a/srml/support/src/dispatch.rs +++ b/srml/support/src/dispatch.rs @@ -17,19 +17,19 @@ //! Dispatch system. Contains a macro for defining runtime modules and //! generating values representing lazy module function calls. -pub use crate::rstd::{result, prelude::{Vec, Clone, Eq, PartialEq}, marker}; -#[cfg(feature = "std")] -pub use std::fmt; +pub use crate::rstd::{result, fmt, prelude::{Vec, Clone, Eq, PartialEq}, marker}; pub use crate::codec::{Codec, EncodeLike, Decode, Encode, Input, Output, HasCompact, EncodeAsRef}; pub use srml_metadata::{ FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata, - ModuleConstantMetadata, DefaultByte, DefaultByteGetter, + ModuleConstantMetadata, DefaultByte, DefaultByteGetter, ModuleErrorMetadata, ErrorMetadata }; pub use sr_primitives::{ weights::{ SimpleDispatchInfo, GetDispatchInfo, DispatchInfo, WeighData, ClassifyDispatch, TransactionPriority - }, traits::{Dispatchable, DispatchResult, ModuleDispatchError}, DispatchError + }, + traits::{Dispatchable, DispatchResult, ModuleDispatchError}, + DispatchError, }; /// A type that cannot be instantiated. @@ -48,18 +48,9 @@ pub trait Callable { // https://github.com/rust-lang/rust/issues/51331 pub type CallableCallFor = >::Call; -#[cfg(feature = "std")] pub trait Parameter: Codec + EncodeLike + Clone + Eq + fmt::Debug {} - -#[cfg(feature = "std")] impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {} -#[cfg(not(feature = "std"))] -pub trait Parameter: Codec + EncodeLike + Clone + Eq {} - -#[cfg(not(feature = "std"))] -impl Parameter for T where T: Codec + EncodeLike + Clone + Eq {} - /// Declares a `Module` struct and a `Call` enum, which implements the dispatch logic. /// /// ## Declaration @@ -1071,8 +1062,7 @@ macro_rules! decl_module { $crate::__check_reserved_fn_name! { $( $fn_name )* } // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, Copy, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Clone, Copy, PartialEq, Eq, $crate::RuntimeDebug)] pub struct $mod_type< $trait_instance: $trait_name $(, $instance: $instantiable $( = $module_default_instance)?)? @@ -1223,7 +1213,6 @@ macro_rules! decl_module { for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* {} - #[cfg(feature = "std")] impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::fmt::Debug for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { @@ -1298,6 +1287,14 @@ macro_rules! decl_module { { $( $other_where_bounds )* } $( $constants )* } + + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::ModuleErrorMetadata + for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* + { + fn metadata() -> &'static [$crate::dispatch::ErrorMetadata] { + <$error_type as $crate::dispatch::ModuleErrorMetadata>::metadata() + } + } } } @@ -1317,8 +1314,12 @@ macro_rules! impl_outer_dispatch { } ) => { $(#[$attr])* - #[derive(Clone, PartialEq, Eq, $crate::codec::Encode, $crate::codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive( + Clone, PartialEq, Eq, + $crate::codec::Encode, + $crate::codec::Decode, + $crate::RuntimeDebug, + )] pub enum $call_type { $( $camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> ) diff --git a/srml/support/src/error.rs b/srml/support/src/error.rs index 2f9d9379bfd04b23aabc3727384656fd39bf56a6..9aa13713daafb7f418cd82efee587613e0841d32 100644 --- a/srml/support/src/error.rs +++ b/srml/support/src/error.rs @@ -18,6 +18,7 @@ #[doc(hidden)] pub use sr_primitives::traits::LookupError; +pub use srml_metadata::{ModuleErrorMetadata, ErrorMetadata, DecodeDifferent}; /// Declare an error type for a runtime module. /// @@ -49,20 +50,19 @@ macro_rules! decl_error { $(#[$attr:meta])* pub enum $error:ident { $( - $( #[$variant_attr:meta] )* + $( #[doc = $doc_attr:tt] )* $name:ident ),* $(,)? } ) => { - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)] $(#[$attr])* pub enum $error { Other(&'static str), CannotLookup, $( - $(#[$variant_attr])* + $( #[doc = $doc_attr] )* $name ),* } @@ -115,6 +115,21 @@ macro_rules! decl_error { $crate::dispatch::DispatchError::new(None, self.as_u8(), Some(self.as_str())) } } + + impl $crate::error::ModuleErrorMetadata for $error { + fn metadata() -> &'static [$crate::error::ErrorMetadata] { + &[ + $( + $crate::error::ErrorMetadata { + name: $crate::error::DecodeDifferent::Encode(stringify!($name)), + documentation: $crate::error::DecodeDifferent::Encode(&[ + $( $doc_attr ),* + ]), + } + ),* + ] + } + } }; (@GENERATE_AS_U8 $self:ident diff --git a/srml/support/src/event.rs b/srml/support/src/event.rs index eb5cc20635725b40b4416a0723fb3157a8f22f56..3411c93922e2b08c034171d117adaa69cce989a3 100644 --- a/srml/support/src/event.rs +++ b/srml/support/src/event.rs @@ -121,8 +121,12 @@ macro_rules! decl_event { } ) => { // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq, $crate::codec::Encode, $crate::codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive( + Clone, PartialEq, Eq, + $crate::codec::Encode, + $crate::codec::Decode, + $crate::RuntimeDebug, + )] /// Events for this module. /// $(#[$attr])* @@ -260,9 +264,12 @@ macro_rules! __decl_generic_event { /// [`Trait`]: trait.Trait.html pub type Event<$event_generic_param $(, $instance $( = $event_default_instance)? )?> = RawEvent<$( $generic_type ),* $(, $instance)? >; - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq, $crate::codec::Encode, $crate::codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive( + Clone, PartialEq, Eq, + $crate::codec::Encode, + $crate::codec::Decode, + $crate::RuntimeDebug, + )] /// Events for this module. /// $(#[$attr])* @@ -452,8 +459,12 @@ macro_rules! impl_outer_event { $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; ) => { $crate::paste::item! { - #[derive(Clone, PartialEq, Eq, $crate::codec::Encode, $crate::codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive( + Clone, PartialEq, Eq, + $crate::codec::Encode, + $crate::codec::Decode, + $crate::RuntimeDebug, + )] $(#[$attr])* #[allow(non_camel_case_types)] pub enum $name { diff --git a/srml/support/src/hash.rs b/srml/support/src/hash.rs index 3775ebc26bc3b9a4bd2ffdee10d0bab0a90ac280..cbd78f603241eb5746045be59f6c4f462af77b49 100644 --- a/srml/support/src/hash.rs +++ b/srml/support/src/hash.rs @@ -59,7 +59,7 @@ impl StorageHasher for Twox64Concat { type Output = Vec; fn hash(x: &[u8]) -> Vec { twox_64(x) - .into_iter() + .iter() .chain(x.into_iter()) .cloned() .collect::>() diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index 32a608015375fd0b63a61e583be13ec61c969ded..e47c6137bb894dffa329bc7b26b317adcca528b4 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -40,10 +40,13 @@ pub use paste; pub use runtime_io::with_storage; #[doc(hidden)] pub use runtime_io::storage_root; +#[doc(hidden)] +pub use sr_primitives::RuntimeDebug; #[macro_use] -pub mod dispatch; +pub mod debug; #[macro_use] +pub mod dispatch; pub mod storage; mod hash; #[macro_use] @@ -173,10 +176,10 @@ macro_rules! assert_err { #[macro_export] #[cfg(feature = "std")] macro_rules! assert_ok { - ( $x:expr ) => { + ( $x:expr $(,)? ) => { assert_eq!($x, Ok(())); }; - ( $x:expr, $y:expr ) => { + ( $x:expr, $y:expr $(,)? ) => { assert_eq!($x, Ok($y)); } } @@ -224,8 +227,7 @@ macro_rules! __assert_eq_uvec { /// The void type - it cannot exist. // Oh rust, you crack me up... -#[derive(Clone, Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Eq, PartialEq, RuntimeDebug)] pub enum Void {} #[cfg(feature = "std")] @@ -236,13 +238,11 @@ pub use serde::{Serialize, Deserialize}; mod tests { use super::*; use codec::{Codec, EncodeLike}; - use runtime_io::with_externalities; - use primitives::Blake2Hasher; - pub use srml_metadata::{ + use srml_metadata::{ DecodeDifferent, StorageEntryMetadata, StorageMetadata, StorageEntryType, - StorageEntryModifier, DefaultByte, DefaultByteGetter, StorageHasher + StorageEntryModifier, DefaultByteGetter, StorageHasher, }; - pub use rstd::marker::PhantomData; + use rstd::marker::PhantomData; pub trait Trait { type BlockNumber: Codec + EncodeLike + Default; @@ -262,10 +262,11 @@ mod tests { decl_storage! { trait Store for Module as Example { - pub Data get(data) build(|_| vec![(15u32, 42u64)]): linked_map hasher(twox_64_concat) u32 => u64; + pub Data get(fn data) build(|_| vec![(15u32, 42u64)]): linked_map hasher(twox_64_concat) u32 => u64; pub OptionLinkedMap: linked_map u32 => Option; - pub GenericData get(generic_data): linked_map hasher(twox_128) T::BlockNumber => T::BlockNumber; - pub GenericData2 get(generic_data2): linked_map T::BlockNumber => 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; + pub GetterNoFnKeyword get(no_fn): Option; pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]): double_map hasher(twox_64_concat) u32, blake2_256(u32) => u64; @@ -281,7 +282,7 @@ mod tests { type Origin = u32; } - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig::default().build_storage().unwrap().into() } @@ -289,7 +290,7 @@ mod tests { #[test] fn linked_map_issue_3318() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { OptionLinkedMap::insert(1, 1); assert_eq!(OptionLinkedMap::get(1), Some(1)); OptionLinkedMap::insert(1, 2); @@ -299,7 +300,7 @@ mod tests { #[test] fn linked_map_swap_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { OptionLinkedMap::insert(0, 0); OptionLinkedMap::insert(1, 1); OptionLinkedMap::insert(2, 2); @@ -357,7 +358,7 @@ mod tests { #[test] fn linked_map_basic_insert_remove_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // initialized during genesis assert_eq!(Map::get(&15u32), 42u64); @@ -383,7 +384,7 @@ mod tests { #[test] fn linked_map_enumeration_and_head_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Map::head(), Some(15)); assert_eq!(Map::enumerate().collect::>(), vec![(15, 42)]); // insert / remove @@ -435,7 +436,7 @@ mod tests { #[test] fn double_map_basic_insert_remove_remove_prefix_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { type DoubleMap = DataDM; // initialized during genesis assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64); @@ -475,7 +476,7 @@ mod tests { #[test] fn double_map_append_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { type DoubleMap = AppendableDM; let key1 = 17u32; @@ -547,6 +548,15 @@ mod tests { ), documentation: DecodeDifferent::Encode(&[]), }, + StorageEntryMetadata { + name: DecodeDifferent::Encode("GetterNoFnKeyword"), + modifier: StorageEntryModifier::Optional, + ty: StorageEntryType::Plain(DecodeDifferent::Encode("u32")), + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGetterNoFnKeyword(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, StorageEntryMetadata { name: DecodeDifferent::Encode("DataDM"), modifier: StorageEntryModifier::Default, diff --git a/srml/support/src/metadata.rs b/srml/support/src/metadata.rs index 7ae5f7d193cc5ade1059621d97313c3dc63c10f0..a223a14f9e4050e64fe559804769b073f211154c 100644 --- a/srml/support/src/metadata.rs +++ b/srml/support/src/metadata.rs @@ -17,7 +17,7 @@ pub use srml_metadata::{ DecodeDifferent, FnEncode, RuntimeMetadata, ModuleMetadata, RuntimeMetadataLastVersion, DefaultByteGetter, RuntimeMetadataPrefixed, StorageEntryMetadata, StorageMetadata, - StorageEntryType, StorageEntryModifier, DefaultByte, StorageHasher + StorageEntryType, StorageEntryModifier, DefaultByte, StorageHasher, ModuleErrorMetadata }; /// Implements the metadata support for the given runtime and all its modules. @@ -96,6 +96,11 @@ macro_rules! __runtime_modules_to_metadata { $crate::metadata::FnEncode( $mod::$module::<$runtime $(, $mod::$instance )?>::module_constants_metadata ) + ), + errors: $crate::metadata::DecodeDifferent::Encode( + $crate::metadata::FnEncode( + <$mod::$module::<$runtime $(, $mod::$instance )?> as $crate::metadata::ModuleErrorMetadata>::metadata + ) ) }; $( $rest )* @@ -227,6 +232,7 @@ mod tests { use srml_metadata::{ EventMetadata, StorageEntryModifier, StorageEntryType, FunctionMetadata, StorageEntryMetadata, ModuleMetadata, RuntimeMetadataPrefixed, DefaultByte, ModuleConstantMetadata, DefaultByteGetter, + ErrorMetadata, }; use codec::{Encode, Decode}; use crate::traits::Get; @@ -278,7 +284,7 @@ mod tests { } mod event_module { - use crate::dispatch::Result; + use crate::dispatch::DispatchResult; pub trait Trait { type Origin; @@ -296,7 +302,19 @@ mod tests { decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn aux_0(_origin) -> Result { unreachable!() } + type Error = Error; + + fn aux_0(_origin) -> DispatchResult { unreachable!() } + } + } + + crate::decl_error! { + pub enum Error { + /// Some user input error + UserInputError, + /// Something bad happened + /// this could be due to many reasons + BadThingHappened, } } } @@ -447,6 +465,7 @@ mod tests { } ]) ), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), }, ModuleMetadata { name: DecodeDifferent::Encode("Module"), @@ -469,6 +488,19 @@ mod tests { ]) )), 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"), @@ -505,6 +537,7 @@ mod tests { ]) )), constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), }, ]) }; diff --git a/srml/support/src/origin.rs b/srml/support/src/origin.rs index f3fec6e3ae6ce16909abd27487262923e8286ee2..6da9bc13858364f42aa9ab58ac750802b8588157 100644 --- a/srml/support/src/origin.rs +++ b/srml/support/src/origin.rs @@ -151,9 +151,7 @@ macro_rules! impl_outer_origin { $( $module:ident $( < $generic:ident > )? $( { $generic_instance:ident } )? ,)* ) => { $crate::paste::item! { - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)] $(#[$attr])* #[allow(non_camel_case_types)] pub enum $name { diff --git a/srml/support/src/runtime.rs b/srml/support/src/runtime.rs index 491252ce7083bb1ba55b5a5e0825106210b6ff04..52bf48baa574096c81835cf47b57972ddb76f4bb 100644 --- a/srml/support/src/runtime.rs +++ b/srml/support/src/runtime.rs @@ -191,8 +191,7 @@ macro_rules! construct_runtime { )* }; ) => { - #[derive(Clone, Copy, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Clone, Copy, PartialEq, Eq, $crate::RuntimeDebug)] pub struct $runtime; impl $crate::sr_primitives::traits::GetNodeBlockType for $runtime { type NodeBlock = $node_block; diff --git a/srml/support/src/storage/generator/mod.rs b/srml/support/src/storage/generator/mod.rs index ab7616158afd80eda28aca445bc18ee8f3be4326..1bda791023792f2e2dc73b4f65a737f3dcd155e9 100644 --- a/srml/support/src/storage/generator/mod.rs +++ b/srml/support/src/storage/generator/mod.rs @@ -30,3 +30,52 @@ pub use linked_map::{StorageLinkedMap, Enumerator, Linkage}; pub use map::StorageMap; pub use double_map::StorageDoubleMap; pub use value::StorageValue; + + +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use runtime_io::TestExternalities; + use codec::Encode; + use crate::storage::{unhashed, generator::StorageValue}; + + struct Runtime {} + pub trait Trait { + type Origin; + type BlockNumber; + } + + impl Trait for Runtime { + type Origin = u32; + type BlockNumber = u32; + } + + decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + crate::decl_storage! { + trait Store for Module as Runtime { + Value get(fn value) config(): (u64, u64); + } + } + + #[test] + fn value_translate_works() { + let t = GenesisConfig::default().build_storage().unwrap(); + TestExternalities::new(t).execute_with(|| { + // put the old value `1111u32` in the storage. + let key = Value::storage_value_final_key(); + unhashed::put_raw(&key, &1111u32.encode()); + + // translate + let translate_fn = |old: Option| -> Option<(u64, u64)> { + old.map(|o| (o.into(), (o*2).into())) + }; + let _ = Value::translate(translate_fn); + + // new storage should be `(1111, 1111 * 2)` + assert_eq!(Value::get(), (1111, 2222)); + }) + } +} diff --git a/srml/support/src/storage/generator/value.rs b/srml/support/src/storage/generator/value.rs index 8423503dde20e2ba8f6e0fb02cc08885f944def3..5ebc25a70af2a6eba052c5870ad61a5443fdb28d 100644 --- a/srml/support/src/storage/generator/value.rs +++ b/srml/support/src/storage/generator/value.rs @@ -16,7 +16,7 @@ #[cfg(not(feature = "std"))] use rstd::prelude::*; -use codec::{FullCodec, Encode, EncodeAppend, EncodeLike}; +use codec::{FullCodec, Encode, EncodeAppend, EncodeLike, Decode}; use crate::{storage::{self, unhashed}, hash::{Twox128, StorageHasher}, traits::Len}; /// Generator for `StorageValue` used by `decl_storage`. @@ -60,6 +60,23 @@ impl> storage::StorageValue for G { G::from_optional_value_to_query(value) } + fn translate) -> Option>(f: F) -> Result, ()> { + let key = Self::storage_value_final_key(); + + // attempt to get the length directly. + let maybe_old = match unhashed::get_raw(&key) { + Some(old_data) => Some(O::decode(&mut &old_data[..]).map_err(|_| ())?), + None => None, + }; + let maybe_new = f(maybe_old); + if let Some(new) = maybe_new.as_ref() { + new.using_encoded(|d| unhashed::put_raw(&key, d)); + } else { + unhashed::kill(&key); + } + Ok(maybe_new) + } + fn put>(val: Arg) { unhashed::put(&Self::storage_value_final_key(), &val) } diff --git a/srml/support/src/storage/mod.rs b/srml/support/src/storage/mod.rs index 102ba1d5b56f983b1c96266e2eaf72d8c01ab717..df8a73708cdb7035fb1297d0edcfbfbd34c75913 100644 --- a/srml/support/src/storage/mod.rs +++ b/srml/support/src/storage/mod.rs @@ -17,17 +17,18 @@ //! Stuff to do with the runtime's storage. use rstd::prelude::*; -use codec::{FullCodec, FullEncode, Encode, EncodeAppend, EncodeLike}; +use codec::{FullCodec, FullEncode, Encode, EncodeAppend, EncodeLike, Decode}; use crate::traits::Len; -#[macro_use] -pub mod storage_items; pub mod unhashed; pub mod hashed; pub mod child; pub mod generator; /// A trait for working with macro-generated storage values under the substrate storage API. +/// +/// Details on implementation can be found at +/// [`generator::StorageValue`] pub trait StorageValue { /// The type that get/take return. type Query; @@ -41,6 +42,28 @@ pub trait StorageValue { /// Load the value from the provided storage instance. fn get() -> Self::Query; + /// Translate a value from some previous type (`O`) to the current type. + /// + /// `f: F` is the translation function. + /// + /// Returns `Err` if the storage item could not be interpreted as the old type, and Ok, along + /// with the new value if it could. + /// + /// NOTE: This operates from and to `Option<_>` types; no effort is made to respect the default + /// value of the original type. + /// + /// # Warning + /// + /// This function must be used with care, before being updated the storage still contains the + /// old type, thus other calls (such as `get`) will fail at decoding it. + /// + /// # Usage + /// + /// This would typically be called inside the module implementation of on_initialize, while + /// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More + /// precisely prior initialized modules doesn't make use of this storage). + fn translate) -> Option>(f: F) -> Result, ()>; + /// Store a value under this key into the provided storage instance. fn put>(val: Arg); @@ -91,6 +114,9 @@ pub trait StorageValue { } /// A strongly-typed map in storage. +/// +/// Details on implementation can be found at +/// [`generator::StorageMap`] pub trait StorageMap { /// The type that get/take return. type Query; @@ -158,6 +184,9 @@ pub trait StorageMap { /// A strongly-typed linked map in storage. /// /// Similar to `StorageMap` but allows to enumerate other elements and doesn't implement append. +/// +/// Details on implementation can be found at +/// [`generator::StorageLinkedMap`] pub trait StorageLinkedMap { /// The type that get/take return. type Query; @@ -207,6 +236,9 @@ pub trait StorageLinkedMap { /// /// It provides an important ability to efficiently remove all entries /// that have a common first key. +/// +/// Details on implementation can be found at +/// [`generator::StorageDoubleMap`] pub trait StorageDoubleMap { /// The type that get/take returns. type Query; diff --git a/srml/support/src/traits.rs b/srml/support/src/traits.rs index 88e61593659dca3a3a46a04dbd95e4a95fd6a963..b321aeaeb9fe9019e0270dc5dcd5ebfc04d8f05a 100644 --- a/srml/support/src/traits.rs +++ b/srml/support/src/traits.rs @@ -18,12 +18,12 @@ //! //! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module. -use rstd::{prelude::*, result, marker::PhantomData, ops::Div}; +use rstd::{prelude::*, result, marker::PhantomData, ops::Div, fmt::Debug}; use codec::{FullCodec, Codec, Encode, Decode}; use primitives::u32_trait::Value as U32; use sr_primitives::{ ConsensusEngineId, - traits::{MaybeSerializeDebug, SimpleArithmetic, Saturating}, + traits::{MaybeSerializeDeserialize, SimpleArithmetic, Saturating}, }; /// Anything that can have a `::len()` method. @@ -148,7 +148,7 @@ pub trait OnUnbalanced { fn on_unbalanced(amount: Imbalance); } -impl OnUnbalanced for () { +impl OnUnbalanced for () { fn on_unbalanced(amount: Imbalance) { drop(amount); } } @@ -156,6 +156,9 @@ impl OnUnbalanced for () { #[derive(Copy, Clone, Eq, PartialEq)] pub enum ExistenceRequirement { /// Operation must not result in the account going out of existence. + /// + /// Note this implies that if the account never existed in the first place, then the operation + /// may legitimately leave the account unchanged and still non-existent. KeepAlive, /// Operation may result in account going out of existence. AllowDeath, @@ -254,7 +257,7 @@ pub enum SignedImbalance>{ impl< P: Imbalance, N: Imbalance, - B: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDebug + Default, + B: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default, > SignedImbalance { pub fn zero() -> Self { SignedImbalance::Positive(P::zero()) @@ -317,7 +320,7 @@ impl< /// Abstraction over a fungible assets system. pub trait Currency { /// The balance of an account. - type Balance: SimpleArithmetic + FullCodec + Copy + MaybeSerializeDebug + Default; + type Balance: SimpleArithmetic + 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. @@ -378,7 +381,7 @@ pub trait Currency { fn ensure_can_withdraw( who: &AccountId, _amount: Self::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, new_balance: Self::Balance, ) -> result::Result<(), &'static str>; @@ -456,7 +459,7 @@ pub trait Currency { fn withdraw( who: &AccountId, value: Self::Balance, - reason: WithdrawReason, + reasons: WithdrawReasons, liveness: ExistenceRequirement, ) -> result::Result; @@ -464,11 +467,11 @@ pub trait Currency { fn settle( who: &AccountId, value: Self::PositiveImbalance, - reason: WithdrawReason, + reasons: WithdrawReasons, liveness: ExistenceRequirement, ) -> result::Result<(), Self::PositiveImbalance> { let v = value.peek(); - match Self::withdraw(who, v, reason, liveness) { + match Self::withdraw(who, v, reasons, liveness) { Ok(opposite) => Ok(drop(value.offset(opposite))), _ => Err(value), } @@ -611,6 +614,8 @@ bitmask! { Reserve = 0b00000100, /// In order to pay some other (higher-level) fees. Fee = 0b00001000, + /// In order to tip a validator for transaction inclusion. + Tip = 0b00010000, } } @@ -627,7 +632,7 @@ impl WithdrawReasons { /// # use srml_support::traits::{WithdrawReason, WithdrawReasons}; /// # fn main() { /// assert_eq!( - /// WithdrawReason::Fee | WithdrawReason::Transfer | WithdrawReason::Reserve, + /// WithdrawReason::Fee | WithdrawReason::Transfer | WithdrawReason::Reserve | WithdrawReason::Tip, /// WithdrawReasons::except(WithdrawReason::TransactionPayment), /// ); /// # } @@ -717,3 +722,23 @@ pub trait InitializeMembers { impl InitializeMembers for () { fn initialize_members(_: &[T]) {} } + +// A trait that is able to provide randomness. +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 + /// different result to other callers of this function; use it like + /// `random(&b"my context"[..])`. + fn random(subject: &[u8]) -> Output; + + /// Get the basic random seed. + /// + /// In general you won't want to use this, but rather `Self::random` which allows you to give a + /// subject for the random result and whose value will be independently low-influence random + /// from any other such seeds. + fn random_seed() -> Output { + Self::random(&[][..]) + } +} diff --git a/srml/support/test/Cargo.toml b/srml/support/test/Cargo.toml index bb1b3749e9d5f45c4ef8bb5359ee0ebdb7f0b19b..a5e375b5c6b9383f46069df871f2f4e5094cc063 100644 --- a/srml/support/test/Cargo.toml +++ b/srml/support/test/Cargo.toml @@ -10,8 +10,9 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = runtime-io ={ package = "sr-io", path = "../../../core/sr-io", default-features = false } support = { package = "srml-support", version = "2", path = "../", default-features = false } inherents = { package = "substrate-inherents", path = "../../../core/inherents", default-features = false } +sr-primitives = { package = "sr-primitives", path = "../../../core/sr-primitives", default-features = false } primitives = { package = "substrate-primitives", path = "../../../core/primitives", default-features = false } -trybuild = "1.0.14" +trybuild = "1.0.17" pretty_assertions = "0.6.1" [features] @@ -23,4 +24,5 @@ std = [ "support/std", "inherents/std", "primitives/std", + "sr-primitives/std", ] diff --git a/srml/support/test/src/lib.rs b/srml/support/test/src/lib.rs index 0e4ab936291415d31d533d2fa981c4eeee7c9458..b65bc43cbb5a8b2ca728ea7c3d419d34314222f1 100644 --- a/srml/support/test/src/lib.rs +++ b/srml/support/test/src/lib.rs @@ -16,9 +16,3 @@ //! Test crate for srml_support. Allow to make use of `support::decl_storage`. //! See tests directory. - -#[test] -fn reserved_keyword() { - let t = trybuild::TestCases::new(); - t.compile_fail("tests/reserved_keyword/*.rs"); -} diff --git a/srml/support/src/storage/storage_items.rs b/srml/support/test/tests/decl_storage.rs similarity index 63% rename from srml/support/src/storage/storage_items.rs rename to srml/support/test/tests/decl_storage.rs index 82f6a08685d0cdea77459b185f6887d3266f5185..631cb27bc42f1f69ed14bb6a72035974d11f3531 100644 --- a/srml/support/src/storage/storage_items.rs +++ b/srml/support/test/tests/decl_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// Copyright 2019 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,276 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Strongly typed wrappers around values in storage. -//! -//! This crate exports a macro `storage_items!` and traits describing behavior of generated -//! structs. -//! -//! Two kinds of data types are currently supported: -//! - values -//! - maps -//! -//! # Examples: -//! -//! ```rust -//! #[macro_use] -//! extern crate srml_support; -//! -//! type AuthorityId = [u8; 32]; -//! type Balance = u64; -//! pub type SessionKey = [u8; 32]; -//! -//! storage_items! { -//! // public value -//! pub Value: b"putd_key" => SessionKey; -//! // private map. -//! Balances: b"private_map:" => map [AuthorityId => Balance]; -//! } -//! -//!# fn main() { } -//! ``` - -#[doc(hidden)] -pub use crate::rstd::borrow::Borrow; -#[doc(hidden)] -pub use crate::rstd::marker::PhantomData; -#[doc(hidden)] -pub use crate::rstd::boxed::Box; - -#[doc(hidden)] -pub fn id(t: T) -> T { - t -} - -#[doc(hidden)] -pub use Some; - -#[doc(hidden)] -pub fn unwrap_or_default(t: Option) -> T { - t.unwrap_or_else(|| Default::default()) -} - -#[doc(hidden)] -pub fn require(t: Option) -> T { - t.expect("Required values must be in storage") -} - -// FIXME #1466 Remove this in favor of `decl_storage` macro. -/// Declares strongly-typed wrappers around codec-compatible types in storage. -#[macro_export] -macro_rules! storage_items { - // simple values - ($name:ident : $key:expr => $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!(() () (OPTION_TYPE Option<$ty>) (id) (id) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident : $key:expr => $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) () (OPTION_TYPE Option<$ty>) (id) (id) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident : $key:expr => default $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!(() () (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident : $key:expr => default $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) () (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident : $key:expr => required $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!(() () (RAW_TYPE $ty) (require) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident : $key:expr => required $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) () (RAW_TYPE $ty) (require) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - - ($name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!(() ($getfn) (OPTION_TYPE Option<$ty>) (id) (id) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $key:expr => $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) ($getfn) (OPTION_TYPE Option<$ty>) (id) (id) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $key:expr => default $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (require) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $key:expr => required $ty:ty; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (require) (Some) $name: $key => $ty); - storage_items!($($t)*); - }; - - // maps - ($name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!(() () (OPTION_TYPE Option<$ty>) (id) (id) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) () (OPTION_TYPE Option<$ty>) (id) (id) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!(() () (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) () (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!(() () (RAW_TYPE $ty) (require) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) () (RAW_TYPE $ty) (require) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - - ($name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!(() ($getfn) (OPTION_TYPE Option<$ty>) (id) (id) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $prefix:expr => map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) ($getfn) (OPTION_TYPE Option<$ty>) (id) (id) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $prefix:expr => default map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (unwrap_or_default) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - ($name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!(() ($getfn) (RAW_TYPE $ty) (require) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - (pub $name:ident get($getfn:ident) : $prefix:expr => required map [$kty:ty => $ty:ty]; $($t:tt)*) => { - $crate::__storage_items_internal!((pub) ($getfn) (RAW_TYPE $ty) (require) (Some) $name: $prefix => map [$kty => $ty]); - storage_items!($($t)*); - }; - - () => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __storage_items_internal { - // generator for values. - (($($vis:tt)*) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($into_query:ident) ($into_opt_val:ident) $name:ident : $key:expr => $ty:ty) => { - $crate::__storage_items_internal!{ ($($vis)*) () ($wraptype $gettype) ($into_query) ($into_opt_val) $name : $key => $ty } - pub fn $get_fn() -> $gettype { <$name as $crate::storage::StorageValue<$ty>> :: get() } - }; - (($($vis:tt)*) () ($wraptype:ident $gettype:ty) ($into_query:ident) ($into_opt_val:ident) $name:ident : $key:expr => $ty:ty) => { - $($vis)* struct $name; - - impl $crate::storage::generator::StorageValue<$ty> for $name { - type Query = $gettype; - - fn unhashed_key() -> &'static [u8] { - $key - } - - fn from_optional_value_to_query(v: Option<$ty>) -> Self::Query { - $crate::storage::storage_items::$into_query(v) - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<$ty> { - $crate::storage::storage_items::$into_opt_val(v) - } - } - }; - // generator for maps. - (($($vis:tt)*) ($get_fn:ident) ($wraptype:ident $gettype:ty) ($into_query:ident) ($into_opt_val:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => { - $crate::__storage_items_internal!{ ($($vis)*) () ($wraptype $gettype) ($into_query) ($into_opt_val) $name : $prefix => map [$kty => $ty] } - pub fn $get_fn>(key: K) -> $gettype { - <$name as $crate::storage::StorageMap<$kty, $ty>> :: get(key.borrow()) - } - }; - (($($vis:tt)*) () ($wraptype:ident $gettype:ty) ($into_query:ident) ($into_opt_val:ident) $name:ident : $prefix:expr => map [$kty:ty => $ty:ty]) => { - $($vis)* struct $name; - - impl $crate::storage::generator::StorageMap<$kty, $ty> for $name { - type Query = $gettype; - type Hasher = $crate::Blake2_256; - - fn prefix() -> &'static [u8] { - $prefix - } - - fn from_optional_value_to_query(v: Option<$ty>) -> Self::Query { - $crate::storage::storage_items::$into_query(v) - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<$ty> { - $crate::storage::storage_items::$into_opt_val(v) - } - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __handle_wrap_internal { - (RAW_TYPE { $($raw:tt)* } { $($option:tt)* }) => { - $($raw)*; - }; - (OPTION_TYPE { $($raw:tt)* } { $($option:tt)* }) => { - $($option)*; - }; -} - -// FIXME: revisit this idiom once we get `type`s in `impl`s. -/*impl Module { - type Now = super::Now; -}*/ - #[cfg(test)] // Do not complain about unused `dispatch` and `dispatch_aux`. #[allow(dead_code)] mod tests { - use crate::metadata::*; - use crate::metadata::StorageHasher; - use crate::rstd::marker::PhantomData; - use crate::codec::{Encode, Decode, EncodeLike}; - - storage_items! { - Value: b"a" => u32; - Map: b"c:" => map [u32 => [u8; 32]]; - } + use support::metadata::*; + use support::metadata::StorageHasher; + use support::rstd::marker::PhantomData; + use support::codec::{Encode, Decode, EncodeLike}; - #[test] - fn value() { - runtime_io::with_storage(&mut Default::default(), || { - assert!(Value::get().is_none()); - Value::put(&100_000); - assert_eq!(Value::get(), Some(100_000)); - Value::kill(); - assert!(Value::get().is_none()); - }) - } - - #[test] - fn map() { - runtime_io::with_storage(&mut Default::default(), || { - assert!(Map::get(&5).is_none()); - Map::insert(&5, &[1; 32]); - assert_eq!(Map::get(&5), Some([1; 32])); - assert_eq!(Map::take(&5), Some([1; 32])); - assert!(Map::get(&5).is_none()); - assert!(Map::get(&999).is_none()); - }) + support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} } pub trait Trait { @@ -291,11 +32,7 @@ mod tests { type BlockNumber; } - decl_module! { - pub struct Module for enum Call where origin: T::Origin {} - } - - crate::decl_storage! { + support::decl_storage! { trait Store for Module as TestStorage { // non-getters: pub / $default @@ -307,15 +44,15 @@ mod tests { // getters: pub / $default // we need at least one type which uses T, otherwise GenesisConfig will complain. - GETU32 get(u32_getter): T::Origin; - pub PUBGETU32 get(pub_u32_getter) build(|config: &GenesisConfig| config.u32_getter_with_config): u32; - GETU32WITHCONFIG get(u32_getter_with_config) config(): u32; - pub PUBGETU32WITHCONFIG get(pub_u32_getter_with_config) config(): u32; - GETU32MYDEF get(u32_getter_mydef): Option; - pub PUBGETU32MYDEF get(pub_u32_getter_mydef) config(): u32 = 3; - GETU32WITHCONFIGMYDEF get(u32_getter_with_config_mydef) config(): u32 = 2; - pub PUBGETU32WITHCONFIGMYDEF get(pub_u32_getter_with_config_mydef) config(): u32 = 1; - PUBGETU32WITHCONFIGMYDEFOPT get(pub_u32_getter_with_config_mydef_opt) config(): Option; + GETU32 get(fn u32_getter): T::Origin; + pub PUBGETU32 get(fn pub_u32_getter) build(|config: &GenesisConfig| config.u32_getter_with_config): u32; + GETU32WITHCONFIG get(fn u32_getter_with_config) config(): u32; + pub PUBGETU32WITHCONFIG get(fn pub_u32_getter_with_config) config(): u32; + GETU32MYDEF get(fn u32_getter_mydef): Option; + pub PUBGETU32MYDEF get(fn pub_u32_getter_mydef) config(): u32 = 3; + GETU32WITHCONFIGMYDEF get(fn u32_getter_with_config_mydef) config(): u32 = 2; + pub PUBGETU32WITHCONFIGMYDEF get(fn pub_u32_getter_with_config_mydef) config(): u32 = 1; + PUBGETU32WITHCONFIGMYDEFOPT get(fn pub_u32_getter_with_config_mydef_opt) config(): Option; // map non-getters: pub / $default MAPU32 : map u32 => Option; @@ -324,17 +61,17 @@ mod tests { pub PUBMAPU32MYDEF : map u32 => Option; // map getters: pub / $default - GETMAPU32 get(map_u32_getter): map u32 => String; - pub PUBGETMAPU32 get(pub_map_u32_getter): map u32 => String; + GETMAPU32 get(fn map_u32_getter): map u32 => String; + pub PUBGETMAPU32 get(fn pub_map_u32_getter): map u32 => String; - GETMAPU32MYDEF get(map_u32_getter_mydef): map u32 => String = "map".into(); - pub PUBGETMAPU32MYDEF get(pub_map_u32_getter_mydef): map u32 => String = "pubmap".into(); + 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(); // linked map LINKEDMAPU32 : linked_map u32 => Option; pub PUBLINKEDMAPU32MYDEF : linked_map u32 => Option; - GETLINKEDMAPU32 get(linked_map_u32_getter): linked_map u32 => String; - pub PUBGETLINKEDMAPU32MYDEF get(pub_linked_map_u32_getter_mydef): linked_map u32 => String = "pubmap".into(); + 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(); COMPLEXTYPE1: ::std::vec::Vec<::Origin>; COMPLEXTYPE2: (Vec)>>, u32); @@ -701,13 +438,13 @@ mod test2 { type BlockNumber; } - decl_module! { + support::decl_module! { pub struct Module for enum Call where origin: T::Origin {} } type PairOf = (T, T); - crate::decl_storage! { + support::decl_storage! { trait Store for Module as TestStorage { SingleDef : u32; PairDef : PairOf; @@ -736,12 +473,12 @@ mod test3 { type Origin; type BlockNumber; } - decl_module! { + support::decl_module! { pub struct Module for enum Call where origin: T::Origin {} } - crate::decl_storage! { + support::decl_storage! { trait Store for Module as Test { - Foo get(foo) config(initial_foo): u32; + Foo get(fn foo) config(initial_foo): u32; } } @@ -758,8 +495,7 @@ mod test3 { #[cfg(test)] #[allow(dead_code)] mod test_append_and_len { - use crate::storage::{StorageValue}; - use runtime_io::{with_externalities, TestExternalities}; + use runtime_io::TestExternalities; use codec::{Encode, Decode}; pub trait Trait { @@ -767,14 +503,14 @@ mod test_append_and_len { type BlockNumber; } - decl_module! { + support::decl_module! { pub struct Module for enum Call where origin: T::Origin {} } #[derive(PartialEq, Eq, Clone, Encode, Decode)] struct NoDef(u32); - crate::decl_storage! { + support::decl_storage! { trait Store for Module as Test { NoDefault: Option; @@ -805,7 +541,7 @@ mod test_append_and_len { #[test] fn default_for_option() { - with_externalities(&mut TestExternalities::default(), || { + TestExternalities::default().execute_with(|| { assert_eq!(OptionVec::get(), None); assert_eq!(JustVec::get(), vec![]); }); @@ -813,7 +549,7 @@ mod test_append_and_len { #[test] fn append_works() { - with_externalities(&mut TestExternalities::default(), || { + TestExternalities::default().execute_with(|| { let _ = MapVec::append(1, [1, 2, 3].iter()); let _ = MapVec::append(1, [4, 5].iter()); assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]); @@ -826,7 +562,7 @@ mod test_append_and_len { #[test] fn append_works_for_default() { - with_externalities(&mut TestExternalities::default(), || { + TestExternalities::default().execute_with(|| { assert_eq!(JustVecWithDefault::get(), vec![6, 9]); let _ = JustVecWithDefault::append([1].iter()); assert_eq!(JustVecWithDefault::get(), vec![6, 9, 1]); @@ -843,7 +579,7 @@ mod test_append_and_len { #[test] fn append_or_put_works() { - with_externalities(&mut TestExternalities::default(), || { + TestExternalities::default().execute_with(|| { let _ = MapVec::append_or_insert(1, &[1, 2, 3][..]); let _ = MapVec::append_or_insert(1, &[4, 5][..]); assert_eq!(MapVec::get(1), vec![1, 2, 3, 4, 5]); @@ -860,7 +596,7 @@ mod test_append_and_len { #[test] fn len_works() { - with_externalities(&mut TestExternalities::default(), || { + TestExternalities::default().execute_with(|| { JustVec::put(&vec![1, 2, 3, 4]); OptionVec::put(&vec![1, 2, 3, 4, 5]); MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]); @@ -877,7 +613,7 @@ mod test_append_and_len { #[test] fn len_works_for_default() { - with_externalities(&mut TestExternalities::default(), || { + TestExternalities::default().execute_with(|| { // vec assert_eq!(JustVec::get(), vec![]); assert_eq!(JustVec::decode_len(), Ok(0)); diff --git a/core/rpc/src/helpers.rs b/srml/support/test/tests/decl_storage_ui.rs similarity index 68% rename from core/rpc/src/helpers.rs rename to srml/support/test/tests/decl_storage_ui.rs index e579c743acdad202caedfd39e760559129c6109f..32c15eb390f8b80f860434cf13b1db744913d095 100644 --- a/core/rpc/src/helpers.rs +++ b/srml/support/test/tests/decl_storage_ui.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2019 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,12 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -/// Unwraps the trailing parameter or falls back with the closure result. -pub fn unwrap_or_else(or_else: F, optional: Option) -> Result where - F: FnOnce() -> Result, -{ - match optional.into() { - None => or_else(), - Some(x) => Ok(x), - } +#[test] +fn decl_storage_ui() { + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("BUILD_DUMMY_WASM_BINARY", "1"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/decl_storage_ui/*.rs"); } diff --git a/srml/support/test/tests/decl_storage_ui/config_duplicate.rs b/srml/support/test/tests/decl_storage_ui/config_duplicate.rs new file mode 100644 index 0000000000000000000000000000000000000000..bdd7da7449aa80686313aca3f7b4a25a1d2fad33 --- /dev/null +++ b/srml/support/test/tests/decl_storage_ui/config_duplicate.rs @@ -0,0 +1,33 @@ +// 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 . + +pub trait Trait { + type Origin; + type BlockNumber: codec::Codec + codec::EncodeLike + Default + Clone; +} + +support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +support::decl_storage!{ + trait Store for Module as FinalKeysNone { + pub Value config(value): u32; + pub Value2 config(value): u32; + } +} + +fn main() {} diff --git a/srml/support/test/tests/decl_storage_ui/config_duplicate.stderr b/srml/support/test/tests/decl_storage_ui/config_duplicate.stderr new file mode 100644 index 0000000000000000000000000000000000000000..761e3f3b7982492444854ec669094eb948f0c409 --- /dev/null +++ b/srml/support/test/tests/decl_storage_ui/config_duplicate.stderr @@ -0,0 +1,5 @@ +error: `config()`/`get()` with the same name already defined. + --> $DIR/config_duplicate.rs:29:21 + | +29 | pub Value2 config(value): u32; + | ^^^^^ diff --git a/srml/support/test/tests/decl_storage_ui/config_get_duplicate.rs b/srml/support/test/tests/decl_storage_ui/config_get_duplicate.rs new file mode 100644 index 0000000000000000000000000000000000000000..2bf8df45322ccd037091d305b522b65ecf9dd391 --- /dev/null +++ b/srml/support/test/tests/decl_storage_ui/config_get_duplicate.rs @@ -0,0 +1,33 @@ +// 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 . + +pub trait Trait { + type Origin; + type BlockNumber: codec::Codec + codec::EncodeLike + Default + Clone; +} + +support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +support::decl_storage!{ + trait Store for Module as FinalKeysNone { + pub Value get(fn value) config(): u32; + pub Value2 config(value): u32; + } +} + +fn main() {} diff --git a/srml/support/test/tests/decl_storage_ui/config_get_duplicate.stderr b/srml/support/test/tests/decl_storage_ui/config_get_duplicate.stderr new file mode 100644 index 0000000000000000000000000000000000000000..34d040f4e14f19a8fbfc1e904675ec6dacc5e963 --- /dev/null +++ b/srml/support/test/tests/decl_storage_ui/config_get_duplicate.stderr @@ -0,0 +1,5 @@ +error: `config()`/`get()` with the same name already defined. + --> $DIR/config_get_duplicate.rs:29:21 + | +29 | pub Value2 config(value): u32; + | ^^^^^ diff --git a/srml/support/test/tests/decl_storage_ui/get_duplicate.rs b/srml/support/test/tests/decl_storage_ui/get_duplicate.rs new file mode 100644 index 0000000000000000000000000000000000000000..41ca708352e8b76a8279d2bcc3655a25fcd1ff0f --- /dev/null +++ b/srml/support/test/tests/decl_storage_ui/get_duplicate.rs @@ -0,0 +1,33 @@ +// 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 . + +pub trait Trait { + type Origin; + type BlockNumber: codec::Codec + codec::EncodeLike + Default + Clone; +} + +support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +support::decl_storage!{ + trait Store for Module as FinalKeysNone { + pub Value get(fn value) config(): u32; + pub Value2 get(fn value) config(): u32; + } +} + +fn main() {} diff --git a/srml/support/test/tests/decl_storage_ui/get_duplicate.stderr b/srml/support/test/tests/decl_storage_ui/get_duplicate.stderr new file mode 100644 index 0000000000000000000000000000000000000000..130244196f67e03bb40de703fbb127d7bdecbfc8 --- /dev/null +++ b/srml/support/test/tests/decl_storage_ui/get_duplicate.stderr @@ -0,0 +1,5 @@ +error: `config()`/`get()` with the same name already defined. + --> $DIR/get_duplicate.rs:29:21 + | +29 | pub Value2 get(fn value) config(): u32; + | ^^^^^ diff --git a/srml/support/test/tests/final_keys.rs b/srml/support/test/tests/final_keys.rs index 04b243ae7ce0b7cb89f5f208f1301c4018b4a039..44a6b540a7a0e32896dab3aa267c7b2b15b7aeea 100644 --- a/srml/support/test/tests/final_keys.rs +++ b/srml/support/test/tests/final_keys.rs @@ -44,8 +44,8 @@ mod no_instance { pub DoubleMap: double_map u32, blake2_256(u32) => u32; pub DoubleMap2: double_map hasher(twox_128) u32, blake2_128(u32) => u32; - pub TestGenericValue get(test_generic_value) config(): Option; - pub TestGenericDoubleMap get(foo2) config(test_generic_double_map): + pub TestGenericValue get(fn test_generic_value) config(): Option; + pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map): double_map u32, blake2_256(T::BlockNumber) => Option; } } @@ -74,8 +74,8 @@ mod instance { pub DoubleMap: double_map u32, blake2_256(u32) => u32; pub DoubleMap2: double_map hasher(twox_128) u32, blake2_128(u32) => u32; - pub TestGenericValue get(test_generic_value) config(): Option; - pub TestGenericDoubleMap get(foo2) config(test_generic_double_map): + pub TestGenericValue get(fn test_generic_value) config(): Option; + pub TestGenericDoubleMap get(fn foo2) config(test_generic_double_map): double_map u32, blake2_256(T::BlockNumber) => Option; } add_extra_genesis { diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index da4e73e71017a1bdacf30abafadfa216c7c41788..282fb9a6e299f149aedcb61bf2c83c10310020c0 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -13,22 +13,22 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . + #![recursion_limit="128"] -use runtime_io::with_externalities; +use sr_primitives::{generic, BuildStorage, traits::{BlakeTwo256, Block as _, Verify}}; use support::{ Parameter, traits::Get, parameter_types, - sr_primitives::{generic, BuildStorage, traits::{BlakeTwo256, Block as _, Verify}}, metadata::{ DecodeDifferent, StorageMetadata, StorageEntryModifier, StorageEntryType, DefaultByteGetter, - StorageEntryMetadata, StorageHasher + StorageEntryMetadata, StorageHasher, }, StorageValue, StorageMap, StorageLinkedMap, StorageDoubleMap, }; use inherents::{ ProvideInherent, InherentData, InherentIdentifier, RuntimeString, MakeFatalError }; -use primitives::{H256, sr25519, Blake2Hasher}; +use primitives::{H256, sr25519}; mod system; @@ -88,8 +88,7 @@ mod module1 { } } - #[derive(PartialEq, Eq, Clone)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)] pub enum Origin, I> where T::BlockNumber: From { Members(u32), _Phantom(std::marker::PhantomData<(T, I)>), @@ -151,8 +150,7 @@ mod module2 { } } - #[derive(PartialEq, Eq, Clone)] - #[cfg_attr(feature = "std", derive(Debug))] + #[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)] pub enum Origin, I=DefaultInstance> { Members(u32), _Phantom(std::marker::PhantomData<(T, I)>), @@ -275,7 +273,7 @@ pub type Header = generic::Header; pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -fn new_test_ext() -> runtime_io::TestExternalities { +fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig{ module1_Instance1: Some(module1::GenesisConfig { value: 3, @@ -329,7 +327,7 @@ fn storage_instance_independance() { #[test] fn storage_with_instance_basic_operation() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { type Value = module2::Value; type Map = module2::Map; type LinkedMap = module2::LinkedMap; diff --git a/srml/support/test/tests/issue2219.rs b/srml/support/test/tests/issue2219.rs index 28bd9463ffa9cfd41ac5061cdb7c27bf70ecbf5f..72e0bc8caf198f5a4519b09dfc9fc02bb4d55ff6 100644 --- a/srml/support/test/tests/issue2219.rs +++ b/srml/support/test/tests/issue2219.rs @@ -102,7 +102,7 @@ mod module { support::decl_storage! { trait Store for Module as Actors { /// requirements to enter and maintain status in roles - pub Parameters get(parameters) build(|config: &GenesisConfig| { + pub Parameters get(fn parameters) build(|config: &GenesisConfig| { if config.enable_storage_role { let storage_params: RoleParameters = Default::default(); vec![(Role::Storage, storage_params)] @@ -112,7 +112,7 @@ mod module { }): map Role => Option>; /// the roles members can enter into - pub AvailableRoles get(available_roles) build(|config: &GenesisConfig| { + pub AvailableRoles get(fn available_roles) build(|config: &GenesisConfig| { if config.enable_storage_role { vec![(Role::Storage)] } else { @@ -121,13 +121,13 @@ mod module { }): Vec; /// Actors list - pub ActorAccountIds get(actor_account_ids) : Vec; + pub ActorAccountIds get(fn actor_account_ids) : Vec; /// actor accounts associated with a role - pub AccountIdsByRole get(account_ids_by_role) : map Role => Vec; + pub AccountIdsByRole get(fn account_ids_by_role) : map Role => Vec; /// tokens locked until given block number - pub Bondage get(bondage) : map T::AccountId => T::BlockNumber; + pub Bondage get(fn bondage) : map 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. @@ -135,10 +135,10 @@ mod module { /// The account making the request will be bonded and must have /// sufficient balance to cover the minimum stake for the role. /// Bonding only occurs after successful entry into a role. - pub RoleEntryRequests get(role_entry_requests) : Requests; + pub RoleEntryRequests get(fn role_entry_requests) : Requests; /// Entry request expires after this number of blocks - pub RequestLifeTime get(request_life_time) config(request_life_time) : u64 = 0; + pub RequestLifeTime get(fn request_life_time) config(request_life_time) : u64 = 0; } add_extra_genesis { config(enable_storage_role): bool; diff --git a/srml/support/test/tests/reserved_keyword.rs b/srml/support/test/tests/reserved_keyword.rs new file mode 100644 index 0000000000000000000000000000000000000000..898d61b8279e91cc4e569df289de683e51cf111a --- /dev/null +++ b/srml/support/test/tests/reserved_keyword.rs @@ -0,0 +1,24 @@ +// 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 . + +#[test] +fn reserved_keyword() { + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("BUILD_DUMMY_WASM_BINARY", "1"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/reserved_keyword/*.rs"); +} diff --git a/srml/support/test/tests/system.rs b/srml/support/test/tests/system.rs index 1040b26cc61268cc9c20b91a794dec14c93bd1ff..2996724f625988feab38795dee5d1c046b98b17a 100644 --- a/srml/support/test/tests/system.rs +++ b/srml/support/test/tests/system.rs @@ -29,14 +29,16 @@ support::decl_event!( support::decl_error! { pub enum Error { + /// Test error documentation TestError, + /// Error documentation + /// with multiple lines AnotherError } } /// Origin for the system module. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, sr_primitives::RuntimeDebug)] pub enum RawOrigin { Root, Signed(AccountId), diff --git a/srml/system/Cargo.toml b/srml/system/Cargo.toml index a9ce6b3f8c830a7dc7313bad0e6c4fd2342cf78b..52570617a1b974a3e3a09bb84c8f05ffdd5776ed 100644 --- a/srml/system/Cargo.toml +++ b/srml/system/Cargo.toml @@ -14,7 +14,7 @@ runtime-io ={ package = "sr-io", path = "../../core/sr-io", default-features = f sr-primitives = { path = "../../core/sr-primitives", default-features = false } sr-version = { path = "../../core/sr-version", default-features = false } support = { package = "srml-support", path = "../support", default-features = false } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [dev-dependencies] criterion = "0.2.11" diff --git a/srml/system/benches/bench.rs b/srml/system/benches/bench.rs index b24e52f45720bbd7c43d01dc50e12653cf2a3191..6da02cf9c8cea0edb4d48a9d7534e2e4ed797ada 100644 --- a/srml/system/benches/bench.rs +++ b/srml/system/benches/bench.rs @@ -17,8 +17,7 @@ use criterion::{Criterion, criterion_group, criterion_main, black_box}; use srml_system as system; use support::{decl_module, decl_event, impl_outer_origin, impl_outer_event}; -use runtime_io::with_externalities; -use primitives::{H256, Blake2Hasher}; +use primitives::H256; use sr_primitives::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; mod module { @@ -69,7 +68,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -82,13 +80,13 @@ impl module::Trait for Runtime { type Event = Event; } -fn new_test_ext() -> runtime_io::TestExternalities { +fn new_test_ext() -> runtime_io::TestExternalities { system::GenesisConfig::default().build_storage::().unwrap().into() } fn deposit_events(n: usize) { let mut t = new_test_ext(); - with_externalities(&mut t, || { + t.execute_with(|| { for _ in 0..n { module::Module::::deposit_event( module::Event::Complex(vec![1, 2, 3], 2, 3, 899) diff --git a/srml/system/rpc/Cargo.toml b/srml/system/rpc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..04856a817f6814377ab99c975dcc2105357391f3 --- /dev/null +++ b/srml/system/rpc/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "srml-system-rpc" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +client = { package = "substrate-client", path = "../../../core/client" } +codec = { package = "parity-scale-codec", version = "1.0.0" } +jsonrpc-core = "13.2.0" +jsonrpc-core-client = "13.2.0" +jsonrpc-derive = "13.2.0" +log = "0.4.8" +serde = { version = "1.0.101", features = ["derive"] } +sr-primitives = { path = "../../../core/sr-primitives" } +srml-system-rpc-runtime-api = { path = "./runtime-api" } +substrate-primitives = { path = "../../../core/primitives" } +transaction_pool = { package = "substrate-transaction-pool", path = "../../../core/transaction-pool" } + +[dev-dependencies] +test-client = { package = "substrate-test-runtime-client", path = "../../../core/test-runtime/client" } +env_logger = "0.7.0" +futures03 = { package = "futures-preview", version = "=0.3.0-alpha.19" } diff --git a/srml/system/rpc/runtime-api/Cargo.toml b/srml/system/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..fc525d8fce2adb9fd1d8cd4adc582461613e322a --- /dev/null +++ b/srml/system/rpc/runtime-api/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "srml-system-rpc-runtime-api" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +client = { package = "substrate-client", path = "../../../../core/client", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } + +[features] +default = ["std"] +std = [ + "client/std", + "codec/std", +] diff --git a/srml/system/rpc/runtime-api/src/lib.rs b/srml/system/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..45af0241e08365f44bfa77e1893d9a3a477e077d --- /dev/null +++ b/srml/system/rpc/runtime-api/src/lib.rs @@ -0,0 +1,34 @@ +// 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 . + +//! Runtime API definition required by System RPC extensions. +//! +//! This API should be imported and implemented by the runtime, +//! of a node that wants to use the custom RPC extension +//! adding System access methods. + +#![cfg_attr(not(feature = "std"), no_std)] + +client::decl_runtime_apis! { + /// The API to query account nonce (aka transaction index). + pub trait AccountNonceApi where + AccountId: codec::Codec, + Index: codec::Codec, + { + /// Get current account nonce of given `AccountId`. + fn account_nonce(account: AccountId) -> Index; + } +} diff --git a/node/rpc/src/accounts.rs b/srml/system/rpc/src/lib.rs similarity index 64% rename from node/rpc/src/accounts.rs rename to srml/system/rpc/src/lib.rs index 6c8e60736ac5326c5eabababf7765b7edfd3d4b7..c6c0a658fba535bb59bc3aa20de3d382c3d6f3fb 100644 --- a/node/rpc/src/accounts.rs +++ b/srml/system/rpc/src/lib.rs @@ -14,58 +14,66 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Node-specific RPC methods for Accounts. +//! System SRML specific RPC methods. use std::sync::Arc; +use codec::{self, Codec, Encode}; use client::blockchain::HeaderBackend; use jsonrpc_core::{Result, Error, ErrorCode}; use jsonrpc_derive::rpc; -use node_primitives::{ - AccountId, Index, AccountNonceApi, Block, BlockId, +use sr_primitives::{ + generic::BlockId, + traits, }; -use codec::Encode; -use sr_primitives::traits; use substrate_primitives::hexdisplay::HexDisplay; use transaction_pool::txpool::{self, Pool}; -pub use self::gen_client::Client as AccountsClient; +pub use srml_system_rpc_runtime_api::AccountNonceApi; +pub use self::gen_client::Client as SystemClient; -/// Accounts RPC methods. +/// System RPC methods. #[rpc] -pub trait AccountsApi { +pub trait SystemApi { /// Returns the next valid index (aka nonce) for given account. /// /// This method takes into consideration all pending transactions /// currently in the pool and if no transactions are found in the pool /// it fallbacks to query the index from the runtime (aka. state nonce). - #[rpc(name = "account_nextIndex")] + #[rpc(name = "system_accountNextIndex", alias("account_nextIndex"))] fn nonce(&self, account: AccountId) -> Result; } -/// An implementation of Accounts specific RPC methods. -pub struct Accounts { +const RUNTIME_ERROR: i64 = 1; + +/// An implementation of System-specific RPC methods. +pub struct System { client: Arc, pool: Arc>, + _marker: std::marker::PhantomData, } -impl Accounts { - /// Create new `Accounts` given client and transaction pool. +impl System { + /// Create new `System` given client and transaction pool. pub fn new(client: Arc, pool: Arc>) -> Self { - Accounts { + System { client, - pool + pool, + _marker: Default::default(), } } } -impl AccountsApi for Accounts +impl SystemApi for System where C: traits::ProvideRuntimeApi, C: HeaderBackend, C: Send + Sync + 'static, - C::Api: AccountNonceApi, + C::Api: AccountNonceApi, P: txpool::ChainApi + Sync + Send + 'static, + Block: traits::Block, + AccountId: Clone + std::fmt::Display + Codec, + Index: Clone + std::fmt::Display + Codec + traits::SimpleArithmetic, { fn nonce(&self, account: AccountId) -> Result { let api = self.client.runtime_api(); @@ -73,7 +81,7 @@ where let at = BlockId::hash(best); let nonce = api.account_nonce(&at, account.clone()).map_err(|e| Error { - code: ErrorCode::ServerError(crate::constants::RUNTIME_ERROR), + code: ErrorCode::ServerError(RUNTIME_ERROR), message: "Unable to query nonce.".into(), data: Some(format!("{:?}", e).into()), })?; @@ -85,12 +93,12 @@ where // Since extrinsics are opaque to us, we look for them using // `provides` tag. And increment the nonce if we find a transaction // that matches the current one. - let mut current_nonce = nonce; - let mut current_tag = (account.clone(), nonce).encode(); + let mut current_nonce = nonce.clone(); + let mut current_tag = (account.clone(), nonce.clone()).encode(); for tx in self.pool.ready() { log::debug!( target: "rpc", - "Current nonce to {:?}, checking {} vs {:?}", + "Current nonce to {}, checking {} vs {:?}", current_nonce, HexDisplay::from(¤t_tag), tx.provides.iter().map(|x| format!("{}", HexDisplay::from(x))).collect::>(), @@ -98,8 +106,8 @@ where // since transactions in `ready()` need to be ordered by nonce // it's fine to continue with current iterator. if tx.provides.get(0) == Some(¤t_tag) { - current_nonce += 1; - current_tag = (account.clone(), current_nonce).encode(); + current_nonce += traits::One::one(); + current_tag = (account.clone(), current_nonce.clone()).encode(); } } @@ -112,42 +120,37 @@ mod tests { use super::*; use futures03::executor::block_on; - use node_runtime::{CheckedExtrinsic, Call, TimestampCall}; - use codec::Decode; - use node_testing::{ - client::{ClientExt, TestClientBuilder, TestClientBuilderExt}, - keyring::{self, alice, signed_extra}, + use test_client::{ + runtime::Transfer, + AccountKeyring, }; - const VERSION: u32 = node_runtime::VERSION.spec_version; - #[test] fn should_return_next_nonce_for_some_account() { // given let _ = env_logger::try_init(); - let client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(test_client::new()); let pool = Arc::new(Pool::new(Default::default(), transaction_pool::FullChainApi::new(client.clone()))); - let new_transaction = |extra| { - let ex = CheckedExtrinsic { - signed: Some((alice().into(), extra)), - function: Call::Timestamp(TimestampCall::set(5)), + let new_transaction = |nonce: u64| { + let t = Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Bob.into(), + amount: 5, + nonce, }; - let xt = keyring::sign(ex, VERSION, client.genesis_hash().into()); - // Convert to OpaqueExtrinsic - let encoded = xt.encode(); - node_primitives::UncheckedExtrinsic::decode(&mut &*encoded).unwrap() + t.into_signed_tx() }; // Populate the pool - let ext0 = new_transaction(signed_extra(0, 0)); + let ext0 = new_transaction(0); block_on(pool.submit_one(&BlockId::number(0), ext0)).unwrap(); - let ext1 = new_transaction(signed_extra(1, 0)); + let ext1 = new_transaction(1); block_on(pool.submit_one(&BlockId::number(0), ext1)).unwrap(); - let accounts = Accounts::new(client, pool); + let accounts = System::new(client, pool); // when - let nonce = accounts.nonce(alice().into()); + let nonce = accounts.nonce(AccountKeyring::Alice.into()); // then assert_eq!(nonce.unwrap(), 2); diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 5a1115b90d71ce04a040fef378612f20fcc9bcb2..e07b9377512677d1f0bf59755e2c114ec63fd08a 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -48,7 +48,7 @@ //! //! - [`CheckWeight`]: Checks the weight and length of the block and ensure that it does not //! exceed the limits. -//! - ['CheckNonce']: Checks the nonce of the transaction. Contains a single payload of type +//! - [`CheckNonce`]: Checks the nonce of the transaction. Contains a single payload of type //! `T::Index`. //! - [`CheckEra`]: Checks the era of the transaction. Contains a single payload of type `Era`. //! - [`CheckGenesis`]: Checks the provided genesis hash of the transaction. Must be a part of the @@ -65,7 +65,7 @@ //! //! Import the System module and derive your module's configuration trait from the system trait. //! -//! ### Example - Get random seed and extrinsic count for the current block +//! ### Example - Get extrinsic count and parent hash for the current block //! //! ``` //! use support::{decl_module, dispatch::Result}; @@ -77,8 +77,8 @@ //! pub struct Module for enum Call where origin: T::Origin { //! pub fn system_module_example(origin) -> Result { //! let _sender = ensure_signed(origin)?; -//! let _random_seed = >::random_seed(); //! let _extrinsic_count = >::extrinsic_count(); +//! let _parent_hash = >::parent_hash(); //! Ok(()) //! } //! } @@ -94,18 +94,20 @@ use rstd::prelude::*; #[cfg(any(feature = "std", test))] use rstd::map; use rstd::marker::PhantomData; +use rstd::fmt::Debug; use sr_version::RuntimeVersion; use sr_primitives::{ + RuntimeDebug, generic::{self, Era}, Perbill, ApplyError, ApplyOutcome, DispatchError, - weights::{Weight, DispatchInfo, DispatchClass, WeightMultiplier, SimpleDispatchInfo}, + weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo}, transaction_validity::{ ValidTransaction, TransactionPriority, TransactionLongevity, TransactionValidityError, InvalidTransaction, TransactionValidity, }, traits::{ - self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Convert, Lookup, LookupError, + self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Lookup, LookupError, SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, SaturatedConversion, - MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, + MaybeSerialize, MaybeSerializeDeserialize, StaticLookup, One, Bounded, }, }; @@ -114,14 +116,13 @@ use support::{ decl_module, decl_event, decl_storage, decl_error, storage, Parameter, traits::{Contains, Get}, }; -use safe_mix::TripletMix; use codec::{Encode, Decode}; #[cfg(any(feature = "std", test))] use runtime_io::TestExternalities; #[cfg(any(feature = "std", test))] -use primitives::{ChangesTrieConfiguration, Blake2Hasher}; +use primitives::ChangesTrieConfiguration; pub mod offchain; @@ -156,47 +157,43 @@ pub fn extrinsics_data_root(xts: Vec>) -> H::Output { pub trait Trait: 'static + Eq + Clone { /// The aggregated `Origin` type used by dispatchable calls. - type Origin: Into, Self::Origin>> + From>; + type Origin: + Into, Self::Origin>> + From>; /// The aggregated `Call` type. - type Call; + type Call: Debug; - /// Account index (aka nonce) type. This stores the number of previous transactions associated with a sender - /// account. + /// Account index (aka nonce) type. This stores the number of previous transactions associated + /// with a sender account. type Index: - Parameter + Member + MaybeSerializeDebugButNotDeserialize + Default + MaybeDisplay + SimpleArithmetic + Copy; + Parameter + Member + MaybeSerialize + Debug + Default + MaybeDisplay + SimpleArithmetic + + Copy; /// The block number type used by the runtime. type BlockNumber: - Parameter + Member + MaybeSerializeDebug + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy - + rstd::hash::Hash; + Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + SimpleArithmetic + + Default + Bounded + Copy + rstd::hash::Hash; /// The output of the `Hashing` function. type Hash: - Parameter + Member + MaybeSerializeDebug + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual - + rstd::hash::Hash + AsRef<[u8]> + AsMut<[u8]>; + Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + SimpleBitOps + + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]> + AsMut<[u8]>; /// The hashing system (algorithm) being used in the runtime (e.g. Blake2). type Hashing: Hash; /// The user account identifier type for the runtime. - type AccountId: Parameter + Member + MaybeSerializeDebug + MaybeDisplay + Ord + Default; + type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord + + Default; /// Converting trait to take a source type and convert to `AccountId`. /// - /// Used to define the type and conversion mechanism for referencing accounts in transactions. It's perfectly - /// reasonable for this to be an identity conversion (with the source type being `AccountId`), but other modules - /// (e.g. Indices module) may provide more functional/efficient alternatives. + /// Used to define the type and conversion mechanism for referencing accounts in transactions. + /// It's perfectly reasonable for this to be an identity conversion (with the source type being + /// `AccountId`), but other modules (e.g. Indices module) may provide more functional/efficient + /// alternatives. type Lookup: StaticLookup; - /// Handler for updating the weight multiplier at the end of each block. - /// - /// It receives the current block's weight as input and returns the next weight multiplier for next - /// block. - /// - /// Note that passing `()` will keep the value constant. - type WeightMultiplierUpdate: Convert<(Weight, WeightMultiplier), WeightMultiplier>; - /// The block header. type Header: Parameter + traits::Header< Number = Self::BlockNumber, @@ -204,7 +201,7 @@ pub trait Trait: 'static + Eq + Clone { >; /// The aggregated event type of the runtime. - type Event: Parameter + Member + From; + type Event: Parameter + Member + From + Debug; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount: Get; @@ -289,8 +286,8 @@ decl_module! { } /// A phase of a block's execution. -#[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] +#[derive(Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone))] pub enum Phase { /// Applying an extrinsic. ApplyExtrinsic(u32), @@ -299,8 +296,8 @@ pub enum Phase { } /// Record of an event happening. -#[derive(Encode, Decode)] -#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] +#[derive(Encode, Decode, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone))] pub struct EventRecord { /// The phase of the block it happened in. pub phase: Phase, @@ -332,8 +329,7 @@ decl_error! { } /// Origin for the System module. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub enum RawOrigin { /// The system itself ordained this dispatch to happen: this is the highest privilege level. Root, @@ -375,35 +371,29 @@ type EventIndex = u32; decl_storage! { trait Store for Module as System { /// Extrinsics nonce for accounts. - pub AccountNonce get(account_nonce): map T::AccountId => T::Index; + pub AccountNonce get(fn account_nonce): map T::AccountId => T::Index; /// 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; - /// The next weight multiplier. This should be updated at the end of each block based on the - /// saturation level (weight). - pub NextWeightMultiplier get(next_weight_multiplier): WeightMultiplier = Default::default(); /// Map of block numbers to block hashes. - pub BlockHash get(block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map T::BlockNumber => T::Hash; + pub BlockHash get(fn block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map T::BlockNumber => T::Hash; /// Extrinsics data for the current block (maps an extrinsic's index to its data). - ExtrinsicData get(extrinsic_data): map u32 => Vec; - /// Series of block headers from the last 81 blocks that acts as random seed material. This is arranged as a - /// ring buffer with the `i8` prefix being the index into the `Vec` of the oldest hash. - RandomMaterial get(random_material): (i8, Vec); + ExtrinsicData get(fn extrinsic_data): map u32 => Vec; /// The current block number being processed. Set by `execute_block`. - Number get(block_number) build(|_| 1.into()): T::BlockNumber; + Number get(fn block_number) build(|_| 1.into()): T::BlockNumber; /// Hash of the previous block. - ParentHash get(parent_hash) build(|_| hash69()): T::Hash; + ParentHash get(fn parent_hash) build(|_| hash69()): T::Hash; /// Extrinsics root of the current block, also part of the block header. - ExtrinsicsRoot get(extrinsics_root): T::Hash; + ExtrinsicsRoot get(fn extrinsics_root): T::Hash; /// Digest of the current block, also part of the block header. - Digest get(digest): DigestOf; + Digest get(fn digest): DigestOf; /// Events deposited for the current block. - Events get(events): Vec>; + Events get(fn events): Vec>; /// The number of events in the `Events` list. - EventCount get(event_count): EventIndex; + EventCount get(fn event_count): EventIndex; // TODO: https://github.com/paritytech/substrate/issues/2553 // Possibly, we can improve it by using something like: @@ -423,7 +413,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(event_topics): double_map hasher(blake2_256) (), blake2_256(T::Hash) + EventTopics get(fn event_topics): double_map hasher(blake2_256) (), blake2_256(T::Hash) => Vec<(T::BlockNumber, EventIndex)>; } add_extra_genesis { @@ -582,7 +572,7 @@ impl Module { // We perform early return if we've reached the maximum capacity of the event list, // so `Events` seems to be corrupted. Also, this has happened after the start of execution // (since the event list is cleared at the block initialization). - if >::append([event].into_iter()).is_err() { + if >::append([event].iter()).is_err() { // The most sensible thing to do here is to just ignore this event and wait until the // new block. return; @@ -616,17 +606,6 @@ impl Module { AllExtrinsicsLen::get().unwrap_or_default() } - /// Update the next weight multiplier. - /// - /// This should be called at then end of each block, before `all_extrinsics_weight` is cleared. - pub fn update_weight_multiplier() { - // update the multiplier based on block weight. - let current_weight = Self::all_extrinsics_weight(); - NextWeightMultiplier::mutate(|fm| { - *fm = T::WeightMultiplierUpdate::convert((current_weight, *fm)) - }); - } - /// Start the execution of a particular block. pub fn initialize( number: &T::BlockNumber, @@ -641,12 +620,6 @@ impl Module { >::put(parent_hash); >::insert(*number - One::one(), parent_hash); >::put(txs_root); - >::mutate(|&mut(ref mut index, ref mut values)| if values.len() < 81 { - values.push(parent_hash.clone()) - } else { - values[*index as usize] = parent_hash.clone(); - *index = (*index + 1) % 81; - }); >::kill(); EventCount::kill(); >::remove_prefix(&()); @@ -655,7 +628,6 @@ impl Module { /// Remove temporary "environment" entries in storage. pub fn finalize() -> T::Header { ExtrinsicCount::kill(); - Self::update_weight_multiplier(); AllExtrinsicsWeight::kill(); AllExtrinsicsLen::kill(); @@ -705,7 +677,7 @@ impl Module { /// Get the basic externalities for this module, useful for tests. #[cfg(any(feature = "std", test))] - pub fn externalities() -> TestExternalities { + pub fn externalities() -> TestExternalities { TestExternalities::new((map![ >::hashed_key_for(T::BlockNumber::zero()) => [69u8; 32].encode(), >::hashed_key().to_vec() => T::BlockNumber::one().encode(), @@ -733,71 +705,15 @@ impl Module { >::put(n); } - /// Return the chain's current runtime version. - pub fn runtime_version() -> RuntimeVersion { T::Version::get() } - - /// Get the basic random seed. - /// - /// In general you won't want to use this, but rather `Self::random` which - /// allows you to give a subject for the random result and whose value will - /// be independently low-influence random from any other such seeds. - pub fn random_seed() -> T::Hash { - Self::random(&[][..]) + /// Set the current block weight. This should only be used in some integration tests. + #[cfg(any(feature = "std", test))] + pub fn set_block_limits(weight: Weight, len: usize) { + AllExtrinsicsWeight::put(weight); + AllExtrinsicsLen::put(len as u32); } - /// Get a low-influence "random" value. - /// - /// Being a deterministic block chain, 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 different result to other - /// callers of this function; use it like `random(&b"my context"[..])`. - /// - /// This is initially implemented through a low-influence "triplet mix" - /// convolution of previous block hash values. In the future it will be - /// generated from a secure verifiable random function (VRF). - /// - /// ### Security Notes - /// - /// This randomness uses a low-influence function, drawing upon the block - /// hashes from the previous 81 blocks. Its result for any given subject - /// will be known in advance by the block producer of this block (and, - /// indeed, anyone who knows the block's `parent_hash`). However, it is - /// mostly impossible for the producer of this block *alone* to influence - /// the value of this hash. A sizable minority of dishonest and coordinating - /// block producers would be required in order to affect this value. If that - /// is an insufficient security guarantee then two things can be used to - /// improve this randomness: - /// - /// - Name, in advance, the block number whose random value will be used; - /// ensure your module retains a buffer of previous random values for its - /// subject and then index into these in order to obviate the ability of - /// your user to look up the parent hash and choose when to transact based - /// upon it. - /// - Require your user to first commit to an additional value by first - /// posting its hash. Require them to reveal the value to determine the - /// final result, hashing it with the output of this random function. This - /// reduces the ability of a cabal of block producers from conspiring - /// against individuals. - /// - /// WARNING: Hashing the result of this function will remove any - /// low-influnce properties it has and mean that all bits of the resulting - /// value are entirely manipulatable by the author of the parent block, who - /// can determine the value of `parent_hash`. - pub fn random(subject: &[u8]) -> T::Hash { - let (index, hash_series) = >::get(); - if hash_series.len() > 0 { - // Always the case after block 1 is initialised. - hash_series.iter() - .cycle() - .skip(index as usize) - .take(81) - .enumerate() - .map(|(i, h)| (i as i8, subject, h).using_encoded(T::Hashing::hash)) - .triplet_mix() - } else { - T::Hash::default() - } - } + /// Return the chain's current runtime version. + pub fn runtime_version() -> RuntimeVersion { T::Version::get() } /// Increment a particular account's nonce by 1. pub fn inc_account_nonce(who: &T::AccountId) { @@ -856,7 +772,6 @@ impl CheckWeight { fn get_dispatch_limit_ratio(class: DispatchClass) -> Perbill { match class { DispatchClass::Operational => Perbill::one(), - // TODO: this must be some sort of a constant. DispatchClass::Normal => T::AvailableBlockRatio::get(), } } @@ -951,10 +866,15 @@ impl SignedExtension for CheckWeight { } } -#[cfg(feature = "std")] -impl rstd::fmt::Debug for CheckWeight { +impl Debug for CheckWeight { + #[cfg(feature = "std")] fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { - write!(f, "CheckWeight") + write!(f, "CheckWeight") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) } } @@ -969,11 +889,16 @@ impl CheckNonce { } } -#[cfg(feature = "std")] -impl rstd::fmt::Debug for CheckNonce { +impl Debug for CheckNonce { + #[cfg(feature = "std")] fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { self.0.fmt(f) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } impl SignedExtension for CheckNonce { @@ -1047,11 +972,16 @@ impl CheckEra { } } -#[cfg(feature = "std")] -impl rstd::fmt::Debug for CheckEra { +impl Debug for CheckEra { + #[cfg(feature = "std")] fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { self.0.fmt(f) } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } } impl SignedExtension for CheckEra { @@ -1090,9 +1020,14 @@ impl SignedExtension for CheckEra { #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckGenesis(rstd::marker::PhantomData); -#[cfg(feature = "std")] -impl rstd::fmt::Debug for CheckGenesis { - fn fmt(&self, _f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { +impl Debug for CheckGenesis { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "CheckGenesis") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { Ok(()) } } @@ -1119,9 +1054,14 @@ impl SignedExtension for CheckGenesis { #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckVersion(rstd::marker::PhantomData); -#[cfg(feature = "std")] -impl rstd::fmt::Debug for CheckVersion { - fn fmt(&self, _f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { +impl Debug for CheckVersion { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "CheckVersion") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { Ok(()) } } @@ -1163,7 +1103,6 @@ impl Lookup for ChainContext { #[cfg(test)] mod tests { use super::*; - use runtime_io::with_externalities; use primitives::H256; use sr_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header, DispatchError}; use support::{impl_outer_origin, parameter_types}; @@ -1192,7 +1131,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = u16; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -1214,7 +1152,7 @@ mod tests { const CALL: &::Call = &(); - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig::default().build_storage::().unwrap().into() } @@ -1235,7 +1173,7 @@ mod tests { #[test] fn deposit_event_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { System::initialize(&1, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); System::note_finished_extrinsics(); System::deposit_event(1u16); @@ -1272,10 +1210,15 @@ mod tests { #[test] fn deposit_event_topics() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { const BLOCK_NUMBER: u64 = 1; - System::initialize(&BLOCK_NUMBER, &[0u8; 32].into(), &[0u8; 32].into(), &Default::default()); + System::initialize( + &BLOCK_NUMBER, + &[0u8; 32].into(), + &[0u8; 32].into(), + &Default::default(), + ); System::note_finished_extrinsics(); let topics = vec![ @@ -1332,7 +1275,7 @@ mod tests { #[test] fn prunes_block_hash_mappings() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // simulate import of 15 blocks for n in 1..=15 { System::initialize( @@ -1365,7 +1308,7 @@ mod tests { #[test] fn signed_ext_check_nonce_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { >::insert(1, 1); let info = DispatchInfo::default(); let len = 0_usize; @@ -1383,7 +1326,7 @@ mod tests { #[test] fn signed_ext_check_weight_works_normal_tx() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let normal_limit = normal_weight_limit(); let small = DispatchInfo { weight: 100, ..Default::default() }; let medium = DispatchInfo { @@ -1410,7 +1353,7 @@ mod tests { #[test] fn signed_ext_check_weight_fee_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let free = DispatchInfo { weight: 0, ..Default::default() }; let len = 0_usize; @@ -1423,7 +1366,7 @@ mod tests { #[test] fn signed_ext_check_weight_max_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let max = DispatchInfo { weight: Weight::max_value(), ..Default::default() }; let len = 0_usize; let normal_limit = normal_weight_limit(); @@ -1437,7 +1380,7 @@ mod tests { #[test] fn signed_ext_check_weight_works_operational_tx() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let normal = DispatchInfo { weight: 100, ..Default::default() }; let op = DispatchInfo { weight: 100, class: DispatchClass::Operational }; let len = 0_usize; @@ -1460,7 +1403,7 @@ mod tests { #[test] fn signed_ext_check_weight_priority_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal }; let op = DispatchInfo { weight: 100, class: DispatchClass::Operational }; let len = 0_usize; @@ -1481,7 +1424,7 @@ mod tests { #[test] fn signed_ext_check_weight_block_size_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let normal = DispatchInfo::default(); let normal_limit = normal_weight_limit() as usize; let reset_check_weight = |tx, s, f| { @@ -1505,7 +1448,7 @@ mod tests { #[test] fn signed_ext_check_era_should_work() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // future assert_eq!( CheckEra::::from(Era::mortal(4, 2)).additional_signed().err().unwrap(), @@ -1521,7 +1464,7 @@ mod tests { #[test] fn signed_ext_check_era_should_change_longevity() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal }; let len = 0_usize; let ext = ( diff --git a/srml/system/src/offchain.rs b/srml/system/src/offchain.rs index e234c74c089442843cacedc7b7093ff8bc396c4f..11f7e234f71977707913b7930a95913aeb6a7522 100644 --- a/srml/system/src/offchain.rs +++ b/srml/system/src/offchain.rs @@ -17,30 +17,50 @@ //! Module helpers for offchain calls. use codec::Encode; -use sr_primitives::app_crypto::RuntimeAppPublic; -use sr_primitives::traits::Extrinsic as ExtrinsicT; +use sr_primitives::app_crypto::{self, RuntimeAppPublic}; +use sr_primitives::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; /// A trait responsible for signing a payload using given account. -pub trait Signer { +pub trait Signer { /// Sign any encodable payload with given account and produce a signature. /// /// Returns `Some` if signing succeeded and `None` in case the `account` couldn't be used. - fn sign(account: Account, payload: &Payload) -> Option; + fn sign(public: Public, payload: &Payload) -> Option; } -impl Signer for AppPublic where - AppPublic: RuntimeAppPublic + From, - AppPublic::Signature: Into, +/// A `Signer` implementation for any `AppPublic` type. +/// +/// This implementation additionaly 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 AppPublic where + AppPublic: RuntimeAppPublic + + app_crypto::AppPublic + + From<::Generic>, + ::Signature: app_crypto::AppSignature, + Signature: From< + <::Signature as app_crypto::AppSignature>::Generic + >, + Public: rstd::convert::TryInto<::Generic> { - fn sign(account: Account, raw_payload: &Payload) -> Option { + fn sign(public: Public, raw_payload: &Payload) -> Option { raw_payload.using_encoded(|payload| { - AppPublic::from(account).sign(&payload).map(Into::into) + let public = public.try_into().ok()?; + AppPublic::from(public).sign(&payload) + .map( + <::Signature as app_crypto::AppSignature> + ::Generic::from + ) + .map(Signature::from) }) } } /// Creates runtime-specific signed transaction. pub trait CreateTransaction { + /// A `Public` key representing a particular `AccountId`. + type Public; + /// A `Signature` generated by the `Signer`. type Signature; /// Attempt to create signed extrinsic data that encodes call from given account. @@ -49,15 +69,26 @@ pub trait CreateTransaction { /// in any way it wants. /// Returns `None` if signed extrinsic could not be created (either because signing failed /// or because of any other runtime-specific reason). - fn create_transaction>( + fn create_transaction>( call: Extrinsic::Call, + public: Self::Public, account: T::AccountId, nonce: T::Index, ) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>; } +type PublicOf = < + >::CreateTransaction as CreateTransaction< + T, + >::Extrinsic, + > +>::Public; + /// A trait to sign and submit transactions in offchain calls. -pub trait SubmitSignedTransaction { +pub trait SubmitSignedTransaction +where + PublicOf: IdentifyAccount + Clone, +{ /// Unchecked extrinsic type. type Extrinsic: ExtrinsicT + codec::Encode; @@ -66,7 +97,7 @@ pub trait SubmitSignedTransaction { /// A type used to sign transactions created using `CreateTransaction`. type Signer: Signer< - T::AccountId, + PublicOf, >::Signature, >; @@ -75,11 +106,12 @@ pub trait SubmitSignedTransaction { /// Returns `Ok` if the transaction was submitted correctly /// and `Err` if the key for given `id` was not found or the /// transaction was rejected from the pool. - fn sign_and_submit(call: impl Into, id: T::AccountId) -> Result<(), ()> { + 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 (call, signature_data) = Self::CreateTransaction - ::create_transaction::(call, id, expected) + ::create_transaction::(call, public, id, expected) .ok_or(())?; let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?; runtime_io::submit_transaction(xt.encode()) @@ -118,8 +150,9 @@ impl Default for TransactionSubmitter { impl SubmitSignedTransaction for TransactionSubmitter where T: crate::Trait, C: CreateTransaction, - S: Signer>::Signature>, + S: Signer<>::Public, >::Signature>, E: ExtrinsicT + codec::Encode, + >::Public: IdentifyAccount + Clone, { type Extrinsic = E; type CreateTransaction = C; diff --git a/srml/timestamp/Cargo.toml b/srml/timestamp/Cargo.toml index 939fb725666083d90ce6ce0daffa74c03bb90152..d8eab64f9f3d983d5b8d3d67f68e91fef18c48b9 100644 --- a/srml/timestamp/Cargo.toml +++ b/srml/timestamp/Cargo.toml @@ -12,7 +12,7 @@ sr-primitives = { path = "../../core/sr-primitives", default-features = false } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false } support = { package = "srml-support", path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } -impl-trait-for-tuples = "0.1.2" +impl-trait-for-tuples = "0.1.3" [dev-dependencies] runtime-io ={ package = "sr-io", path = "../../core/sr-io" } diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index 4af0de8bd1b1806dd7eac887b3efc9681669fded..2ab751088e8c7ad2a12b82e944cbaa2ba93bb47a 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -111,8 +111,8 @@ pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"timstap0"; pub type InherentType = u64; /// Errors that can occur while checking the timestamp inherent. -#[derive(Encode)] -#[cfg_attr(feature = "std", derive(Debug, Decode))] +#[derive(Encode, sr_primitives::RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Decode))] pub enum InherentError { /// The timestamp is valid in the future. /// This is a non-fatal-error and will not stop checking the inherents. @@ -244,7 +244,7 @@ decl_module! { decl_storage! { trait Store for Module as Timestamp { /// Current time for the current block. - pub Now get(now) build(|_| 0.into()): T::Moment; + pub Now get(fn now) build(|_| 0.into()): T::Moment; /// Did the timestamp get updated in this block? DidUpdate: bool; @@ -322,7 +322,7 @@ mod tests { use super::*; use support::{impl_outer_origin, assert_ok, parameter_types}; - use runtime_io::{with_externalities, TestExternalities}; + use runtime_io::TestExternalities; use primitives::H256; use sr_primitives::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; @@ -348,7 +348,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -369,7 +368,7 @@ mod tests { #[test] fn timestamp_works() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - with_externalities(&mut TestExternalities::new(t), || { + TestExternalities::new(t).execute_with(|| { Timestamp::set_timestamp(42); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); assert_eq!(Timestamp::now(), 69); @@ -380,7 +379,7 @@ mod tests { #[should_panic(expected = "Timestamp must be updated only once in the block")] fn double_timestamp_should_fail() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - with_externalities(&mut TestExternalities::new(t), || { + TestExternalities::new(t).execute_with(|| { Timestamp::set_timestamp(42); assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); let _ = Timestamp::dispatch(Call::set(70), Origin::NONE); @@ -391,7 +390,7 @@ mod tests { #[should_panic(expected = "Timestamp must increment by at least between sequential blocks")] fn block_period_minimum_enforced() { let t = system::GenesisConfig::default().build_storage::().unwrap(); - with_externalities(&mut TestExternalities::new(t), || { + TestExternalities::new(t).execute_with(|| { Timestamp::set_timestamp(42); let _ = Timestamp::dispatch(Call::set(46), Origin::NONE); }); diff --git a/srml/transaction-payment/Cargo.toml b/srml/transaction-payment/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..cbce1e945e69b8e17d78295fb66e3d3d9a9e8919 --- /dev/null +++ b/srml/transaction-payment/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "srml-transaction-payment" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +support = { package = "srml-support", path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } +transaction-payment-rpc-runtime-api = { package = "srml-transaction-payment-rpc-runtime-api", path = "./rpc/runtime-api", default-features = false } + +[dev-dependencies] +runtime-io = { package = "sr-io", path = "../../core/sr-io" } +primitives = { package = "substrate-primitives", path = "../../core/primitives" } +balances = { package = "srml-balances", path = "../balances" } + +[features] +default = ["std"] +std = [ + "codec/std", + "rstd/std", + "sr-primitives/std", + "support/std", + "system/std", + "transaction-payment-rpc-runtime-api/std" +] diff --git a/srml/transaction-payment/rpc/Cargo.toml b/srml/transaction-payment/rpc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e3dc6f553fc0ca2d92145f0c76b04a32f5987791 --- /dev/null +++ b/srml/transaction-payment/rpc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "srml-transaction-payment-rpc" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +client = { package = "substrate-client", path = "../../../core/client" } +codec = { package = "parity-scale-codec", version = "1.0.0" } +jsonrpc-core = "13.2.0" +jsonrpc-core-client = "13.2.0" +jsonrpc-derive = "13.2.0" +primitives = { package = "substrate-primitives", path = "../../../core/primitives" } +rpc-primitives = { package = "substrate-rpc-primitives", path = "../../../core/rpc/primitives" } +serde = { version = "1.0.101", features = ["derive"] } +sr-primitives = { path = "../../../core/sr-primitives" } +srml-transaction-payment-rpc-runtime-api = { path = "./runtime-api" } diff --git a/srml/transaction-payment/rpc/runtime-api/Cargo.toml b/srml/transaction-payment/rpc/runtime-api/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..c26f295f4bec313a3f52e45f50455e9e052839f3 --- /dev/null +++ b/srml/transaction-payment/rpc/runtime-api/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "srml-transaction-payment-rpc-runtime-api" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +serde = { version = "1.0.101", optional = true, features = ["derive"] } +client = { package = "substrate-client", path = "../../../../core/client", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../../../../core/sr-std", default-features = false } +sr-primitives = { path = "../../../../core/sr-primitives", default-features = false } + +[features] +default = ["std"] +std = [ + "serde", + "client/std", + "codec/std", + "rstd/std", + "sr-primitives/std", +] diff --git a/srml/transaction-payment/rpc/runtime-api/src/lib.rs b/srml/transaction-payment/rpc/runtime-api/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc6360ca88d76abe159baac8bd1d9d2172084277 --- /dev/null +++ b/srml/transaction-payment/rpc/runtime-api/src/lib.rs @@ -0,0 +1,47 @@ +// 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 . + +//! Runtime API definition for transaction payment module. + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use sr_primitives::weights::{Weight, DispatchClass}; +use codec::{Encode, Codec, Decode}; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; + +/// 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, Serialize, Deserialize))] +pub struct RuntimeDispatchInfo { + /// 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: Balance, +} + +client::decl_runtime_apis! { + pub trait TransactionPaymentApi where + Balance: Codec, + Extrinsic: Codec, + { + fn query_info(uxt: Extrinsic, len: u32) -> RuntimeDispatchInfo; + } +} diff --git a/srml/transaction-payment/rpc/src/lib.rs b/srml/transaction-payment/rpc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ee3c7eddbc55cce2d76c2e83fa0e192fd0038c6 --- /dev/null +++ b/srml/transaction-payment/rpc/src/lib.rs @@ -0,0 +1,108 @@ +// 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 transaction payment module. + +use std::sync::Arc; +use codec::{Codec, Decode}; +use client::blockchain::HeaderBackend; +use jsonrpc_core::{Error as RpcError, ErrorCode, Result}; +use jsonrpc_derive::rpc; +use sr_primitives::{ + generic::BlockId, + traits::{Block as BlockT, ProvideRuntimeApi}, +}; +use primitives::Bytes; +use srml_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; +pub use srml_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi; +pub use self::gen_client::Client as TransactionPaymentClient; + +#[rpc] +pub trait TransactionPaymentApi { + #[rpc(name = "payment_queryInfo")] + fn query_info( + &self, + encoded_xt: Bytes, + at: Option + ) -> Result>; +} + +/// A struct that implements the [`TransactionPaymentApi`]. +pub struct TransactionPayment { + client: Arc, + _marker: std::marker::PhantomData

, +} + +impl TransactionPayment { + /// Create new `TransactionPayment` with the given reference to the client. + pub fn new(client: Arc) -> Self { + TransactionPayment { client, _marker: Default::default() } + } +} + +/// Error type of this RPC api. +pub enum Error { + /// The transaction was not decodable. + DecodeError, + /// The call to runtime failed. + RuntimeError, +} + +impl From for i64 { + fn from(e: Error) -> i64 { + match e { + Error::RuntimeError => 1, + Error::DecodeError => 2, + } + } +} + +impl TransactionPaymentApi<::Hash, Balance> + for TransactionPayment +where + Block: BlockT, + C: Send + Sync + 'static, + C: ProvideRuntimeApi, + C: HeaderBackend, + C::Api: TransactionPaymentRuntimeApi, + Balance: Codec, + Extrinsic: Codec + Send + Sync + 'static, +{ + fn query_info( + &self, + encoded_xt: Bytes, + at: Option<::Hash> + ) -> 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. + self.client.info().best_hash + )); + + let encoded_len = encoded_xt.len() as u32; + + let uxt: Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::DecodeError.into()), + message: "Unable to query dispatch info.".into(), + data: Some(format!("{:?}", e).into()), + })?; + api.query_info(&at, uxt, encoded_len).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), + message: "Unable to query dispatch info.".into(), + data: Some(format!("{:?}", e).into()), + }) + } +} diff --git a/srml/transaction-payment/src/lib.rs b/srml/transaction-payment/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..2314edf053958537e9be6e5afe5652ba99e2a222 --- /dev/null +++ b/srml/transaction-payment/src/lib.rs @@ -0,0 +1,534 @@ +// 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 . + +//! # Transaction Payment Module +//! +//! This module provides the basic logic needed to pay the absolute minimum amount needed for a +//! transaction to be included. This includes: +//! - _weight fee_: A fee proportional to amount of weight a transaction consumes. +//! - _length fee_: A fee proportional to the encoded length of the transaction. +//! - _tip_: An optional tip. Tip increases the priority of the transaction, giving it a higher +//! chance to be included by the transaction queue. +//! +//! Additionally, this module allows one to configure: +//! - The mapping between one unit of weight to one unit of fee via [`WeightToFee`]. +//! - A means of updating the fee for the next block, via defining a multiplier, based on the +//! final state of the chain at the end of the previous block. This can be configured via +//! [`FeeMultiplierUpdate`] + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use codec::{Encode, Decode}; +use support::{ + decl_storage, decl_module, + traits::{Currency, Get, OnUnbalanced, ExistenceRequirement, WithdrawReason}, +}; +use sr_primitives::{ + Fixed64, + transaction_validity::{ + TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, + TransactionValidity, + }, + traits::{Zero, Saturating, SignedExtension, SaturatedConversion, Convert}, + weights::{Weight, DispatchInfo, GetDispatchInfo}, +}; +use transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; + +type Multiplier = Fixed64; +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = + <::Currency as Currency<::AccountId>>::NegativeImbalance; + +pub trait Trait: system::Trait { + /// The currency type in which fees will be paid. + type Currency: Currency; + + /// Handler for the unbalanced reduction when taking transaction fees. + type OnTransactionPayment: OnUnbalanced>; + + /// The fee to be paid for making a transaction; the base. + type TransactionBaseFee: Get>; + + /// The fee to be paid for making a transaction; the per-byte portion. + type TransactionByteFee: Get>; + + /// Convert a weight value into a deductible fee based on the currency type. + type WeightToFee: Convert>; + + /// Update the multiplier of the next block, based on the previous block's weight. + type FeeMultiplierUpdate: Convert; +} + +decl_storage! { + trait Store for Module as Balances { + NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_parts(0); + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// The fee to be paid for making a transaction; the base. + const TransactionBaseFee: BalanceOf = T::TransactionBaseFee::get(); + + /// The fee to be paid for making a transaction; the per-byte portion. + const TransactionByteFee: BalanceOf = T::TransactionByteFee::get(); + + fn on_finalize() { + NextFeeMultiplier::mutate(|fm| { + *fm = T::FeeMultiplierUpdate::convert(*fm) + }); + } + } +} + +impl Module { + /// Query the data that we know about the fee of a given `call`. + /// + /// As this module is not and cannot be aware of the internals of a signed extension, it only + /// interprets them as some encoded value and takes their length into account. + /// + /// All dispatchables must be annotated with weight and will have some fee info. This function + /// always returns. + // NOTE: we can actually make it understand `ChargeTransactionPayment`, but would be some hassle + // for sure. We have to make it aware of the index of `ChargeTransactionPayment` in `Extra`. + // Alternatively, we could actually execute the tx's per-dispatch and record the balance of the + // sender before and after the pipeline.. but this is way too much hassle for a very very little + // potential gain in the future. + pub fn query_info( + unchecked_extrinsic: Extrinsic, + len: u32, + ) -> RuntimeDispatchInfo> + where T: Send + Sync, + { + let dispatch_info = ::get_dispatch_info(&unchecked_extrinsic); + + let partial_fee = >::compute_fee(len, dispatch_info, 0u32.into()); + let DispatchInfo { weight, class } = dispatch_info; + + RuntimeDispatchInfo { weight, class, partial_fee } + } +} + +/// Require the transactor pay for themselves and maybe include a tip to gain additional priority +/// in the queue. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct ChargeTransactionPayment(#[codec(compact)] BalanceOf); + +impl ChargeTransactionPayment { + /// utility constructor. Used only in client/factory code. + pub fn from(fee: BalanceOf) -> Self { + Self(fee) + } + + /// Compute the final fee value for a particular transaction. + /// + /// The final fee is composed of: + /// - _length-fee_: This is the amount paid merely to pay for size of the transaction. + /// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike + /// size-fee, this is not input dependent and reflects the _complexity_ of the execution + /// and the time it consumes. + /// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed + /// transactions can have a tip. + fn compute_fee(len: u32, info: DispatchInfo, tip: BalanceOf) -> BalanceOf { + let len_fee = if info.pay_length_fee() { + let len = >::from(len); + let base = T::TransactionBaseFee::get(); + let per_byte = T::TransactionByteFee::get(); + base.saturating_add(per_byte.saturating_mul(len)) + } else { + Zero::zero() + }; + + let weight_fee = { + // cap the weight to the maximum defined in runtime, otherwise it will be the `Bounded` + // maximum of its data type, which is not desired. + let capped_weight = info.weight.min(::MaximumBlockWeight::get()); + T::WeightToFee::convert(capped_weight) + }; + + // everything except for tip + let basic_fee = len_fee.saturating_add(weight_fee); + let fee_update = NextFeeMultiplier::get(); + let adjusted_fee = fee_update.saturated_multiply_accumulate(basic_fee); + + adjusted_fee.saturating_add(tip) + } +} + +impl rstd::fmt::Debug for ChargeTransactionPayment { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "ChargeTransactionPayment<{:?}>", self.0) + } + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + Ok(()) + } +} + +impl SignedExtension for ChargeTransactionPayment + where BalanceOf: Send + Sync +{ + type AccountId = T::AccountId; + type Call = T::Call; + type AdditionalSigned = (); + type Pre = (); + fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) } + + fn validate( + &self, + who: &Self::AccountId, + _call: &Self::Call, + info: DispatchInfo, + len: usize, + ) -> TransactionValidity { + // pay any fees. + let tip = self.0; + let fee = Self::compute_fee(len as u32, info, tip); + let imbalance = match T::Currency::withdraw( + who, + fee, + if tip.is_zero() { + WithdrawReason::TransactionPayment.into() + } else { + WithdrawReason::TransactionPayment | WithdrawReason::Tip + }, + ExistenceRequirement::KeepAlive, + ) { + Ok(imbalance) => imbalance, + Err(_) => return InvalidTransaction::Payment.into(), + }; + T::OnTransactionPayment::on_unbalanced(imbalance); + + let mut r = ValidTransaction::default(); + // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which + // will be a bit more than setting the priority to tip. For now, this is enough. + r.priority = fee.saturated_into::(); + Ok(r) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use codec::Encode; + use support::{parameter_types, impl_outer_origin, impl_outer_dispatch}; + use primitives::H256; + use sr_primitives::{ + Perbill, + testing::{Header, TestXt}, + traits::{BlakeTwo256, IdentityLookup, Extrinsic}, + weights::{DispatchClass, DispatchInfo, GetDispatchInfo}, + }; + use balances::Call as BalancesCall; + use rstd::cell::RefCell; + use transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; + + const CALL: &::Call = &Call::Balances(BalancesCall::transfer(2, 69)); + + impl_outer_dispatch! { + pub enum Call for Runtime where origin: Origin { + balances::Balances, + system::System, + } + } + + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Runtime; + + impl_outer_origin!{ + pub enum Origin for Runtime {} + } + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + + impl system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = Call; + 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 = (); + } + + parameter_types! { + pub const TransferFee: u64 = 0; + pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 0; + } + + impl balances::Trait for Runtime { + type Balance = u64; + type OnFreeBalanceZero = (); + type OnNewAccount = (); + type Event = (); + type TransferPayment = (); + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + } + + 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); + } + + pub struct TransactionBaseFee; + impl Get for TransactionBaseFee { + fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) } + } + + pub struct TransactionByteFee; + impl Get for TransactionByteFee { + fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) } + } + + pub struct WeightToFee(u64); + impl Convert for WeightToFee { + fn convert(t: Weight) -> u64 { + WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64)) + } + } + + impl Trait for Runtime { + type Currency = balances::Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = WeightToFee; + type FeeMultiplierUpdate = (); + } + + type Balances = balances::Module; + type System = system::Module; + type TransactionPayment = Module; + + pub struct ExtBuilder { + balance_factor: u64, + base_fee: u64, + byte_fee: u64, + weight_to_fee: u64 + } + + impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + base_fee: 0, + byte_fee: 1, + weight_to_fee: 1, + } + } + } + + impl ExtBuilder { + pub fn fees(mut self, base: u64, byte: u64, weight: u64) -> Self { + self.base_fee = base; + self.byte_fee = byte; + self.weight_to_fee = weight; + self + } + pub fn balance_factor(mut self, factor: u64) -> Self { + self.balance_factor = factor; + self + } + fn set_constants(&self) { + TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.base_fee); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); + WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + } + pub fn build(self) -> runtime_io::TestExternalities { + self.set_constants(); + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + 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![], + }.assimilate_storage(&mut t).unwrap(); + t.into() + } + } + + /// 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() } + } + + #[test] + fn signed_extension_transaction_payment_work() { + ExtBuilder::default() + .balance_factor(10) // 100 + .fees(5, 1, 1) // 5 fixed, 1 per byte, 1 per weight + .build() + .execute_with(|| + { + let len = 10; + assert!( + ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, info_from_weight(5), len) + .is_ok() + ); + 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); + }); + } + + #[test] + fn signed_extension_transaction_payment_is_bounded() { + ExtBuilder::default() + .balance_factor(1000) + .fees(0, 0, 1) + .build() + .execute_with(|| + { + use sr_primitives::weights::Weight; + + // maximum weight possible + assert!( + ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10) + .is_ok() + ); + // fee will be proportional to what is the actual maximum weight in the runtime. + assert_eq!( + Balances::free_balance(&1), + (10000 - ::MaximumBlockWeight::get()) as u64 + ); + }); + } + + #[test] + fn signed_extension_allows_free_transactions() { + ExtBuilder::default() + .fees(100, 1, 1) + .balance_factor(0) + .build() + .execute_with(|| + { + // 1 ain't have a penny. + assert_eq!(Balances::free_balance(&1), 0); + + // like a FreeOperational + let operational_transaction = DispatchInfo { + weight: 0, + class: DispatchClass::Operational + }; + let len = 100; + assert!( + ChargeTransactionPayment::::from(0) + .validate(&1, CALL, operational_transaction , len) + .is_ok() + ); + + // like a FreeNormal + let free_transaction = DispatchInfo { + weight: 0, + class: DispatchClass::Normal + }; + assert!( + ChargeTransactionPayment::::from(0) + .validate(&1, CALL, free_transaction , len) + .is_err() + ); + }); + } + + #[test] + fn signed_ext_length_fee_is_also_updated_per_congestion() { + ExtBuilder::default() + .fees(5, 1, 1) + .balance_factor(10) + .build() + .execute_with(|| + { + // all fees should be x1.5 + NextFeeMultiplier::put(Fixed64::from_rational(1, 2)); + let len = 10; + + assert!( + ChargeTransactionPayment::::from(10) // tipped + .pre_dispatch(&1, CALL, info_from_weight(3), len) + .is_ok() + ); + assert_eq!(Balances::free_balance(&1), 100 - 10 - (5 + 10 + 3) * 3 / 2); + }) + } + + #[test] + fn query_info_works() { + let call = Call::Balances(BalancesCall::transfer(2, 69)); + let origin = 111111; + let extra = (); + let xt = TestXt::new(call, Some((origin, extra))).unwrap(); + let info = xt.get_dispatch_info(); + let ext = xt.encode(); + let len = ext.len() as u32; + ExtBuilder::default() + .fees(5, 1, 2) + .build() + .execute_with(|| + { + // all fees should be x1.5 + NextFeeMultiplier::put(Fixed64::from_rational(1, 2)); + + assert_eq!( + TransactionPayment::query_info(xt, len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: ( + 5 /* base */ + + len as u64 /* len * 1 */ + + info.weight.min(MaximumBlockWeight::get()) as u64 * 2 /* weight * weight_to_fee */ + ) * 3 / 2 + }, + ); + + }); + } +} + + diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index db8b0f34c90445d26eabbb1055c44756d5c5b2e0..7c38115bfbab12476d1fc93dd2d3951f4db5230f 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -77,7 +77,7 @@ use support::traits::{ }; use sr_primitives::{Permill, Perbill, ModuleId}; use sr_primitives::traits::{ - Zero, EnsureOrigin, StaticLookup, AccountIdConversion, CheckedSub + Zero, EnsureOrigin, StaticLookup, AccountIdConversion, CheckedSub, Saturating }; use sr_primitives::weights::SimpleDispatchInfo; use codec::{Encode, Decode}; @@ -102,9 +102,6 @@ pub trait Trait: system::Trait { /// The overarching event type. type Event: From> + Into<::Event>; - /// Handler for the unbalanced increase when minting cash from the "Pot". - type MintedForSpending: OnUnbalanced>; - /// Handler for the unbalanced decrease when slashing for a rejected proposal. type ProposalRejection: OnUnbalanced>; @@ -213,8 +210,8 @@ decl_module! { } /// A spending proposal. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Encode, Decode, Clone, PartialEq, Eq, sr_primitives::RuntimeDebug)] pub struct Proposal { proposer: AccountId, value: Balance, @@ -225,13 +222,22 @@ pub struct Proposal { decl_storage! { trait Store for Module as Treasury { /// Number of proposals that have been made. - ProposalCount get(proposal_count): ProposalIndex; + ProposalCount get(fn proposal_count): ProposalIndex; /// Proposals that have been made. - Proposals get(proposals): map ProposalIndex => Option>>; + Proposals get(fn proposals): map ProposalIndex => Option>>; /// Proposal indices that have been approved but not yet awarded. - Approvals get(approvals): Vec; + Approvals get(fn approvals): Vec; + } + add_extra_genesis { + build(|_config| { + // Create Treasury account + let _ = T::Currency::make_free_balance_be( + &>::account_id(), + T::Currency::minimum_balance(), + ); + }); } } @@ -311,10 +317,14 @@ impl Module { Self::deposit_event(RawEvent::Burnt(burn)) } + // Must never be an error, but better to be safe. + // proof: budget_remaining is account free balance minus ED; + // Thus we can't spend more than account free balance minus ED; + // Thus account is kept alive; qed; if let Err(problem) = T::Currency::settle( &Self::account_id(), imbalance, - WithdrawReason::Transfer, + WithdrawReason::Transfer.into(), ExistenceRequirement::KeepAlive ) { print("Inconsistent state - couldn't settle imbalance for funds spent by treasury"); @@ -325,21 +335,32 @@ impl Module { Self::deposit_event(RawEvent::Rollover(budget_remaining)); } + /// Return the amount of money in the pot. + // The existential deposit is not part of the pot so treasury account never gets deleted. fn pot() -> BalanceOf { T::Currency::free_balance(&Self::account_id()) + // Must never be less than 0 but better be safe. + .saturating_sub(T::Currency::minimum_balance()) } } impl OnUnbalanced> for Module { fn on_unbalanced(amount: NegativeImbalanceOf) { - T::Currency::resolve_creating(&Self::account_id(), amount); + // Must resolve into existing but better to be safe. + let _ = T::Currency::resolve_creating(&Self::account_id(), amount); } } +/// Mint extra funds for the treasury to keep the ratio of portion to total_issuance equal +/// pre dilution and post-dilution. +/// +/// i.e. +/// ```nocompile +/// portion / total_issuance_before_dilution == +/// (portion + minted) / (total_issuance_before_dilution + minted_to_treasury + minted) +/// ``` impl OnDilution> for Module { fn on_dilution(minted: BalanceOf, portion: BalanceOf) { - // Mint extra funds for the treasury to keep the ratio of portion to total_issuance equal - // pre dilution and post-dilution. if !minted.is_zero() && !portion.is_zero() { let total_issuance = T::Currency::total_issuance(); if let Some(funding) = total_issuance.checked_sub(&portion) { @@ -355,13 +376,10 @@ impl OnDilution> for Module { mod tests { use super::*; - use runtime_io::with_externalities; use support::{assert_noop, assert_ok, impl_outer_origin, parameter_types}; - use primitives::{H256, Blake2Hasher}; + use primitives::H256; use sr_primitives::{ - traits::{BlakeTwo256, OnFinalize, IdentityLookup}, - testing::Header, - assert_eq_error_rate, + traits::{BlakeTwo256, OnFinalize, IdentityLookup}, testing::Header, assert_eq_error_rate, }; impl_outer_origin! { @@ -386,7 +404,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -395,26 +412,20 @@ mod tests { type Version = (); } parameter_types! { - pub const ExistentialDeposit: u64 = 0; + pub const ExistentialDeposit: u64 = 1; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = u64; type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); @@ -427,7 +438,6 @@ mod tests { type ApproveOrigin = system::EnsureRoot; type RejectOrigin = system::EnsureRoot; type Event = (); - type MintedForSpending = (); type ProposalRejection = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; @@ -437,18 +447,20 @@ mod tests { type Balances = balances::Module; type Treasury = Module; - fn new_test_ext() -> runtime_io::TestExternalities { + fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); balances::GenesisConfig::{ - balances: vec![(0, 100), (1, 99), (2, 1)], + // 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() } #[test] fn genesis_config_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_eq!(Treasury::pot(), 0); assert_eq!(Treasury::proposal_count(), 0); }); @@ -456,7 +468,7 @@ mod tests { #[test] fn minting_works() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { // Check that accumulate works when we have Some value in Dummy already. Treasury::on_dilution(100, 100); assert_eq!(Treasury::pot(), 100); @@ -467,7 +479,7 @@ mod tests { fn minting_works_2() { let tests = [(1, 10), (1, 20), (40, 130), (2, 66), (2, 67), (2, 100), (2, 101), (2, 134)]; for &(minted, portion) in &tests { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let init_total_issuance = Balances::total_issuance(); Treasury::on_dilution(minted, portion); @@ -491,7 +503,7 @@ mod tests { #[test] fn spend_proposal_takes_min_deposit() { - with_externalities(&mut new_test_ext(), || { + 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); @@ -500,7 +512,7 @@ mod tests { #[test] fn spend_proposal_takes_proportional_deposit() { - with_externalities(&mut new_test_ext(), || { + 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); @@ -509,14 +521,14 @@ mod tests { #[test] fn spend_proposal_fails_when_proposer_poor() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Treasury::propose_spend(Origin::signed(2), 100, 3), "Proposer's balance too low"); }); } #[test] fn accepted_spend_proposal_ignored_outside_spend_period() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Treasury::on_dilution(100, 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); @@ -530,7 +542,7 @@ mod tests { #[test] fn unused_pot_should_diminish() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { let init_total_issuance = Balances::total_issuance(); Treasury::on_dilution(100, 100); assert_eq!(Balances::total_issuance(), init_total_issuance + 100); @@ -543,7 +555,7 @@ mod tests { #[test] fn rejected_spend_proposal_ignored_on_spend_period() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Treasury::on_dilution(100, 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); @@ -557,7 +569,7 @@ mod tests { #[test] fn reject_already_rejected_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Treasury::on_dilution(100, 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); @@ -568,21 +580,21 @@ mod tests { #[test] fn reject_non_existant_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), "No proposal at that index"); }); } #[test] fn accept_non_existant_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), "No proposal at that index"); }); } #[test] fn accept_already_rejected_spend_proposal_fails() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Treasury::on_dilution(100, 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); @@ -593,7 +605,7 @@ mod tests { #[test] fn accepted_spend_proposal_enacted_on_spend_period() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Treasury::on_dilution(100, 100); assert_eq!(Treasury::pot(), 100); @@ -608,19 +620,79 @@ mod tests { #[test] fn pot_underflow_should_not_diminish() { - with_externalities(&mut new_test_ext(), || { + new_test_ext().execute_with(|| { Treasury::on_dilution(100, 100); + assert_eq!(Treasury::pot(), 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); + assert_eq!(Treasury::pot(), 100); // Pot hasn't changed + + Treasury::on_dilution(100, 100); + >::on_finalize(4); + assert_eq!(Balances::free_balance(&3), 150); // Fund has been spent + assert_eq!(Treasury::pot(), 75); // Pot has finally changed + }); + } + + // Treasury account doesn't get deleted if amount approved to spend is all its free balance. + // i.e. pot should not include existential deposit needed for account survival. + #[test] + fn treasury_account_doesnt_get_deleted() { + new_test_ext().execute_with(|| { + Treasury::on_dilution(100, 100); assert_eq!(Treasury::pot(), 100); + let treasury_balance = Balances::free_balance(&Treasury::account_id()); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), treasury_balance, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Treasury::pot(), 100); // Pot hasn't changed + + assert_ok!(Treasury::propose_spend(Origin::signed(0), Treasury::pot(), 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::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 + }); + } + + // In case treasury account is not existing then it works fine. + // This is usefull for chain that will just update runtime. + #[test] + fn inexisting_account_works() { + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + 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: runtime_io::TestExternalities = t.into(); + + t.execute_with(|| { + 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)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); + 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 Treasury::on_dilution(100, 100); + assert_eq!(Treasury::pot(), 99); // Pot now contains funds + assert_eq!(Balances::free_balance(&Treasury::account_id()), 100); // Account does exist + >::on_finalize(4); - assert_eq!(Balances::free_balance(&3), 150); - assert_eq!(Treasury::pot(), 75); + + assert_eq!(Treasury::pot(), 0); // Pot has changed + assert_eq!(Balances::free_balance(&3), 99); // Balance of `3` has changed }); } } diff --git a/srml/utility/Cargo.toml b/srml/utility/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5e92bcf88ad4860ce0ddeed6172d05c6aa87f7ac --- /dev/null +++ b/srml/utility/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "srml-utility" +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 } +support = { package = "srml-support", path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +runtime-io = { package = "sr-io", path = "../../core/sr-io", default-features = false } + +[dev-dependencies] +primitives = { package = "substrate-primitives", path = "../../core/primitives" } +balances = { package = "srml-balances", path = "../balances" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sr-primitives/std", + "support/std", + "system/std", + "runtime-io/std", + "rstd/std" +] diff --git a/srml/utility/src/lib.rs b/srml/utility/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..d69e260ff9aac4f7b25c8b1923ff238e28456c7a --- /dev/null +++ b/srml/utility/src/lib.rs @@ -0,0 +1,158 @@ +// 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 . + +//! # Utility Module +//! A module full of useful helpers for practical chain management. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use support::{decl_module, decl_event, Parameter}; +use system::ensure_root; +use sr_primitives::{traits::Dispatchable, weights::SimpleDispatchInfo, DispatchError}; + +/// Configuration trait. +pub trait Trait: system::Trait { + /// The overarching event type. + type Event: From + Into<::Event>; + + /// The overarching call type. + type Call: Parameter + Dispatchable; +} + +decl_event!( + /// Events type. + pub enum Event { + BatchExecuted(Vec>), + } +); + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// Deposit one of this module's events by using the default implementation. + fn deposit_event() = default; + + /// Send a batch of dispatch calls (only root). + #[weight = SimpleDispatchInfo::FreeOperational] + fn batch(origin, calls: Vec<::Call>) { + ensure_root(origin)?; + let results = calls.into_iter() + .map(|call| call.dispatch(system::RawOrigin::Root.into())) + .map(|res| res.map_err(Into::into)) + .collect::>(); + Self::deposit_event(Event::BatchExecuted(results)); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use support::{assert_ok, assert_noop, impl_outer_origin, parameter_types, impl_outer_dispatch}; + use primitives::H256; + use sr_primitives::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + impl_outer_dispatch! { + pub enum Call for Test where origin: Origin { + balances::Balances, + utility::Utility, + } + } + + // 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: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = 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 = (); + } + parameter_types! { + pub const ExistentialDeposit: u64 = 0; + pub const TransferFee: u64 = 0; + pub const CreationFee: u64 = 0; + } + impl 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; + } + impl Trait for Test { + type Event = (); + type Call = Call; + } + type Balances = balances::Module; + type Utility = Module; + + fn new_test_ext() -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + balances::GenesisConfig:: { + balances: vec![(1, 10), (2, 0)], + vesting: vec![], + }.assimilate_storage(&mut t).unwrap(); + t.into() + } + + #[test] + fn batch_works() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + assert_eq!(Balances::free_balance(2), 0); + assert_noop!(Utility::batch(Origin::signed(1), vec![ + Call::Balances(balances::Call::force_transfer(1, 2, 5)), + Call::Balances(balances::Call::force_transfer(1, 2, 5)) + ]), "RequireRootOrigin"); + assert_ok!(Utility::batch(Origin::ROOT, vec![ + Call::Balances(balances::Call::force_transfer(1, 2, 5)), + Call::Balances(balances::Call::force_transfer(1, 2, 5)) + ])); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::free_balance(2), 10); + }); + } +} diff --git a/subkey/Cargo.toml b/subkey/Cargo.toml index 1b1a697d0fa675d12d2e12eb165259e8481d44d9..e1c3ef64d26d410e12794c03679486735567cb3c 100644 --- a/subkey/Cargo.toml +++ b/subkey/Cargo.toml @@ -10,7 +10,7 @@ node-runtime = { version = "*", path = "../node/runtime" } node-primitives = { version = "*", path = "../node/primitives" } sr-primitives = { version = "*", path = "../core/sr-primitives" } rand = "0.7.2" -clap = { version = "~2.32.0", features = ["yaml"] } +clap = { version = "2.33.0", features = ["yaml"] } tiny-bip39 = "0.6.2" rustc-hex = "2.0.1" substrate-bip39 = "0.3.1" @@ -19,6 +19,7 @@ hex-literal = "0.2.1" codec = { package = "parity-scale-codec", version = "1.0.0" } system = { package = "srml-system", path = "../srml/system" } balances = { package = "srml-balances", path = "../srml/balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../srml/transaction-payment" } [features] bench = [] diff --git a/subkey/src/cli.yml b/subkey/src/cli.yml index aa6c6d4a574e50e879e27e956845e622c4ad6c63..5d8cd11b1e6f8def2ff8a4dfb3ae877e6f8d6057 100644 --- a/subkey/src/cli.yml +++ b/subkey/src/cli.yml @@ -12,6 +12,11 @@ args: long: sr25519 help: Use Schnorr/Ristretto x25519/BIP39 cryptography takes_value: false + - secp256k1: + short: k + long: secp256k1 + help: Use SECP256k1/ECDSA/BIP39 cryptography + takes_value: false - password: short: p long: password diff --git a/subkey/src/main.rs b/subkey/src/main.rs index 973502b3d6810fcae9a0152a914e40681eb0a84b..1b537580f57ff739f41c9a552ebbfd28bac7161c 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -22,15 +22,15 @@ use bip39::{Language, Mnemonic, MnemonicType}; use clap::{load_yaml, App, ArgMatches}; use codec::{Decode, Encode}; use hex_literal::hex; -use node_primitives::{Balance, Hash, Index}; +use node_primitives::{Balance, Hash, Index, AccountId, Signature}; use node_runtime::{BalancesCall, Call, Runtime, SignedPayload, UncheckedExtrinsic, VERSION}; use primitives::{ crypto::{set_default_ss58_version, Ss58AddressFormat, Ss58Codec}, - ed25519, sr25519, Pair, Public, H256, hexdisplay::HexDisplay, + ed25519, sr25519, ecdsa, Pair, Public, H256, hexdisplay::HexDisplay, }; -use sr_primitives::generic::Era; +use sr_primitives::{traits::{IdentifyAccount, Verify}, generic::Era}; use std::{ - convert::TryInto, + convert::{TryInto, TryFrom}, io::{stdin, Read}, str::FromStr, }; @@ -43,8 +43,10 @@ trait Crypto: Sized { fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair { Self::Pair::from_string(suri, password).expect("Invalid phrase") } - fn ss58_from_pair(pair: &Self::Pair) -> String { - pair.public().to_ss58check() + fn ss58_from_pair(pair: &Self::Pair) -> String where + ::Public: PublicT, + { + pair.public().into_runtime().into_account().to_ss58check() } fn public_from_pair(pair: &Self::Pair) -> Self::Public { pair.public() @@ -58,28 +60,43 @@ trait Crypto: Sized { { 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 Address (SS58): {}", + 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), + format_public_key::(public_key.clone()), + format_account_id::(public_key), Self::ss58_from_pair(&pair) ); - } else if let Ok(pair) = Self::Pair::from_string(uri, password) { + } 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 Public key (hex): {}\n Address (SS58): {}", + println!("Secret Key URI `{}` is account:\n \ + Secret seed: {}\n \ + Public key (hex): {}\n \ + Account ID: {}\n \ + SS58 Address: {}", uri, - format_public_key::(public_key), + 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 Address (SS58): {}", + 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 { @@ -106,17 +123,37 @@ impl Crypto for Sr25519 { type Public = sr25519::Public; } +struct Ecdsa; + +impl Crypto for Ecdsa { + type Pair = ecdsa::Pair; + type Public = ecdsa::Public; +} + type SignatureOf = <::Pair as Pair>::Signature; type PublicOf = <::Pair as Pair>::Public; type SeedOf = <::Pair as Pair>::Seed; +type AccountPublic = ::Signer; -trait SignatureT: AsRef<[u8]> + AsMut<[u8]> + Default {} -trait PublicT: Sized + AsRef<[u8]> + Ss58Codec {} +trait SignatureT: AsRef<[u8]> + AsMut<[u8]> + Default { + /// Converts the signature into a runtime account signature, if possible. If not possible, bombs out. + fn into_runtime(self) -> Signature { + panic!("This cryptography isn't supported for this runtime.") + } +} +trait PublicT: Sized + AsRef<[u8]> + Ss58Codec { + /// Converts the public key into a runtime account public key, if possible. If not possible, bombs out. + fn into_runtime(self) -> AccountPublic { + panic!("This cryptography isn't supported for this runtime.") + } +} -impl SignatureT for sr25519::Signature {} -impl SignatureT for ed25519::Signature {} -impl PublicT for sr25519::Public {} -impl PublicT for ed25519::Public {} +impl SignatureT for sr25519::Signature { fn into_runtime(self) -> Signature { self.into() } } +impl SignatureT for ed25519::Signature { fn into_runtime(self) -> Signature { self.into() } } +impl SignatureT for ecdsa::Signature { fn into_runtime(self) -> Signature { self.into() } } +impl PublicT for sr25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } } +impl PublicT for ed25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } } +impl PublicT for ecdsa::Public { fn into_runtime(self) -> AccountPublic { self.into() } } fn main() { let yaml = load_yaml!("cli.yml"); @@ -125,10 +162,12 @@ fn main() { .get_matches(); if matches.is_present("ed25519") { - execute::(matches) - } else { - execute::(matches) + return execute::(matches) } + if matches.is_present("secp256k1") { + return execute::(matches) + } + return execute::(matches) } fn execute(matches: ArgMatches) @@ -165,7 +204,7 @@ where ("verify", Some(matches)) => { let should_decode = matches.is_present("hex"); let message = read_message_from_stdin(should_decode); - let is_valid_signature = do_verify::(matches, message, password); + let is_valid_signature = do_verify::(matches, message); if is_valid_signature { println!("Signature verifies correctly."); } else { @@ -182,20 +221,20 @@ where C::print_from_uri(&formated_seed, None, maybe_network); } ("transfer", Some(matches)) => { - let signer = read_pair::(matches.value_of("from"), password); + let signer = read_pair::(matches.value_of("from"), password); let index = read_required_parameter::(matches, "index"); let genesis_hash = read_genesis_hash(matches); - let to = read_public_key::(matches.value_of("to"), password); + let to: AccountId = read_account_id(matches.value_of("to")); let amount = read_required_parameter::(matches, "amount"); let function = Call::Balances(BalancesCall::transfer(to.into(), amount)); - let extrinsic = create_extrinsic(function, index, signer, genesis_hash); + let extrinsic = create_extrinsic::(function, index, signer, genesis_hash); print_extrinsic(extrinsic); } ("sign-transaction", Some(matches)) => { - let signer = read_pair::(matches.value_of("suri"), password); + let signer = read_pair::(matches.value_of("suri"), password); let index = read_required_parameter::(matches, "nonce"); let genesis_hash = read_genesis_hash(matches); @@ -205,7 +244,7 @@ where .and_then(|x| Decode::decode(&mut &x[..]).ok()) .unwrap(); - let extrinsic = create_extrinsic(function, index, signer, genesis_hash); + let extrinsic = create_extrinsic::(function, index, signer, genesis_hash); print_extrinsic(extrinsic); } @@ -236,13 +275,13 @@ where format_signature::(&signature) } -fn do_verify(matches: &ArgMatches, message: Vec, password: Option<&str>) -> bool +fn do_verify(matches: &ArgMatches, message: Vec) -> bool where SignatureOf: SignatureT, PublicOf: PublicT, { let signature = read_signature::(matches); - let pubkey = read_public_key::(matches.value_of("uri"), password); + let pubkey = read_public_key::(matches.value_of("uri")); <::Pair as Pair>::verify(&signature, &message, &pubkey) } @@ -305,9 +344,7 @@ where signature } -fn read_public_key(matched_uri: Option<&str>, password: Option<&str>) -> PublicOf -where - SignatureOf: SignatureT, +fn read_public_key(matched_uri: Option<&str>) -> PublicOf where PublicOf: PublicT, { let uri = matched_uri.expect("parameter is required; thus it can't be None; qed"); @@ -319,13 +356,28 @@ where if let Ok(pubkey_vec) = hex::decode(uri) { ::Public::from_slice(pubkey_vec.as_slice()) } else { - ::Pair::from_string(uri, password) + ::Public::from_string(uri) .ok() - .map(|p| p.public()) .expect("Invalid URI; expecting either a secret URI or a public URI.") } } +fn read_account_id(matched_uri: Option<&str>) -> AccountId { + let uri = matched_uri.expect("parameter is required; thus it can't be None; qed"); + let uri = if uri.starts_with("0x") { + &uri[2..] + } else { + uri + }; + if let Ok(data_vec) = hex::decode(uri) { + AccountId::try_from(data_vec.as_slice()) + .expect("Invalid hex length for account ID; should be 32 bytes") + } else { + AccountId::from_ss58check(uri).ok() + .expect("Invalid SS58-check address given for account ID.") + } +} + fn read_pair( matched_suri: Option<&str>, password: Option<&str>, @@ -350,12 +402,21 @@ fn format_public_key(public_key: PublicOf) -> String { format!("0x{}", HexDisplay::from(&public_key.as_ref())) } -fn create_extrinsic( +fn format_account_id(public_key: PublicOf) -> String where + PublicOf: PublicT, +{ + format!("0x{}", HexDisplay::from(&public_key.into_runtime().into_account().as_ref())) +} + +fn create_extrinsic( function: Call, index: Index, - signer: ::Pair, + signer: C::Pair, genesis_hash: H256, -) -> UncheckedExtrinsic { +) -> UncheckedExtrinsic where + PublicOf: PublicT, + SignatureOf: SignatureT, +{ let extra = |i: Index, f: Balance| { ( system::CheckVersion::::new(), @@ -363,7 +424,7 @@ fn create_extrinsic( system::CheckEra::::from(Era::Immortal), system::CheckNonce::::from(i), system::CheckWeight::::new(), - balances::TakeFees::::from(f), + transaction_payment::ChargeTransactionPayment::::from(f), Default::default(), ) }; @@ -380,13 +441,14 @@ fn create_extrinsic( (), ), ); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); + let signature = raw_payload.using_encoded(|payload| signer.sign(payload)).into_runtime(); + let signer = signer.public().into_runtime(); let (function, extra, _) = raw_payload.deconstruct(); UncheckedExtrinsic::new_signed( function, - signer.public().into(), - signature.into(), + signer.into_account().into(), + signature, extra, ) } @@ -439,7 +501,7 @@ mod tests { let matches = App::from_yaml(yaml).get_matches_from(arg_vec); let matches = matches.subcommand().1.unwrap(); - assert!(do_verify::(matches, message, password)); + assert!(do_verify::(matches, message)); } #[test] diff --git a/subkey/src/vanity.rs b/subkey/src/vanity.rs index b612bc470f3938eca9b11ba03b0876f20d51e689..835001a0aa9d0d4dd82b7e719a91f907cce4f495 100644 --- a/subkey/src/vanity.rs +++ b/subkey/src/vanity.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use super::Crypto; +use super::{PublicOf, PublicT, Crypto}; use primitives::Pair; use rand::{rngs::OsRng, RngCore}; @@ -62,7 +62,9 @@ fn calculate_score(_desired: &str, key: &str) -> usize { 0 } -pub(super) fn generate_key(desired: &str) -> Result, &str> { +pub(super) fn generate_key(desired: &str) -> Result, &str> where + PublicOf: PublicT, +{ if desired.is_empty() { return Err("Pattern must not be empty"); } diff --git a/test-utils/chain-spec-builder/Cargo.toml b/test-utils/chain-spec-builder/Cargo.toml index 0bd9b2c55ce45f07c1d6b0ffc483d269ee91269f..e8ca8d354025a0890e61121e0c2c89bb54769a5c 100644 --- a/test-utils/chain-spec-builder/Cargo.toml +++ b/test-utils/chain-spec-builder/Cargo.toml @@ -3,9 +3,12 @@ name = "chain-spec-builder" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" +build = "build.rs" [dependencies] -clap = { version = "~2.32.0", features = ["yaml"] } +ansi_term = "0.12.1" +keystore = { package = "substrate-keystore", path = "../../core/keystore" } node-cli = { path = "../../node/cli" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } -substrate-service = { path = "../../core/service" } +rand = "0.7.2" +structopt = "0.3.3" diff --git a/build.rs b/test-utils/chain-spec-builder/build.rs similarity index 71% rename from build.rs rename to test-utils/chain-spec-builder/build.rs index 273700c525c883579880be28b231a5f14e43c6d7..36730271c715eaa1daf6e957e4b1e01443a2bc7a 100644 --- a/build.rs +++ b/test-utils/chain-spec-builder/build.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2019 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,11 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use vergen::{ConstantsFlags, generate_cargo_keys}; - -const ERROR_MSG: &str = "Failed to generate metadata files"; +use std::env; fn main() { - generate_cargo_keys(ConstantsFlags::all()).expect(ERROR_MSG); - println!("cargo:rerun-if-changed=.git/HEAD"); + if let Ok(profile) = env::var("PROFILE") { + println!("cargo:rustc-cfg=build_type=\"{}\"", profile); + } } diff --git a/test-utils/chain-spec-builder/src/cli.yml b/test-utils/chain-spec-builder/src/cli.yml deleted file mode 100644 index f4b1cb7e2eaaab0e5d2df01a6801d36a3de527b8..0000000000000000000000000000000000000000 --- a/test-utils/chain-spec-builder/src/cli.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: chain-spec-builder -author: "azban " -about: Utility for creating chain specs primarily for testing -args: -- initial_authority_seed: - short: a - value_name: INITIAL_AUTHORITY_SEED - help: Initial authority seed - takes_value: true - multiple: true - required: true -- endowed_account_seed: - short: e - value_name: ENDOWED_ACCOUNT_SEED - help: Endowed account seed - takes_value: true - multiple: true - required: true -- sudo_key_seed: - short: u - value_name: SUDO_KEY_SEED - help: Sudo key seed - takes_value: true - required: true diff --git a/test-utils/chain-spec-builder/src/main.rs b/test-utils/chain-spec-builder/src/main.rs index 8f59c7349363f423cc79f8f364bedea71d460b7a..d46f4b7f0bc98beb0f19c0286dd5a6693ff85c5f 100644 --- a/test-utils/chain-spec-builder/src/main.rs +++ b/test-utils/chain-spec-builder/src/main.rs @@ -1,49 +1,268 @@ -use clap::{App, load_yaml}; +// 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 . + +use std::{fs, path::{Path, PathBuf}}; + +use ansi_term::Style; +use rand::{Rng, distributions::Alphanumeric, rngs::OsRng}; +use structopt::StructOpt; + +use keystore::{Store as Keystore}; use node_cli::chain_spec::{self, AccountId}; -use substrate_service::chain_ops::build_spec; +use primitives::{sr25519, crypto::{Public, Ss58Codec}, traits::BareCryptoStore}; -fn genesis_constructor() -> chain_spec::GenesisConfig { - let yaml = load_yaml!("./cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); - let authorities = matches.values_of("initial_authority_seed") - .unwrap() - .map(chain_spec::get_authority_keys_from_seed) - .collect(); +/// A utility to easily create a testnet chain spec definition with a given set +/// of authorities and endowed accounts and/or generate random accounts. +#[derive(StructOpt)] +#[structopt(rename_all = "kebab-case")] +enum ChainSpecBuilder { + /// Create a new chain spec with the given authorities, endowed and sudo + /// accounts. + New { + /// Authority key seed. + #[structopt(long, short, required = true)] + authority_seeds: Vec, + /// Endowed account address (SS58 format). + #[structopt(long, short)] + endowed_accounts: Vec, + /// Sudo account address (SS58 format). + #[structopt(long, short)] + sudo_account: String, + /// The path where the chain spec should be saved. + #[structopt(long, short, default_value = "./chain_spec.json")] + chain_spec_path: PathBuf, + }, + /// Create a new chain spec with the given number of authorities and endowed + /// accounts. Random keys will be generated as required. + Generate { + /// The number of authorities. + #[structopt(long, short)] + authorities: usize, + /// The number of endowed accounts. + #[structopt(long, short, default_value = "0")] + endowed: usize, + /// The path where the chain spec should be saved. + #[structopt(long, short, default_value = "./chain_spec.json")] + chain_spec_path: PathBuf, + /// Path to use when saving generated keystores for each authority. + /// + /// At this path, a new folder will be created for each authority's + /// keystore named `auth-$i` where `i` is the authority index, i.e. + /// `auth-0`, `auth-1`, etc. + #[structopt(long, short)] + keystore_path: Option, + }, +} - let endowed_accounts = matches.values_of("endowed_account_seed") - .unwrap() - .map(chain_spec::get_from_seed::) - .collect(); +impl ChainSpecBuilder { + /// Returns the path where the chain spec should be saved. + fn chain_spec_path(&self) -> &Path { + match self { + ChainSpecBuilder::New { chain_spec_path, .. } => + chain_spec_path.as_path(), + ChainSpecBuilder::Generate { chain_spec_path, .. } => + chain_spec_path.as_path(), + } + } +} - let sudo_key_seed = matches.value_of("sudo_key_seed").unwrap(); - let sudo_key = chain_spec::get_from_seed::(sudo_key_seed); +fn genesis_constructor( + authority_seeds: &[String], + endowed_accounts: &[AccountId], + sudo_account: &AccountId, +) -> chain_spec::GenesisConfig { + let authorities = authority_seeds + .iter() + .map(AsRef::as_ref) + .map(chain_spec::get_authority_keys_from_seed) + .collect::>(); let enable_println = true; chain_spec::testnet_genesis( authorities, - sudo_key.into(), - Some(endowed_accounts), + sudo_account.clone(), + Some(endowed_accounts.to_vec()), enable_println, ) } -fn generate_chain_spec() -> String { +fn generate_chain_spec( + authority_seeds: Vec, + endowed_accounts: Vec, + sudo_account: String, +) -> Result { + let parse_account = |address: &String| { + AccountId::from_string(address) + .map_err(|err| format!("Failed to parse account address: {:?}", err)) + }; + + let endowed_accounts = endowed_accounts + .iter() + .map(parse_account) + .collect::, String>>()?; + + let sudo_account = parse_account(&sudo_account)?; + let chain_spec = chain_spec::ChainSpec::from_genesis( "Custom", "custom", - genesis_constructor, + move || genesis_constructor(&authority_seeds, &endowed_accounts, &sudo_account), vec![], None, None, None, Default::default(), ); - build_spec(chain_spec, false).unwrap() + + chain_spec.to_json(false).map_err(|err| err.to_string()) +} + +fn generate_authority_keys_and_store( + seeds: &[String], + keystore_path: &Path, +) -> Result<(), String> { + for (n, seed) in seeds.into_iter().enumerate() { + let keystore = Keystore::open( + keystore_path.join(format!("auth-{}", n)), + None, + ).map_err(|err| err.to_string())?; + + let (_, _, grandpa, babe, im_online) = + chain_spec::get_authority_keys_from_seed(seed); + + let insert_key = |key_type, public| { + keystore.write().insert_unknown( + key_type, + &format!("//{}", seed), + public, + ).map_err(|_| format!("Failed to insert key: {}", grandpa)) + }; + + insert_key( + primitives::crypto::key_types::BABE, + babe.as_slice(), + )?; + + insert_key( + primitives::crypto::key_types::GRANDPA, + grandpa.as_slice(), + )?; + + insert_key( + primitives::crypto::key_types::IM_ONLINE, + im_online.as_slice(), + )?; + } + + Ok(()) +} + +fn print_seeds( + authority_seeds: &[String], + endowed_seeds: &[String], + sudo_seed: &str, +) { + let header = Style::new().bold().underline(); + let entry = Style::new().bold(); + + println!("{}", header.paint("Authority seeds")); + + for (n, seed) in authority_seeds.iter().enumerate() { + println!("{} //{}", + entry.paint(format!("auth-{}:", n)), + seed, + ); + } + + println!(); + + if !endowed_seeds.is_empty() { + println!("{}", header.paint("Endowed seeds")); + for (n, seed) in endowed_seeds.iter().enumerate() { + println!("{} //{}", + entry.paint(format!("endowed-{}:", n)), + seed, + ); + } + + println!(); + } + + println!("{}", header.paint("Sudo seed")); + println!("//{}", sudo_seed); } -fn main() { - let json = generate_chain_spec(); - println!("{}", json); +fn main() -> Result<(), String> { + #[cfg(build_type="debug")] + println!( + "The chain spec builder builds a chain specification that includes a Substrate runtime compiled as WASM. To \ + ensure proper functioning of the included runtime compile (or run) the chain spec builder binary in \ + `--release` mode.\n", + ); + + let builder = ChainSpecBuilder::from_args(); + let chain_spec_path = builder.chain_spec_path().to_path_buf(); + + let (authority_seeds, endowed_accounts, sudo_account) = match builder { + ChainSpecBuilder::Generate { authorities, endowed, keystore_path, .. } => { + let authorities = authorities.max(1); + let rand_str = || -> String { + OsRng.sample_iter(&Alphanumeric) + .take(32) + .collect() + }; + + let authority_seeds = (0..authorities).map(|_| rand_str()).collect::>(); + let endowed_seeds = (0..endowed).map(|_| rand_str()).collect::>(); + let sudo_seed = rand_str(); + + print_seeds( + &authority_seeds, + &endowed_seeds, + &sudo_seed, + ); + + if let Some(keystore_path) = keystore_path { + generate_authority_keys_and_store( + &authority_seeds, + &keystore_path, + )?; + } + + let endowed_accounts = endowed_seeds.iter().map(|seed| { + chain_spec::get_account_id_from_seed::(seed) + .to_ss58check() + }).collect(); + + let sudo_account = chain_spec::get_account_id_from_seed::(&sudo_seed) + .to_ss58check(); + + (authority_seeds, endowed_accounts, sudo_account) + }, + ChainSpecBuilder::New { authority_seeds, endowed_accounts, sudo_account, .. } => { + (authority_seeds, endowed_accounts, sudo_account) + }, + }; + + let json = generate_chain_spec( + authority_seeds, + endowed_accounts, + sudo_account, + )?; + + fs::write(chain_spec_path, json).map_err(|err| err.to_string()) }